blob de5d202b (1607353B) - Raw
1 //! Semantic analysis of ZIR instructions. 2 //! Shared to every Block. Stored on the stack. 3 //! State used for compiling a ZIR into AIR. 4 //! Transforms untyped ZIR instructions into semantically-analyzed AIR instructions. 5 //! Does type checking, comptime control flow, and safety-check generation. 6 //! This is the the heart of the Zig compiler. 7 8 const std = @import("std"); 9 const math = std.math; 10 const mem = std.mem; 11 const Allocator = mem.Allocator; 12 const assert = std.debug.assert; 13 const log = std.log.scoped(.sema); 14 15 const Sema = @This(); 16 const Value = @import("Value.zig"); 17 const MutableValue = @import("mutable_value.zig").MutableValue; 18 const Type = @import("Type.zig"); 19 const Air = @import("Air.zig"); 20 const Zir = std.zig.Zir; 21 const Zcu = @import("Zcu.zig"); 22 const trace = @import("tracy.zig").trace; 23 const Namespace = Zcu.Namespace; 24 const CompileError = Zcu.CompileError; 25 const SemaError = Zcu.SemaError; 26 const LazySrcLoc = Zcu.LazySrcLoc; 27 const RangeSet = @import("RangeSet.zig"); 28 const target_util = @import("target.zig"); 29 const Package = @import("Package.zig"); 30 const crash_report = @import("crash_report.zig"); 31 const build_options = @import("build_options"); 32 const Compilation = @import("Compilation.zig"); 33 const InternPool = @import("InternPool.zig"); 34 const Alignment = InternPool.Alignment; 35 const AnalUnit = InternPool.AnalUnit; 36 const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; 37 const Cache = std.Build.Cache; 38 const LowerZon = @import("Sema/LowerZon.zig"); 39 const arith = @import("Sema/arith.zig"); 40 41 pt: Zcu.PerThread, 42 /// Alias to `zcu.gpa`. 43 gpa: Allocator, 44 /// Points to the temporary arena allocator of the Sema. 45 /// This arena will be cleared when the sema is destroyed. 46 arena: Allocator, 47 code: Zir, 48 air_instructions: std.MultiArrayList(Air.Inst) = .{}, 49 air_extra: std.ArrayListUnmanaged(u32) = .empty, 50 /// Maps ZIR to AIR. 51 inst_map: InstMap = .{}, 52 /// The "owner" of a `Sema` represents the root "thing" that is being analyzed. 53 /// This does not change throughout the entire lifetime of a `Sema`. For instance, 54 /// when analyzing a runtime function body, this is always `func` of that function, 55 /// even if an inline/comptime function call is being analyzed. 56 owner: AnalUnit, 57 /// The function this ZIR code is the body of, according to the source code. 58 /// This starts out the same as `sema.owner.func` if applicable, and then diverges 59 /// in the case of an inline or comptime function call. 60 /// This could be `none`, a `func_decl`, or a `func_instance`. 61 func_index: InternPool.Index, 62 /// Whether the type of func_index has a calling convention of `.naked`. 63 func_is_naked: bool, 64 /// Used to restore the error return trace when returning a non-error from a function. 65 error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none, 66 comptime_err_ret_trace: *std.array_list.Managed(LazySrcLoc), 67 /// When semantic analysis needs to know the return type of the function whose body 68 /// is being analyzed, this `Type` should be used instead of going through `func`. 69 /// This will correctly handle the case of a comptime/inline function call of a 70 /// generic function which uses a type expression for the return type. 71 /// The type will be `void` in the case that `func` is `null`. 72 fn_ret_ty: Type, 73 /// In case of the return type being an error union with an inferred error 74 /// set, this is the inferred error set. `null` otherwise. Allocated with 75 /// `Sema.arena`. 76 fn_ret_ty_ies: ?*InferredErrorSet, 77 branch_quota: u32 = default_branch_quota, 78 branch_count: u32 = 0, 79 /// Populated when returning `error.ComptimeBreak`. Used to communicate the 80 /// break instruction up the stack to find the corresponding Block. 81 comptime_break_inst: Zir.Inst.Index = undefined, 82 /// These are lazily created runtime blocks from block_inline instructions. 83 /// They are created when an break_inline passes through a runtime condition, because 84 /// Sema must convert comptime control flow to runtime control flow, which means 85 /// breaking from a block. 86 post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .empty, 87 /// Populated with the last compile error created. 88 err: ?*Zcu.ErrorMsg = null, 89 90 /// The temporary arena is used for the memory of the `InferredAlloc` values 91 /// here so the values can be dropped without any cleanup. 92 unresolved_inferred_allocs: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .empty, 93 94 /// Links every pointer derived from a base `alloc` back to that `alloc`. Used 95 /// to detect comptime-known `const`s. 96 /// TODO: ZIR liveness analysis would allow us to remove elements from this map. 97 base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .empty, 98 99 /// Runtime `alloc`s are placed in this map to track all comptime-known writes 100 /// before the corresponding `make_ptr_const` instruction. 101 /// If any store to the alloc depends on a runtime condition or stores a runtime 102 /// value, the corresponding element in this map is erased, to indicate that the 103 /// alloc is not comptime-known. 104 /// If the alloc remains in this map when `make_ptr_const` is reached, its value 105 /// is comptime-known, and all stores to the pointer must be applied at comptime 106 /// to determine the comptime value. 107 /// Backed by gpa. 108 maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .empty, 109 110 /// Comptime-mutable allocs, and any comptime allocs which reference it, are 111 /// stored as elements of this array. 112 /// Pointers to such memory are represented via an index into this array. 113 /// Backed by gpa. 114 comptime_allocs: std.ArrayListUnmanaged(ComptimeAlloc) = .empty, 115 116 /// A list of exports performed by this analysis. After this `Sema` terminates, 117 /// these are flushed to `Zcu.single_exports` or `Zcu.multi_exports`. 118 exports: std.ArrayListUnmanaged(Zcu.Export) = .empty, 119 120 /// All references registered so far by this `Sema`. This is a temporary duplicate 121 /// of data stored in `Zcu.all_references`. It exists to avoid adding references to 122 /// a given `AnalUnit` multiple times. 123 references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty, 124 type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty, 125 126 /// All dependencies registered so far by this `Sema`. This is a temporary duplicate 127 /// of the main dependency data. It exists to avoid adding dependencies to a given 128 /// `AnalUnit` multiple times. 129 dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .empty, 130 131 /// Whether memoization of this call is permitted. Operations with side effects global 132 /// to the `Sema`, such as `@setEvalBranchQuota`, set this to `false`. It is observed 133 /// by `analyzeCall`. 134 allow_memoize: bool = true, 135 136 /// The `BranchHint` for the current branch of runtime control flow. 137 /// This state is on `Sema` so that `cold` hints can be propagated up through blocks with less special handling. 138 branch_hint: ?std.builtin.BranchHint = null, 139 140 const RuntimeIndex = enum(u32) { 141 zero = 0, 142 comptime_field_ptr = std.math.maxInt(u32), 143 _, 144 145 pub fn increment(ri: *RuntimeIndex) void { 146 ri.* = @enumFromInt(@intFromEnum(ri.*) + 1); 147 } 148 }; 149 150 const MaybeComptimeAlloc = struct { 151 /// The runtime index of the `alloc` instruction. 152 runtime_index: RuntimeIndex, 153 /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to 154 /// RLS, a single comptime-known allocation may have arbitrarily many stores. 155 /// This list also contains `set_union_tag`, `optional_payload_ptr_set`, and 156 /// `errunion_payload_ptr_set` instructions. 157 /// If the instruction is one of these three tags, `src` may be `.unneeded`. 158 stores: std.MultiArrayList(struct { 159 inst: Air.Inst.Index, 160 src: LazySrcLoc, 161 }) = .{}, 162 }; 163 164 const ComptimeAlloc = struct { 165 val: MutableValue, 166 is_const: bool, 167 src: LazySrcLoc, 168 /// `.none` indicates that the alignment is the natural alignment of `val`. 169 alignment: Alignment, 170 /// This is the `runtime_index` at the point of this allocation. If an store 171 /// to this alloc ever occurs with a runtime index greater than this one, it 172 /// is behind a runtime condition, so a compile error will be emitted. 173 runtime_index: RuntimeIndex, 174 }; 175 176 /// `src` may be `null` if `is_const` will be set. 177 fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex { 178 const idx = sema.comptime_allocs.items.len; 179 try sema.comptime_allocs.append(sema.gpa, .{ 180 .val = .{ .interned = try sema.pt.intern(.{ .undef = ty.toIntern() }) }, 181 .is_const = false, 182 .src = src, 183 .alignment = alignment, 184 .runtime_index = block.runtime_index, 185 }); 186 return @enumFromInt(idx); 187 } 188 189 pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc { 190 return &sema.comptime_allocs.items[@intFromEnum(idx)]; 191 } 192 193 pub const default_branch_quota = 1000; 194 195 pub const InferredErrorSet = struct { 196 /// The function body from which this error set originates. 197 /// This is `none` in the case of a comptime/inline function call, corresponding to 198 /// `InternPool.Index.adhoc_inferred_error_set_type`. 199 /// The function's resolved error set is not set until analysis of the 200 /// function body completes. 201 func: InternPool.Index, 202 /// All currently known errors that this error set contains. This includes 203 /// direct additions via `return error.Foo;`, and possibly also errors that 204 /// are returned from any dependent functions. 205 errors: NameMap = .{}, 206 /// Other inferred error sets which this inferred error set should include. 207 inferred_error_sets: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty, 208 /// The regular error set created by resolving this inferred error set. 209 resolved: InternPool.Index = .none, 210 211 pub const NameMap = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void); 212 213 pub fn addErrorSet( 214 self: *InferredErrorSet, 215 err_set_ty: Type, 216 ip: *InternPool, 217 arena: Allocator, 218 ) !void { 219 switch (err_set_ty.toIntern()) { 220 .anyerror_type => self.resolved = .anyerror_type, 221 .adhoc_inferred_error_set_type => {}, // Adding an inferred error set to itself. 222 223 else => switch (ip.indexToKey(err_set_ty.toIntern())) { 224 .error_set_type => |error_set_type| { 225 for (error_set_type.names.get(ip)) |name| { 226 try self.errors.put(arena, name, {}); 227 } 228 }, 229 .inferred_error_set_type => { 230 try self.inferred_error_sets.put(arena, err_set_ty.toIntern(), {}); 231 }, 232 else => unreachable, 233 }, 234 } 235 } 236 }; 237 238 /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve 239 /// instructions during analysis. 240 /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the 241 /// zir instruction index and a start offset. An index is not present in the map if the value 242 /// at the index is `Air.Inst.Ref.none`. 243 /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that 244 /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can 245 /// be called safely for any of the instructions passed in. 246 pub const InstMap = struct { 247 items: []Air.Inst.Ref = &[_]Air.Inst.Ref{}, 248 start: Zir.Inst.Index = @enumFromInt(0), 249 250 pub fn deinit(map: InstMap, allocator: mem.Allocator) void { 251 allocator.free(map.items); 252 } 253 254 pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref { 255 if (!map.contains(key)) return null; 256 return map.items[@intFromEnum(key) - @intFromEnum(map.start)]; 257 } 258 259 pub fn putAssumeCapacity( 260 map: *InstMap, 261 key: Zir.Inst.Index, 262 ref: Air.Inst.Ref, 263 ) void { 264 map.items[@intFromEnum(key) - @intFromEnum(map.start)] = ref; 265 } 266 267 pub fn putAssumeCapacityNoClobber( 268 map: *InstMap, 269 key: Zir.Inst.Index, 270 ref: Air.Inst.Ref, 271 ) void { 272 assert(!map.contains(key)); 273 map.putAssumeCapacity(key, ref); 274 } 275 276 pub const GetOrPutResult = struct { 277 value_ptr: *Air.Inst.Ref, 278 found_existing: bool, 279 }; 280 281 pub fn getOrPutAssumeCapacity( 282 map: *InstMap, 283 key: Zir.Inst.Index, 284 ) GetOrPutResult { 285 const index = @intFromEnum(key) - @intFromEnum(map.start); 286 return GetOrPutResult{ 287 .value_ptr = &map.items[index], 288 .found_existing = map.items[index] != .none, 289 }; 290 } 291 292 pub fn remove(map: InstMap, key: Zir.Inst.Index) bool { 293 if (!map.contains(key)) return false; 294 map.items[@intFromEnum(key) - @intFromEnum(map.start)] = .none; 295 return true; 296 } 297 298 pub fn contains(map: InstMap, key: Zir.Inst.Index) bool { 299 return map.items[@intFromEnum(key) - @intFromEnum(map.start)] != .none; 300 } 301 302 pub fn ensureSpaceForInstructions( 303 map: *InstMap, 304 allocator: mem.Allocator, 305 insts: []const Zir.Inst.Index, 306 ) !void { 307 const start, const end = mem.minMax(u32, @ptrCast(insts)); 308 const map_start = @intFromEnum(map.start); 309 if (map_start <= start and end < map.items.len + map_start) 310 return; 311 312 const old_start = if (map.items.len == 0) start else map_start; 313 var better_capacity = map.items.len; 314 var better_start = old_start; 315 while (true) { 316 const extra_capacity = better_capacity / 2 + 16; 317 better_capacity += extra_capacity; 318 better_start -|= @intCast(extra_capacity / 2); 319 if (better_start <= start and end < better_capacity + better_start) 320 break; 321 } 322 323 const start_diff = old_start - better_start; 324 const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity); 325 @memset(new_items[0..start_diff], .none); 326 @memcpy(new_items[start_diff..][0..map.items.len], map.items); 327 @memset(new_items[start_diff + map.items.len ..], .none); 328 329 allocator.free(map.items); 330 map.items = new_items; 331 map.start = @enumFromInt(better_start); 332 } 333 }; 334 335 /// This is the context needed to semantically analyze ZIR instructions and 336 /// produce AIR instructions. 337 /// This is a temporary structure stored on the stack; references to it are valid only 338 /// during semantic analysis of the block. 339 pub const Block = struct { 340 parent: ?*Block, 341 /// Shared among all child blocks. 342 sema: *Sema, 343 /// The namespace to use for lookups from this source block 344 namespace: InternPool.NamespaceIndex, 345 /// The AIR instructions generated for this block. 346 instructions: std.ArrayListUnmanaged(Air.Inst.Index), 347 // `param` instructions are collected here to be used by the `func` instruction. 348 /// When doing a generic function instantiation, this array collects a type 349 /// for each *runtime-known* parameter. This array corresponds to the instance 350 /// function type, while `Sema.comptime_args` corresponds to the generic owner 351 /// function type. 352 /// This memory is allocated by a parent `Sema` in the temporary arena, and is 353 /// used to add a `func_instance` into the `InternPool`. 354 params: std.MultiArrayList(Param) = .{}, 355 356 label: ?*Label = null, 357 inlining: ?*Inlining, 358 /// If runtime_index is not 0 then one of these is guaranteed to be non null. 359 runtime_cond: ?LazySrcLoc = null, 360 runtime_loop: ?LazySrcLoc = null, 361 /// Non zero if a non-inline loop or a runtime conditional have been encountered. 362 /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index. 363 runtime_index: RuntimeIndex = .zero, 364 inline_block: Zir.Inst.OptionalIndex = .none, 365 366 comptime_reason: ?BlockComptimeReason = null, 367 is_typeof: bool = false, 368 369 /// Keep track of the active error return trace index around blocks so that we can correctly 370 /// pop the error trace upon block exit. 371 error_return_trace_index: Air.Inst.Ref = .none, 372 373 /// when null, it is determined by build mode, changed by @setRuntimeSafety 374 want_safety: ?bool = null, 375 376 /// What mode to generate float operations in, set by @setFloatMode 377 float_mode: std.builtin.FloatMode = .strict, 378 379 c_import_buf: ?*std.array_list.Managed(u8) = null, 380 381 /// If not `null`, this boolean is set when a `dbg_var_ptr`, `dbg_var_val`, or `dbg_arg_inline`. 382 /// instruction is emitted. It signals that the innermost lexically 383 /// enclosing `block`/`block_inline` should be translated into a real AIR 384 /// `block` in order for codegen to match lexical scoping for debug vars. 385 need_debug_scope: ?*bool = null, 386 387 /// Relative source locations encountered while traversing this block should be 388 /// treated as relative to the AST node of this ZIR instruction. 389 src_base_inst: InternPool.TrackedInst.Index, 390 391 /// The name of the current "context" for naming namespace types. 392 /// The interpretation of this depends on the name strategy in ZIR, but the name 393 /// is always incorporated into the type name somehow. 394 /// See `Sema.createTypeName`. 395 type_name_ctx: InternPool.NullTerminatedString, 396 397 /// Create a `LazySrcLoc` based on an `Offset` from the code being analyzed in this block. 398 /// Specifically, the given `Offset` is treated as relative to `block.src_base_inst`. 399 pub fn src(block: Block, offset: LazySrcLoc.Offset) LazySrcLoc { 400 return .{ 401 .base_node_inst = block.src_base_inst, 402 .offset = offset, 403 }; 404 } 405 406 fn isComptime(block: Block) bool { 407 return block.comptime_reason != null; 408 } 409 410 fn builtinCallArgSrc(block: *Block, builtin_call_node: std.zig.Ast.Node.Offset, arg_index: u32) LazySrcLoc { 411 return block.src(.{ .node_offset_builtin_call_arg = .{ 412 .builtin_call_node = builtin_call_node, 413 .arg_index = arg_index, 414 } }); 415 } 416 417 pub fn nodeOffset(block: Block, node_offset: std.zig.Ast.Node.Offset) LazySrcLoc { 418 return block.src(LazySrcLoc.Offset.nodeOffset(node_offset)); 419 } 420 421 fn tokenOffset(block: Block, tok_offset: std.zig.Ast.TokenOffset) LazySrcLoc { 422 return block.src(.{ .token_offset = tok_offset }); 423 } 424 425 const Param = struct { 426 /// `none` means `anytype`. 427 ty: InternPool.Index, 428 is_comptime: bool, 429 name: Zir.NullTerminatedString, 430 }; 431 432 /// This `Block` maps a block ZIR instruction to the corresponding 433 /// AIR instruction for break instruction analysis. 434 pub const Label = struct { 435 zir_block: Zir.Inst.Index, 436 merges: Merges, 437 }; 438 439 /// This `Block` indicates that an inline function call is happening 440 /// and return instructions should be analyzed as a break instruction 441 /// to this AIR block instruction. 442 /// It is shared among all the blocks in an inline or comptime called 443 /// function. 444 pub const Inlining = struct { 445 call_block: *Block, 446 call_src: LazySrcLoc, 447 func: InternPool.Index, 448 449 /// Populated lazily by `refFrame`. 450 ref_frame: Zcu.InlineReferenceFrame.Index.Optional = .none, 451 452 /// If `true`, the following fields are `undefined`. This doesn't represent a true inline 453 /// call, but rather a generic call analyzing the instantiation's generic type bodies. 454 is_generic_instantiation: bool, 455 456 has_comptime_args: bool, 457 comptime_result: Air.Inst.Ref, 458 merges: Merges, 459 460 fn refFrame(inlining: *Inlining, zcu: *Zcu) Allocator.Error!Zcu.InlineReferenceFrame.Index { 461 if (inlining.ref_frame == .none) { 462 inlining.ref_frame = (try zcu.addInlineReferenceFrame(.{ 463 .callee = inlining.func, 464 .call_src = inlining.call_src, 465 .parent = if (inlining.call_block.inlining) |parent_inlining| p: { 466 break :p (try parent_inlining.refFrame(zcu)).toOptional(); 467 } else .none, 468 })).toOptional(); 469 } 470 return inlining.ref_frame.unwrap().?; 471 } 472 }; 473 474 pub const Merges = struct { 475 block_inst: Air.Inst.Index, 476 /// Separate array list from break_inst_list so that it can be passed directly 477 /// to resolvePeerTypes. 478 results: std.ArrayListUnmanaged(Air.Inst.Ref), 479 /// Keeps track of the break instructions so that the operand can be replaced 480 /// if we need to add type coercion at the end of block analysis. 481 /// Same indexes, capacity, length as `results`. 482 br_list: std.ArrayListUnmanaged(Air.Inst.Index), 483 /// Keeps the source location of the rhs operand of the break instruction, 484 /// to enable more precise compile errors. 485 /// Same indexes, capacity, length as `results`. 486 src_locs: std.ArrayListUnmanaged(?LazySrcLoc), 487 /// Most blocks do not utilize this field. When it is used, its use is 488 /// contextual. The possible uses are as follows: 489 /// * for a `switch_block[_ref]`, this refers to dummy `br` instructions 490 /// which correspond to `switch_continue` ZIR. The switch logic will 491 /// rewrite these to appropriate AIR switch dispatches. 492 extra_insts: std.ArrayListUnmanaged(Air.Inst.Index) = .empty, 493 /// Same indexes, capacity, length as `extra_insts`. 494 extra_src_locs: std.ArrayListUnmanaged(LazySrcLoc) = .empty, 495 496 pub fn deinit(merges: *@This(), allocator: Allocator) void { 497 merges.results.deinit(allocator); 498 merges.br_list.deinit(allocator); 499 merges.src_locs.deinit(allocator); 500 merges.extra_insts.deinit(allocator); 501 merges.extra_src_locs.deinit(allocator); 502 } 503 }; 504 505 pub fn makeSubBlock(parent: *Block) Block { 506 return .{ 507 .parent = parent, 508 .sema = parent.sema, 509 .namespace = parent.namespace, 510 .instructions = .{}, 511 .label = null, 512 .inlining = parent.inlining, 513 .comptime_reason = parent.comptime_reason, 514 .is_typeof = parent.is_typeof, 515 .runtime_cond = parent.runtime_cond, 516 .runtime_loop = parent.runtime_loop, 517 .runtime_index = parent.runtime_index, 518 .want_safety = parent.want_safety, 519 .float_mode = parent.float_mode, 520 .c_import_buf = parent.c_import_buf, 521 .error_return_trace_index = parent.error_return_trace_index, 522 .need_debug_scope = parent.need_debug_scope, 523 .src_base_inst = parent.src_base_inst, 524 .type_name_ctx = parent.type_name_ctx, 525 }; 526 } 527 528 fn wantSafeTypes(block: *const Block) bool { 529 return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) { 530 .Debug => true, 531 .ReleaseSafe => true, 532 .ReleaseFast => false, 533 .ReleaseSmall => false, 534 }; 535 } 536 537 fn wantSafety(block: *const Block) bool { 538 if (block.isComptime()) return false; // runtime safety checks are pointless in comptime blocks 539 return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) { 540 .Debug => true, 541 .ReleaseSafe => true, 542 .ReleaseFast => false, 543 .ReleaseSmall => false, 544 }; 545 } 546 547 pub fn getFileScope(block: *Block, zcu: *Zcu) *Zcu.File { 548 return zcu.fileByIndex(getFileScopeIndex(block, zcu)); 549 } 550 551 pub fn getFileScopeIndex(block: *Block, zcu: *Zcu) Zcu.File.Index { 552 return zcu.namespacePtr(block.namespace).file_scope; 553 } 554 555 fn addTy( 556 block: *Block, 557 tag: Air.Inst.Tag, 558 ty: Type, 559 ) error{OutOfMemory}!Air.Inst.Ref { 560 return block.addInst(.{ 561 .tag = tag, 562 .data = .{ .ty = ty }, 563 }); 564 } 565 566 fn addTyOp( 567 block: *Block, 568 tag: Air.Inst.Tag, 569 ty: Type, 570 operand: Air.Inst.Ref, 571 ) error{OutOfMemory}!Air.Inst.Ref { 572 return block.addInst(.{ 573 .tag = tag, 574 .data = .{ .ty_op = .{ 575 .ty = Air.internedToRef(ty.toIntern()), 576 .operand = operand, 577 } }, 578 }); 579 } 580 581 fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref { 582 return block.addInst(.{ 583 .tag = .bitcast, 584 .data = .{ .ty_op = .{ 585 .ty = Air.internedToRef(ty.toIntern()), 586 .operand = operand, 587 } }, 588 }); 589 } 590 591 fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref { 592 return block.addInst(.{ 593 .tag = tag, 594 .data = .{ .no_op = {} }, 595 }); 596 } 597 598 fn addUnOp( 599 block: *Block, 600 tag: Air.Inst.Tag, 601 operand: Air.Inst.Ref, 602 ) error{OutOfMemory}!Air.Inst.Ref { 603 return block.addInst(.{ 604 .tag = tag, 605 .data = .{ .un_op = operand }, 606 }); 607 } 608 609 fn addBr( 610 block: *Block, 611 target_block: Air.Inst.Index, 612 operand: Air.Inst.Ref, 613 ) error{OutOfMemory}!Air.Inst.Ref { 614 return block.addInst(.{ 615 .tag = .br, 616 .data = .{ .br = .{ 617 .block_inst = target_block, 618 .operand = operand, 619 } }, 620 }); 621 } 622 623 fn addBinOp( 624 block: *Block, 625 tag: Air.Inst.Tag, 626 lhs: Air.Inst.Ref, 627 rhs: Air.Inst.Ref, 628 ) error{OutOfMemory}!Air.Inst.Ref { 629 return block.addInst(.{ 630 .tag = tag, 631 .data = .{ .bin_op = .{ 632 .lhs = lhs, 633 .rhs = rhs, 634 } }, 635 }); 636 } 637 638 fn addStructFieldPtr( 639 block: *Block, 640 struct_ptr: Air.Inst.Ref, 641 field_index: u32, 642 ptr_field_ty: Type, 643 ) !Air.Inst.Ref { 644 const ty = Air.internedToRef(ptr_field_ty.toIntern()); 645 const tag: Air.Inst.Tag = switch (field_index) { 646 0 => .struct_field_ptr_index_0, 647 1 => .struct_field_ptr_index_1, 648 2 => .struct_field_ptr_index_2, 649 3 => .struct_field_ptr_index_3, 650 else => { 651 return block.addInst(.{ 652 .tag = .struct_field_ptr, 653 .data = .{ .ty_pl = .{ 654 .ty = ty, 655 .payload = try block.sema.addExtra(Air.StructField{ 656 .struct_operand = struct_ptr, 657 .field_index = field_index, 658 }), 659 } }, 660 }); 661 }, 662 }; 663 return block.addInst(.{ 664 .tag = tag, 665 .data = .{ .ty_op = .{ 666 .ty = ty, 667 .operand = struct_ptr, 668 } }, 669 }); 670 } 671 672 fn addStructFieldVal( 673 block: *Block, 674 struct_val: Air.Inst.Ref, 675 field_index: u32, 676 field_ty: Type, 677 ) !Air.Inst.Ref { 678 return block.addInst(.{ 679 .tag = .struct_field_val, 680 .data = .{ .ty_pl = .{ 681 .ty = Air.internedToRef(field_ty.toIntern()), 682 .payload = try block.sema.addExtra(Air.StructField{ 683 .struct_operand = struct_val, 684 .field_index = field_index, 685 }), 686 } }, 687 }); 688 } 689 690 fn addSliceElemPtr( 691 block: *Block, 692 slice: Air.Inst.Ref, 693 elem_index: Air.Inst.Ref, 694 elem_ptr_ty: Type, 695 ) !Air.Inst.Ref { 696 return block.addInst(.{ 697 .tag = .slice_elem_ptr, 698 .data = .{ .ty_pl = .{ 699 .ty = Air.internedToRef(elem_ptr_ty.toIntern()), 700 .payload = try block.sema.addExtra(Air.Bin{ 701 .lhs = slice, 702 .rhs = elem_index, 703 }), 704 } }, 705 }); 706 } 707 708 fn addPtrElemPtr( 709 block: *Block, 710 array_ptr: Air.Inst.Ref, 711 elem_index: Air.Inst.Ref, 712 elem_ptr_ty: Type, 713 ) !Air.Inst.Ref { 714 const ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 715 return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref); 716 } 717 718 fn addPtrElemPtrTypeRef( 719 block: *Block, 720 array_ptr: Air.Inst.Ref, 721 elem_index: Air.Inst.Ref, 722 elem_ptr_ty: Air.Inst.Ref, 723 ) !Air.Inst.Ref { 724 return block.addInst(.{ 725 .tag = .ptr_elem_ptr, 726 .data = .{ .ty_pl = .{ 727 .ty = elem_ptr_ty, 728 .payload = try block.sema.addExtra(Air.Bin{ 729 .lhs = array_ptr, 730 .rhs = elem_index, 731 }), 732 } }, 733 }); 734 } 735 736 fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref { 737 const sema = block.sema; 738 const pt = sema.pt; 739 const zcu = pt.zcu; 740 return block.addInst(.{ 741 .tag = if (block.float_mode == .optimized) .cmp_vector_optimized else .cmp_vector, 742 .data = .{ .ty_pl = .{ 743 .ty = Air.internedToRef((try pt.vectorType(.{ 744 .len = sema.typeOf(lhs).vectorLen(zcu), 745 .child = .bool_type, 746 })).toIntern()), 747 .payload = try sema.addExtra(Air.VectorCmp{ 748 .lhs = lhs, 749 .rhs = rhs, 750 .op = Air.VectorCmp.encodeOp(cmp_op), 751 }), 752 } }, 753 }); 754 } 755 756 fn addReduce(block: *Block, operand: Air.Inst.Ref, operation: std.builtin.ReduceOp) !Air.Inst.Ref { 757 const sema = block.sema; 758 const zcu = sema.pt.zcu; 759 const allow_optimized = switch (sema.typeOf(operand).childType(zcu).zigTypeTag(zcu)) { 760 .float => true, 761 .bool, .int => false, 762 else => unreachable, 763 }; 764 return block.addInst(.{ 765 .tag = if (allow_optimized and block.float_mode == .optimized) .reduce_optimized else .reduce, 766 .data = .{ .reduce = .{ 767 .operand = operand, 768 .operation = operation, 769 } }, 770 }); 771 } 772 773 fn addAggregateInit( 774 block: *Block, 775 aggregate_ty: Type, 776 elements: []const Air.Inst.Ref, 777 ) !Air.Inst.Ref { 778 const sema = block.sema; 779 const ty_ref = Air.internedToRef(aggregate_ty.toIntern()); 780 try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len); 781 const extra_index: u32 = @intCast(sema.air_extra.items.len); 782 sema.appendRefsAssumeCapacity(elements); 783 784 return block.addInst(.{ 785 .tag = .aggregate_init, 786 .data = .{ .ty_pl = .{ 787 .ty = ty_ref, 788 .payload = extra_index, 789 } }, 790 }); 791 } 792 793 fn addUnionInit( 794 block: *Block, 795 union_ty: Type, 796 field_index: u32, 797 init: Air.Inst.Ref, 798 ) !Air.Inst.Ref { 799 return block.addInst(.{ 800 .tag = .union_init, 801 .data = .{ .ty_pl = .{ 802 .ty = Air.internedToRef(union_ty.toIntern()), 803 .payload = try block.sema.addExtra(Air.UnionInit{ 804 .field_index = field_index, 805 .init = init, 806 }), 807 } }, 808 }); 809 } 810 811 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { 812 return (try block.addInstAsIndex(inst)).toRef(); 813 } 814 815 pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { 816 const sema = block.sema; 817 const gpa = sema.gpa; 818 819 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 820 try block.instructions.ensureUnusedCapacity(gpa, 1); 821 822 const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 823 sema.air_instructions.appendAssumeCapacity(inst); 824 block.instructions.appendAssumeCapacity(result_index); 825 return result_index; 826 } 827 828 /// Insert an instruction into the block at `index`. Moves all following 829 /// instructions forward in the block to make room. Operation is O(N). 830 pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { 831 return (try block.insertInstAsIndex(index, inst)).toRef(); 832 } 833 834 pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { 835 const sema = block.sema; 836 const gpa = sema.gpa; 837 838 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 839 840 const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 841 sema.air_instructions.appendAssumeCapacity(inst); 842 843 try block.instructions.insert(gpa, @intFromEnum(index), result_index); 844 return result_index; 845 } 846 847 pub fn ownerModule(block: Block) *Package.Module { 848 const zcu = block.sema.pt.zcu; 849 return zcu.namespacePtr(block.namespace).fileScope(zcu).mod.?; 850 } 851 852 fn trackZir(block: *Block, inst: Zir.Inst.Index) Allocator.Error!InternPool.TrackedInst.Index { 853 const pt = block.sema.pt; 854 block.sema.code.assertTrackable(inst); 855 return pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{ 856 .file = block.getFileScopeIndex(pt.zcu), 857 .inst = inst, 858 }); 859 } 860 861 /// Returns the `*Block` that should be passed to `Sema.failWithOwnedErrorMsg`, because all inline 862 /// calls below it have already been reported with "called at comptime from here" notes. 863 fn explainWhyBlockIsComptime(start_block: *Block, err_msg: *Zcu.ErrorMsg) !*Block { 864 const sema = start_block.sema; 865 var block = start_block; 866 while (true) { 867 switch (block.comptime_reason.?) { 868 .inlining_parent => { 869 const inlining = block.inlining.?; 870 try sema.errNote(inlining.call_src, err_msg, "called at comptime from here", .{}); 871 block = inlining.call_block; 872 }, 873 .reason => |r| { 874 try r.r.explain(sema, r.src, err_msg); 875 return block; 876 }, 877 } 878 } 879 } 880 }; 881 882 /// Represents the reason we are resolving a value or evaluating code at comptime. 883 /// Most reasons are represented by a `std.zig.SimpleComptimeReason`, which provides a plain message. 884 const ComptimeReason = union(enum) { 885 /// Evaluating at comptime for a reason in the `std.zig.SimpleComptimeReason` enum. 886 simple: std.zig.SimpleComptimeReason, 887 888 /// Evaluating at comptime because of a comptime-only type. This field is separate so that 889 /// the type in question can be included in the error message. AstGen could never emit this 890 /// reason, because it knows nothing of types. 891 /// The format string looks like "foo '{f}' bar", where "{f}" is the comptime-only type. 892 /// We will then explain why this type is comptime-only. 893 comptime_only: struct { 894 ty: Type, 895 msg: enum { 896 union_init, 897 struct_init, 898 tuple_init, 899 }, 900 }, 901 902 /// Like `comptime_only`, but for a parameter type. 903 /// Includes a "parameter type declared here" note. 904 comptime_only_param_ty: struct { 905 ty: Type, 906 param_ty_src: LazySrcLoc, 907 }, 908 909 /// Like `comptime_only`, but for a return type. 910 /// Includes a "return type declared here" note. 911 comptime_only_ret_ty: struct { 912 ty: Type, 913 is_generic_inst: bool, 914 ret_ty_src: LazySrcLoc, 915 }, 916 917 /// Evaluating at comptime because we're evaluating an argument to a parameter marked `comptime`. 918 comptime_param: struct { 919 comptime_src: LazySrcLoc, 920 }, 921 922 fn explain(reason: ComptimeReason, sema: *Sema, src: LazySrcLoc, err_msg: *Zcu.ErrorMsg) !void { 923 switch (reason) { 924 .simple => |simple| { 925 try sema.errNote(src, err_msg, "{s}", .{simple.message()}); 926 }, 927 .comptime_only => |co| { 928 const pre, const post = switch (co.msg) { 929 .union_init => .{ "initializer of comptime-only union", "must be comptime-known" }, 930 .struct_init => .{ "initializer of comptime-only struct", "must be comptime-known" }, 931 .tuple_init => .{ "initializer of comptime-only tuple", "must be comptime-known" }, 932 }; 933 try sema.errNote(src, err_msg, "{s} '{f}' {s}", .{ pre, co.ty.fmt(sema.pt), post }); 934 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); 935 }, 936 .comptime_only_param_ty => |co| { 937 try sema.errNote(src, err_msg, "argument to parameter with comptime-only type '{f}' must be comptime-known", .{co.ty.fmt(sema.pt)}); 938 try sema.errNote(co.param_ty_src, err_msg, "parameter type declared here", .{}); 939 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); 940 }, 941 .comptime_only_ret_ty => |co| { 942 const function_with: []const u8 = if (co.is_generic_inst) "generic function instantiated with" else "function with"; 943 try sema.errNote(src, err_msg, "call to {s} comptime-only return type '{f}' is evaluated at comptime", .{ function_with, co.ty.fmt(sema.pt) }); 944 try sema.errNote(co.ret_ty_src, err_msg, "return type declared here", .{}); 945 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); 946 }, 947 .comptime_param => |cp| { 948 try sema.errNote(src, err_msg, "argument to comptime parameter must be comptime-known", .{}); 949 try sema.errNote(cp.comptime_src, err_msg, "parameter declared comptime here", .{}); 950 }, 951 } 952 } 953 }; 954 955 /// Represents the reason a `Block` is being evaluated at comptime. 956 const BlockComptimeReason = union(enum) { 957 /// This block inherits being comptime-only from the `inlining` call site. 958 inlining_parent, 959 960 /// Comptime evaluation began somewhere in the current function for a given `ComptimeReason`. 961 reason: struct { 962 /// The source location which this reason originates from. `r` is reported here. 963 src: LazySrcLoc, 964 r: ComptimeReason, 965 }, 966 }; 967 968 const LabeledBlock = struct { 969 block: Block, 970 label: Block.Label, 971 972 fn destroy(lb: *LabeledBlock, gpa: Allocator) void { 973 lb.block.instructions.deinit(gpa); 974 lb.label.merges.deinit(gpa); 975 gpa.destroy(lb); 976 } 977 }; 978 979 /// The value stored in the inferred allocation. This will go into 980 /// peer type resolution. This is stored in a separate list so that 981 /// the items are contiguous in memory and thus can be passed to 982 /// `Zcu.resolvePeerTypes`. 983 const InferredAlloc = struct { 984 /// The placeholder `store` instructions used before the result pointer type 985 /// is known. These should be rewritten to perform any required coercions 986 /// when the type is resolved. 987 /// Allocated from `sema.arena`. 988 prongs: std.ArrayListUnmanaged(Air.Inst.Index) = .empty, 989 }; 990 991 pub fn deinit(sema: *Sema) void { 992 const gpa = sema.gpa; 993 sema.air_instructions.deinit(gpa); 994 sema.air_extra.deinit(gpa); 995 sema.inst_map.deinit(gpa); 996 { 997 var it = sema.post_hoc_blocks.iterator(); 998 while (it.next()) |entry| { 999 const labeled_block = entry.value_ptr.*; 1000 labeled_block.destroy(gpa); 1001 } 1002 sema.post_hoc_blocks.deinit(gpa); 1003 } 1004 sema.unresolved_inferred_allocs.deinit(gpa); 1005 sema.base_allocs.deinit(gpa); 1006 sema.maybe_comptime_allocs.deinit(gpa); 1007 sema.comptime_allocs.deinit(gpa); 1008 sema.exports.deinit(gpa); 1009 sema.references.deinit(gpa); 1010 sema.type_references.deinit(gpa); 1011 sema.dependencies.deinit(gpa); 1012 sema.* = undefined; 1013 } 1014 1015 /// Performs semantic analysis of a ZIR body which is behind a runtime condition. If comptime 1016 /// control flow happens here, Sema will convert it to runtime control flow by introducing post-hoc 1017 /// blocks where necessary. 1018 /// Returns the branch hint for this branch. 1019 fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !std.builtin.BranchHint { 1020 const parent_hint = sema.branch_hint; 1021 defer sema.branch_hint = parent_hint; 1022 sema.branch_hint = null; 1023 1024 sema.analyzeBodyInner(block, body) catch |err| switch (err) { 1025 error.ComptimeBreak => { 1026 const zir_datas = sema.code.instructions.items(.data); 1027 const break_data = zir_datas[@intFromEnum(sema.comptime_break_inst)].@"break"; 1028 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 1029 try sema.addRuntimeBreak(block, extra.block_inst, break_data.operand); 1030 }, 1031 else => |e| return e, 1032 }; 1033 1034 return sema.branch_hint orelse .none; 1035 } 1036 1037 /// Semantically analyze a ZIR function body. It is guranteed by AstGen that such a body cannot 1038 /// trigger comptime control flow to move above the function body. 1039 pub fn analyzeFnBody( 1040 sema: *Sema, 1041 block: *Block, 1042 body: []const Zir.Inst.Index, 1043 ) !void { 1044 sema.analyzeBodyInner(block, body) catch |err| switch (err) { 1045 error.ComptimeBreak => unreachable, // unexpected comptime control flow 1046 else => |e| return e, 1047 }; 1048 } 1049 1050 /// Given a ZIR body which can be exited via a `break_inline` instruction, or a non-inline body which 1051 /// we are evaluating at comptime, semantically analyze the body and return the result from it. 1052 /// Returns `null` if control flow did not break from this block, but instead terminated with some 1053 /// other runtime noreturn instruction. Compile-time breaks to blocks further up the stack still 1054 /// return `error.ComptimeBreak`. If `block.isComptime()`, this function will never return `null`. 1055 fn analyzeInlineBody( 1056 sema: *Sema, 1057 block: *Block, 1058 body: []const Zir.Inst.Index, 1059 /// The index which a break instruction can target to break from this body. 1060 break_target: Zir.Inst.Index, 1061 ) CompileError!?Air.Inst.Ref { 1062 if (sema.analyzeBodyInner(block, body)) |_| { 1063 return null; 1064 } else |err| switch (err) { 1065 error.ComptimeBreak => {}, 1066 else => |e| return e, 1067 } 1068 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst)); 1069 switch (break_inst.tag) { 1070 .switch_continue => { 1071 // This is handled by separate logic. 1072 return error.ComptimeBreak; 1073 }, 1074 .break_inline, .@"break" => {}, 1075 else => unreachable, 1076 } 1077 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data; 1078 if (extra.block_inst != break_target) { 1079 // This control flow goes further up the stack. 1080 return error.ComptimeBreak; 1081 } 1082 return try sema.resolveInst(break_inst.data.@"break".operand); 1083 } 1084 1085 /// Like `analyzeInlineBody`, but if the body does not break with a value, returns 1086 /// `.unreachable_value` instead of `null`. Notably, use this to evaluate an arbitrary 1087 /// body at comptime to a single result value. 1088 pub fn resolveInlineBody( 1089 sema: *Sema, 1090 block: *Block, 1091 body: []const Zir.Inst.Index, 1092 /// The index which a break instruction can target to break from this body. 1093 break_target: Zir.Inst.Index, 1094 ) CompileError!Air.Inst.Ref { 1095 return (try sema.analyzeInlineBody(block, body, break_target)) orelse .unreachable_value; 1096 } 1097 1098 /// This function is the main loop of `Sema`. It analyzes a single body of ZIR instructions. 1099 /// 1100 /// If this function returns normally, the merges of `block` were populated with all possible 1101 /// (runtime) results of this block. Peer type resolution should be performed on the result, 1102 /// and relevant runtime instructions written to perform necessary coercions and breaks. See 1103 /// `resolveAnalyzedBlock`. This form of return is impossible if `block.isComptime()`. 1104 /// 1105 /// Alternatively, this function may return `error.ComptimeBreak`. This indicates that comptime 1106 /// control flow is happening, and we are breaking at comptime from a block indicated by the 1107 /// break instruction in `sema.comptime_break_inst`. This occurs for any `break_inline`, or for a 1108 /// standard `break` at comptime. This error is pushed up the stack until the target block is 1109 /// reached, at which point the break operand will be fetched. 1110 /// 1111 /// It is rare to call this function directly. Usually, you want one of the following wrappers: 1112 /// * If the body is exited via a `break_inline`, or is being evaluated at comptime, 1113 /// use `Sema.analyzeInlineBody` or `Sema.resolveInlineBody`. 1114 /// * If the body is behind a fresh runtime condition, use `Sema.analyzeBodyRuntimeBreak`. 1115 /// * If the body is an entire function body, use `Sema.analyzeFnBody`. 1116 /// * If the body is to be generated into an AIR `block`, use `Sema.resolveBlockBody`. 1117 /// * Otherwise, direct usage of `Sema.analyzeBodyInner` may be necessary. 1118 fn analyzeBodyInner( 1119 sema: *Sema, 1120 block: *Block, 1121 body: []const Zir.Inst.Index, 1122 ) CompileError!void { 1123 // No tracy calls here, to avoid interfering with the tail call mechanism. 1124 1125 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); 1126 1127 const pt = sema.pt; 1128 const zcu = pt.zcu; 1129 const map = &sema.inst_map; 1130 const tags = sema.code.instructions.items(.tag); 1131 const datas = sema.code.instructions.items(.data); 1132 1133 var crash_info = crash_report.prepAnalyzeBody(sema, block, body); 1134 crash_info.push(); 1135 defer crash_info.pop(); 1136 1137 // We use a while (true) loop here to avoid a redundant way of breaking out of 1138 // the loop. The only way to break out of the loop is with a `noreturn` 1139 // instruction. 1140 var i: u32 = 0; 1141 while (true) { 1142 crash_info.setBodyIndex(i); 1143 const inst = body[i]; 1144 1145 // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away. 1146 if (build_options.enable_logging) { 1147 std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path: { 1148 const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool); 1149 const file = zcu.fileByIndex(file_index); 1150 break :path file.path.fmt(zcu.comp); 1151 }, inst }); 1152 } 1153 1154 const air_inst: Air.Inst.Ref = inst: switch (tags[@intFromEnum(inst)]) { 1155 // zig fmt: off 1156 .alloc => try sema.zirAlloc(block, inst), 1157 .alloc_inferred => try sema.zirAllocInferred(block, true), 1158 .alloc_inferred_mut => try sema.zirAllocInferred(block, false), 1159 .alloc_inferred_comptime => try sema.zirAllocInferredComptime(true), 1160 .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(false), 1161 .resolve_inferred_alloc => try sema.zirResolveInferredAlloc(block, inst), 1162 .alloc_mut => try sema.zirAllocMut(block, inst), 1163 .alloc_comptime_mut => try sema.zirAllocComptime(block, inst), 1164 .make_ptr_const => try sema.zirMakePtrConst(block, inst), 1165 .anyframe_type => try sema.zirAnyframeType(block, inst), 1166 .array_cat => try sema.zirArrayCat(block, inst), 1167 .array_mul => try sema.zirArrayMul(block, inst), 1168 .array_type => try sema.zirArrayType(block, inst), 1169 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), 1170 .vector_type => try sema.zirVectorType(block, inst), 1171 .as_node => try sema.zirAsNode(block, inst), 1172 .as_shift_operand => try sema.zirAsShiftOperand(block, inst), 1173 .bit_and => try sema.zirBitwise(block, inst, .bit_and), 1174 .bit_not => try sema.zirBitNot(block, inst), 1175 .bit_or => try sema.zirBitwise(block, inst, .bit_or), 1176 .bitcast => try sema.zirBitcast(block, inst), 1177 .suspend_block => try sema.zirSuspendBlock(block, inst), 1178 .bool_not => try sema.zirBoolNot(block, inst), 1179 .bool_br_and => try sema.zirBoolBr(block, inst, false), 1180 .bool_br_or => try sema.zirBoolBr(block, inst, true), 1181 .c_import => try sema.zirCImport(block, inst), 1182 .call => try sema.zirCall(block, inst, .direct), 1183 .field_call => try sema.zirCall(block, inst, .field), 1184 .cmp_lt => try sema.zirCmp(block, inst, .lt), 1185 .cmp_lte => try sema.zirCmp(block, inst, .lte), 1186 .cmp_eq => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .optimized)), 1187 .cmp_gte => try sema.zirCmp(block, inst, .gte), 1188 .cmp_gt => try sema.zirCmp(block, inst, .gt), 1189 .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .optimized)), 1190 .decl_ref => try sema.zirDeclRef(block, inst), 1191 .decl_val => try sema.zirDeclVal(block, inst), 1192 .load => try sema.zirLoad(block, inst), 1193 .elem_ptr => try sema.zirElemPtr(block, inst), 1194 .elem_ptr_node => try sema.zirElemPtrNode(block, inst), 1195 .elem_val => try sema.zirElemVal(block, inst), 1196 .elem_ptr_load => try sema.zirElemPtrLoad(block, inst), 1197 .elem_val_imm => try sema.zirElemValImm(block, inst), 1198 .elem_type => try sema.zirElemType(block, inst), 1199 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), 1200 .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst), 1201 .enum_literal => try sema.zirEnumLiteral(block, inst), 1202 .decl_literal => try sema.zirDeclLiteral(block, inst, true), 1203 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false), 1204 .int_from_enum => try sema.zirIntFromEnum(block, inst), 1205 .enum_from_int => try sema.zirEnumFromInt(block, inst), 1206 .err_union_code => try sema.zirErrUnionCode(block, inst), 1207 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), 1208 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst), 1209 .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst), 1210 .error_union_type => try sema.zirErrorUnionType(block, inst), 1211 .error_value => try sema.zirErrorValue(block, inst), 1212 .field_ptr => try sema.zirFieldPtr(block, inst), 1213 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), 1214 .field_ptr_load => try sema.zirFieldPtrLoad(block, inst), 1215 .field_ptr_named_load => try sema.zirFieldPtrNamedLoad(block, inst), 1216 .func => try sema.zirFunc(block, inst, false), 1217 .func_inferred => try sema.zirFunc(block, inst, true), 1218 .func_fancy => try sema.zirFuncFancy(block, inst), 1219 .import => try sema.zirImport(block, inst), 1220 .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), 1221 .int => try sema.zirInt(block, inst), 1222 .int_big => try sema.zirIntBig(block, inst), 1223 .float => try sema.zirFloat(block, inst), 1224 .float128 => try sema.zirFloat128(block, inst), 1225 .int_type => try sema.zirIntType(inst), 1226 .is_non_err => try sema.zirIsNonErr(block, inst), 1227 .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), 1228 .ret_is_non_err => try sema.zirRetIsNonErr(block, inst), 1229 .is_non_null => try sema.zirIsNonNull(block, inst), 1230 .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), 1231 .merge_error_sets => try sema.zirMergeErrorSets(block, inst), 1232 .negate => try sema.zirNegate(block, inst), 1233 .negate_wrap => try sema.zirNegateWrap(block, inst), 1234 .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), 1235 .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), 1236 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), 1237 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), 1238 .optional_type => try sema.zirOptionalType(block, inst), 1239 .ptr_type => try sema.zirPtrType(block, inst), 1240 .ref => try sema.zirRef(block, inst), 1241 .ret_err_value_code => try sema.zirRetErrValueCode(inst), 1242 .shr => try sema.zirShr(block, inst, .shr), 1243 .shr_exact => try sema.zirShr(block, inst, .shr_exact), 1244 .slice_end => try sema.zirSliceEnd(block, inst), 1245 .slice_sentinel => try sema.zirSliceSentinel(block, inst), 1246 .slice_start => try sema.zirSliceStart(block, inst), 1247 .slice_length => try sema.zirSliceLength(block, inst), 1248 .slice_sentinel_ty => try sema.zirSliceSentinelTy(block, inst), 1249 .str => try sema.zirStr(inst), 1250 .switch_block => try sema.zirSwitchBlock(block, inst, false), 1251 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), 1252 .switch_block_err_union => try sema.zirSwitchBlockErrUnion(block, inst), 1253 .type_info => try sema.zirTypeInfo(block, inst), 1254 .size_of => try sema.zirSizeOf(block, inst), 1255 .bit_size_of => try sema.zirBitSizeOf(block, inst), 1256 .typeof => try sema.zirTypeof(block, inst), 1257 .typeof_builtin => try sema.zirTypeofBuiltin(block, inst), 1258 .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), 1259 .xor => try sema.zirBitwise(block, inst, .xor), 1260 .struct_init_empty => try sema.zirStructInitEmpty(block, inst), 1261 .struct_init_empty_result => try sema.zirStructInitEmptyResult(block, inst, false), 1262 .struct_init_empty_ref_result => try sema.zirStructInitEmptyResult(block, inst, true), 1263 .struct_init_anon => try sema.zirStructInitAnon(block, inst), 1264 .struct_init => try sema.zirStructInit(block, inst, false), 1265 .struct_init_ref => try sema.zirStructInit(block, inst, true), 1266 .struct_init_field_type => try sema.zirStructInitFieldType(block, inst), 1267 .struct_init_field_ptr => try sema.zirStructInitFieldPtr(block, inst), 1268 .array_init_anon => try sema.zirArrayInitAnon(block, inst), 1269 .array_init => try sema.zirArrayInit(block, inst, false), 1270 .array_init_ref => try sema.zirArrayInit(block, inst, true), 1271 .array_init_elem_type => try sema.zirArrayInitElemType(block, inst), 1272 .array_init_elem_ptr => try sema.zirArrayInitElemPtr(block, inst), 1273 .union_init => try sema.zirUnionInit(block, inst), 1274 .field_type_ref => try sema.zirFieldTypeRef(block, inst), 1275 .int_from_ptr => try sema.zirIntFromPtr(block, inst), 1276 .align_of => try sema.zirAlignOf(block, inst), 1277 .int_from_bool => try sema.zirIntFromBool(block, inst), 1278 .embed_file => try sema.zirEmbedFile(block, inst), 1279 .error_name => try sema.zirErrorName(block, inst), 1280 .tag_name => try sema.zirTagName(block, inst), 1281 .type_name => try sema.zirTypeName(block, inst), 1282 .frame_type => try sema.zirFrameType(block, inst), 1283 .int_from_float => try sema.zirIntFromFloat(block, inst), 1284 .float_from_int => try sema.zirFloatFromInt(block, inst), 1285 .ptr_from_int => try sema.zirPtrFromInt(block, inst), 1286 .float_cast => try sema.zirFloatCast(block, inst), 1287 .int_cast => try sema.zirIntCast(block, inst), 1288 .ptr_cast => try sema.zirPtrCast(block, inst), 1289 .truncate => try sema.zirTruncate(block, inst), 1290 .has_decl => try sema.zirHasDecl(block, inst), 1291 .has_field => try sema.zirHasField(block, inst), 1292 .byte_swap => try sema.zirByteSwap(block, inst), 1293 .bit_reverse => try sema.zirBitReverse(block, inst), 1294 .bit_offset_of => try sema.zirBitOffsetOf(block, inst), 1295 .offset_of => try sema.zirOffsetOf(block, inst), 1296 .splat => try sema.zirSplat(block, inst), 1297 .reduce => try sema.zirReduce(block, inst), 1298 .shuffle => try sema.zirShuffle(block, inst), 1299 .atomic_load => try sema.zirAtomicLoad(block, inst), 1300 .atomic_rmw => try sema.zirAtomicRmw(block, inst), 1301 .mul_add => try sema.zirMulAdd(block, inst), 1302 .builtin_call => try sema.zirBuiltinCall(block, inst), 1303 .@"resume" => try sema.zirResume(block, inst), 1304 .for_len => try sema.zirForLen(block, inst), 1305 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), 1306 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), 1307 .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst), 1308 1309 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), 1310 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), 1311 .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount), 1312 .abs => try sema.zirAbs(block, inst), 1313 1314 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), 1315 .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), 1316 .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos), 1317 .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan), 1318 .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp), 1319 .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2), 1320 .log => try sema.zirUnaryMath(block, inst, .log, Value.log), 1321 .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2), 1322 .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10), 1323 .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor), 1324 .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil), 1325 .round => try sema.zirUnaryMath(block, inst, .round, Value.round), 1326 .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc), 1327 1328 .error_set_decl => try sema.zirErrorSetDecl(inst), 1329 1330 .add => try sema.zirArithmetic(block, inst, .add, true), 1331 .addwrap => try sema.zirArithmetic(block, inst, .addwrap, true), 1332 .add_sat => try sema.zirArithmetic(block, inst, .add_sat, true), 1333 .add_unsafe => try sema.zirArithmetic(block, inst, .add_unsafe, false), 1334 .mul => try sema.zirArithmetic(block, inst, .mul, true), 1335 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap, true), 1336 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat, true), 1337 .sub => try sema.zirArithmetic(block, inst, .sub, true), 1338 .subwrap => try sema.zirArithmetic(block, inst, .subwrap, true), 1339 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat, true), 1340 1341 .div => try sema.zirDiv(block, inst), 1342 .div_exact => try sema.zirDivExact(block, inst), 1343 .div_floor => try sema.zirDivFloor(block, inst), 1344 .div_trunc => try sema.zirDivTrunc(block, inst), 1345 1346 .mod_rem => try sema.zirModRem(block, inst), 1347 .mod => try sema.zirMod(block, inst), 1348 .rem => try sema.zirRem(block, inst), 1349 1350 .max => try sema.zirMinMax(block, inst, .max), 1351 .min => try sema.zirMinMax(block, inst, .min), 1352 1353 .shl => try sema.zirShl(block, inst, .shl), 1354 .shl_exact => try sema.zirShl(block, inst, .shl_exact), 1355 .shl_sat => try sema.zirShl(block, inst, .shl_sat), 1356 1357 .ret_ptr => try sema.zirRetPtr(block, inst), 1358 .ret_type => Air.internedToRef(sema.fn_ret_ty.toIntern()), 1359 1360 // Instructions that we know to *always* be noreturn based solely on their tag. 1361 // These functions match the return type of analyzeBody so that we can 1362 // tail call them here. 1363 .compile_error => break try sema.zirCompileError(block, inst), 1364 .ret_implicit => break try sema.zirRetImplicit(block, inst), 1365 .ret_node => break try sema.zirRetNode(block, inst), 1366 .ret_load => break try sema.zirRetLoad(block, inst), 1367 .ret_err_value => break try sema.zirRetErrValue(block, inst), 1368 .@"unreachable" => break try sema.zirUnreachable(block, inst), 1369 .panic => break try sema.zirPanic(block, inst), 1370 .trap => break try sema.zirTrap(block, inst), 1371 // zig fmt: on 1372 1373 // This instruction never exists in an analyzed body. It exists only in the declaration 1374 // list for a container type. 1375 .declaration => unreachable, 1376 1377 .extended => ext: { 1378 const extended = datas[@intFromEnum(inst)].extended; 1379 break :ext switch (extended.opcode) { 1380 // zig fmt: off 1381 .struct_decl => try sema.zirStructDecl( block, extended, inst), 1382 .enum_decl => try sema.zirEnumDecl( block, extended, inst), 1383 .union_decl => try sema.zirUnionDecl( block, extended, inst), 1384 .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst), 1385 .tuple_decl => try sema.zirTupleDecl( block, extended), 1386 .this => try sema.zirThis( block, extended), 1387 .ret_addr => try sema.zirRetAddr( block, extended), 1388 .builtin_src => try sema.zirBuiltinSrc( block, extended), 1389 .error_return_trace => try sema.zirErrorReturnTrace( block), 1390 .frame => try sema.zirFrame( block, extended), 1391 .frame_address => try sema.zirFrameAddress( block, extended), 1392 .alloc => try sema.zirAllocExtended( block, extended), 1393 .builtin_extern => try sema.zirBuiltinExtern( block, extended), 1394 .@"asm" => try sema.zirAsm( block, extended, false), 1395 .asm_expr => try sema.zirAsm( block, extended, true), 1396 .typeof_peer => try sema.zirTypeofPeer( block, extended, inst), 1397 .compile_log => try sema.zirCompileLog( block, extended), 1398 .min_multi => try sema.zirMinMaxMulti( block, extended, .min), 1399 .max_multi => try sema.zirMinMaxMulti( block, extended, .max), 1400 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1401 .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1402 .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1403 .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1404 .c_undef => try sema.zirCUndef( block, extended), 1405 .c_include => try sema.zirCInclude( block, extended), 1406 .c_define => try sema.zirCDefine( block, extended), 1407 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), 1408 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), 1409 .prefetch => try sema.zirPrefetch( block, extended), 1410 .error_cast => try sema.zirErrorCast( block, extended), 1411 .select => try sema.zirSelect( block, extended), 1412 .int_from_error => try sema.zirIntFromError( block, extended), 1413 .error_from_int => try sema.zirErrorFromInt( block, extended), 1414 .reify => try sema.zirReify( block, extended, inst), 1415 .cmpxchg => try sema.zirCmpxchg( block, extended), 1416 .c_va_arg => try sema.zirCVaArg( block, extended), 1417 .c_va_copy => try sema.zirCVaCopy( block, extended), 1418 .c_va_end => try sema.zirCVaEnd( block, extended), 1419 .c_va_start => try sema.zirCVaStart( block, extended), 1420 .ptr_cast_full => try sema.zirPtrCastFull( block, extended), 1421 .ptr_cast_no_dest => try sema.zirPtrCastNoDest( block, extended), 1422 .work_item_id => try sema.zirWorkItem( block, extended, extended.opcode), 1423 .work_group_size => try sema.zirWorkItem( block, extended, extended.opcode), 1424 .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode), 1425 .in_comptime => try sema.zirInComptime( block), 1426 .closure_get => try sema.zirClosureGet( block, extended), 1427 // zig fmt: on 1428 1429 .set_float_mode => { 1430 try sema.zirSetFloatMode(block, extended); 1431 i += 1; 1432 continue; 1433 }, 1434 .breakpoint => { 1435 if (!block.isComptime()) { 1436 _ = try block.addNoOp(.breakpoint); 1437 } 1438 i += 1; 1439 continue; 1440 }, 1441 .disable_instrumentation => { 1442 try sema.zirDisableInstrumentation(); 1443 i += 1; 1444 continue; 1445 }, 1446 .disable_intrinsics => { 1447 try sema.zirDisableIntrinsics(); 1448 i += 1; 1449 continue; 1450 }, 1451 .restore_err_ret_index => { 1452 try sema.zirRestoreErrRetIndex(block, extended); 1453 i += 1; 1454 continue; 1455 }, 1456 .branch_hint => { 1457 try sema.zirBranchHint(block, extended); 1458 i += 1; 1459 continue; 1460 }, 1461 .value_placeholder => unreachable, // never appears in a body 1462 .field_parent_ptr => try sema.zirFieldParentPtr(block, extended), 1463 .builtin_value => try sema.zirBuiltinValue(block, extended), 1464 .inplace_arith_result_ty => try sema.zirInplaceArithResultTy(extended), 1465 .dbg_empty_stmt => { 1466 try sema.zirDbgEmptyStmt(block, inst); 1467 i += 1; 1468 continue; 1469 }, 1470 .astgen_error => return error.AnalysisFail, 1471 .float_op_result_ty => try sema.zirFloatOpResultType(block, extended), 1472 }; 1473 }, 1474 1475 // Instructions that we know can *never* be noreturn based solely on 1476 // their tag. We avoid needlessly checking if they are noreturn and 1477 // continue the loop. 1478 // We also know that they cannot be referenced later, so we avoid 1479 // putting them into the map. 1480 .dbg_stmt => { 1481 try sema.zirDbgStmt(block, inst); 1482 i += 1; 1483 continue; 1484 }, 1485 .dbg_var_ptr => { 1486 try sema.zirDbgVar(block, inst, .dbg_var_ptr); 1487 i += 1; 1488 continue; 1489 }, 1490 .dbg_var_val => { 1491 try sema.zirDbgVar(block, inst, .dbg_var_val); 1492 i += 1; 1493 continue; 1494 }, 1495 .ensure_err_union_payload_void => { 1496 try sema.zirEnsureErrUnionPayloadVoid(block, inst); 1497 i += 1; 1498 continue; 1499 }, 1500 .ensure_result_non_error => { 1501 try sema.zirEnsureResultNonError(block, inst); 1502 i += 1; 1503 continue; 1504 }, 1505 .ensure_result_used => { 1506 try sema.zirEnsureResultUsed(block, inst); 1507 i += 1; 1508 continue; 1509 }, 1510 .set_eval_branch_quota => { 1511 try sema.zirSetEvalBranchQuota(block, inst); 1512 i += 1; 1513 continue; 1514 }, 1515 .atomic_store => { 1516 try sema.zirAtomicStore(block, inst); 1517 i += 1; 1518 continue; 1519 }, 1520 .store_node => { 1521 try sema.zirStoreNode(block, inst); 1522 i += 1; 1523 continue; 1524 }, 1525 .store_to_inferred_ptr => { 1526 try sema.zirStoreToInferredPtr(block, inst); 1527 i += 1; 1528 continue; 1529 }, 1530 .validate_struct_init_ty => { 1531 try sema.zirValidateStructInitTy(block, inst, false); 1532 i += 1; 1533 continue; 1534 }, 1535 .validate_struct_init_result_ty => { 1536 try sema.zirValidateStructInitTy(block, inst, true); 1537 i += 1; 1538 continue; 1539 }, 1540 .validate_array_init_ty => { 1541 try sema.zirValidateArrayInitTy(block, inst, false); 1542 i += 1; 1543 continue; 1544 }, 1545 .validate_array_init_result_ty => { 1546 try sema.zirValidateArrayInitTy(block, inst, true); 1547 i += 1; 1548 continue; 1549 }, 1550 .validate_ptr_struct_init => { 1551 try sema.zirValidatePtrStructInit(block, inst); 1552 i += 1; 1553 continue; 1554 }, 1555 .validate_ptr_array_init => { 1556 try sema.zirValidatePtrArrayInit(block, inst); 1557 i += 1; 1558 continue; 1559 }, 1560 .validate_deref => { 1561 try sema.zirValidateDeref(block, inst); 1562 i += 1; 1563 continue; 1564 }, 1565 .validate_destructure => { 1566 try sema.zirValidateDestructure(block, inst); 1567 i += 1; 1568 continue; 1569 }, 1570 .validate_ref_ty => { 1571 try sema.zirValidateRefTy(block, inst); 1572 i += 1; 1573 continue; 1574 }, 1575 .validate_const => { 1576 try sema.zirValidateConst(block, inst); 1577 i += 1; 1578 continue; 1579 }, 1580 .@"export" => { 1581 try sema.zirExport(block, inst); 1582 i += 1; 1583 continue; 1584 }, 1585 .set_runtime_safety => { 1586 try sema.zirSetRuntimeSafety(block, inst); 1587 i += 1; 1588 continue; 1589 }, 1590 .param => { 1591 try sema.zirParam(block, inst, false); 1592 i += 1; 1593 continue; 1594 }, 1595 .param_comptime => { 1596 try sema.zirParam(block, inst, true); 1597 i += 1; 1598 continue; 1599 }, 1600 .param_anytype => { 1601 try sema.zirParamAnytype(block, inst, false); 1602 i += 1; 1603 continue; 1604 }, 1605 .param_anytype_comptime => { 1606 try sema.zirParamAnytype(block, inst, true); 1607 i += 1; 1608 continue; 1609 }, 1610 .memcpy => { 1611 try sema.zirMemcpy(block, inst, .memcpy, true); 1612 i += 1; 1613 continue; 1614 }, 1615 .memmove => { 1616 try sema.zirMemcpy(block, inst, .memmove, false); 1617 i += 1; 1618 continue; 1619 }, 1620 .memset => { 1621 try sema.zirMemset(block, inst); 1622 i += 1; 1623 continue; 1624 }, 1625 .check_comptime_control_flow => { 1626 if (!block.isComptime()) { 1627 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 1628 const src = block.nodeOffset(inst_data.src_node); 1629 const inline_block = inst_data.operand.toIndex().?; 1630 1631 var check_block = block; 1632 const target_runtime_index = while (true) { 1633 if (check_block.inline_block == inline_block.toOptional()) { 1634 break check_block.runtime_index; 1635 } 1636 check_block = check_block.parent.?; 1637 }; 1638 1639 if (@intFromEnum(target_runtime_index) < @intFromEnum(block.runtime_index)) { 1640 const runtime_src = block.runtime_cond orelse block.runtime_loop.?; 1641 const msg = msg: { 1642 const msg = try sema.errMsg(src, "comptime control flow inside runtime block", .{}); 1643 errdefer msg.destroy(sema.gpa); 1644 try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); 1645 break :msg msg; 1646 }; 1647 return sema.failWithOwnedErrorMsg(block, msg); 1648 } 1649 } 1650 i += 1; 1651 continue; 1652 }, 1653 .save_err_ret_index => { 1654 try sema.zirSaveErrRetIndex(block, inst); 1655 i += 1; 1656 continue; 1657 }, 1658 .restore_err_ret_index_unconditional => { 1659 const un_node = datas[@intFromEnum(inst)].un_node; 1660 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), un_node.operand, .none); 1661 i += 1; 1662 continue; 1663 }, 1664 .restore_err_ret_index_fn_entry => { 1665 const un_node = datas[@intFromEnum(inst)].un_node; 1666 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), .none, un_node.operand); 1667 i += 1; 1668 continue; 1669 }, 1670 1671 // Special case instructions to handle comptime control flow. 1672 .@"break" => { 1673 if (block.isComptime()) { 1674 sema.comptime_break_inst = inst; 1675 return error.ComptimeBreak; 1676 } else { 1677 try sema.zirBreak(block, inst); 1678 break; 1679 } 1680 }, 1681 .break_inline => { 1682 sema.comptime_break_inst = inst; 1683 return error.ComptimeBreak; 1684 }, 1685 .repeat => { 1686 if (block.isComptime()) { 1687 // Send comptime control flow back to the beginning of this block. 1688 const src = block.nodeOffset(datas[@intFromEnum(inst)].node); 1689 try sema.emitBackwardBranch(block, src); 1690 i = 0; 1691 continue; 1692 } else { 1693 // We are definitely called by `zirLoop`, which will treat the 1694 // fact that this body does not terminate `noreturn` as an 1695 // implicit repeat. 1696 // TODO: since AIR has `repeat` now, we could change ZIR to generate 1697 // more optimal code utilizing `repeat` instructions across blocks! 1698 break; 1699 } 1700 }, 1701 .repeat_inline => { 1702 // Send comptime control flow back to the beginning of this block. 1703 const src = block.nodeOffset(datas[@intFromEnum(inst)].node); 1704 try sema.emitBackwardBranch(block, src); 1705 i = 0; 1706 continue; 1707 }, 1708 .switch_continue => if (block.isComptime()) { 1709 sema.comptime_break_inst = inst; 1710 return error.ComptimeBreak; 1711 } else { 1712 try sema.zirSwitchContinue(block, inst); 1713 break; 1714 }, 1715 1716 .loop => if (block.isComptime()) { 1717 continue :inst .block_inline; 1718 } else try sema.zirLoop(block, inst), 1719 1720 .block => if (block.isComptime()) { 1721 continue :inst .block_inline; 1722 } else try sema.zirBlock(block, inst), 1723 1724 .block_comptime => { 1725 const pl_node = datas[@intFromEnum(inst)].pl_node; 1726 const src = block.nodeOffset(pl_node.src_node); 1727 const extra = sema.code.extraData(Zir.Inst.BlockComptime, pl_node.payload_index); 1728 const block_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1729 1730 var child_block = block.makeSubBlock(); 1731 defer child_block.instructions.deinit(sema.gpa); 1732 1733 // We won't have any merges, but we must ensure this block is properly labeled for 1734 // any `.restore_err_ret_index_*` instructions. 1735 var label: Block.Label = .{ 1736 .zir_block = inst, 1737 .merges = undefined, 1738 }; 1739 child_block.label = &label; 1740 1741 child_block.comptime_reason = .{ .reason = .{ 1742 .src = src, 1743 .r = .{ .simple = extra.data.reason }, 1744 } }; 1745 1746 const result = try sema.resolveInlineBody(&child_block, block_body, inst); 1747 1748 // Only check for the result being comptime-known in the outermost `block_comptime`. 1749 // That way, AstGen can safely elide redundant `block_comptime` without affecting semantics. 1750 if (!block.isComptime() and !try sema.isComptimeKnown(result)) { 1751 return sema.failWithNeededComptime(&child_block, src, null); 1752 } 1753 1754 break :inst result; 1755 }, 1756 1757 .block_inline => blk: { 1758 // Directly analyze the block body without introducing a new block. 1759 // However, in the case of a corresponding break_inline which reaches 1760 // through a runtime conditional branch, we must retroactively emit 1761 // a block, so we remember the block index here just in case. 1762 const block_index = block.instructions.items.len; 1763 const inst_data = datas[@intFromEnum(inst)].pl_node; 1764 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 1765 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1766 const gpa = sema.gpa; 1767 1768 const BreakResult = struct { 1769 block_inst: Zir.Inst.Index, 1770 operand: Zir.Inst.Ref, 1771 }; 1772 1773 const opt_break_data: ?BreakResult, const need_debug_scope = b: { 1774 // Create a temporary child block so that this inline block is properly 1775 // labeled for any .restore_err_ret_index instructions 1776 var child_block = block.makeSubBlock(); 1777 var need_debug_scope = false; 1778 child_block.need_debug_scope = &need_debug_scope; 1779 1780 // If this block contains a function prototype, we need to reset the 1781 // current list of parameters and restore it later. 1782 // Note: this probably needs to be resolved in a more general manner. 1783 const tag_index = @intFromEnum(inline_body[inline_body.len - 1]); 1784 child_block.inline_block = (if (tags[tag_index] == .repeat_inline) 1785 inline_body[0] 1786 else 1787 inst).toOptional(); 1788 1789 var label: Block.Label = .{ 1790 .zir_block = inst, 1791 .merges = undefined, 1792 }; 1793 child_block.label = &label; 1794 1795 // Write these instructions directly into the parent block 1796 child_block.instructions = block.instructions; 1797 defer block.instructions = child_block.instructions; 1798 1799 const break_result: ?BreakResult = if (sema.analyzeBodyInner(&child_block, inline_body)) |_| r: { 1800 break :r null; 1801 } else |err| switch (err) { 1802 error.ComptimeBreak => brk_res: { 1803 const break_inst = sema.comptime_break_inst; 1804 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; 1805 const break_extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 1806 break :brk_res .{ 1807 .block_inst = break_extra.block_inst, 1808 .operand = break_data.operand, 1809 }; 1810 }, 1811 else => |e| return e, 1812 }; 1813 1814 if (need_debug_scope) { 1815 _ = try sema.ensurePostHoc(block, inst); 1816 } 1817 1818 break :b .{ break_result, need_debug_scope }; 1819 }; 1820 1821 // A runtime conditional branch that needs a post-hoc block to be 1822 // emitted communicates this by mapping the block index into the inst map. 1823 if (map.get(inst)) |new_block_ref| ph: { 1824 // Comptime control flow populates the map, so we don't actually know 1825 // if this is a post-hoc runtime block until we check the 1826 // post_hoc_block map. 1827 const new_block_inst = new_block_ref.toIndex() orelse break :ph; 1828 const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse 1829 break :ph; 1830 1831 // In this case we need to move all the instructions starting at 1832 // block_index from the current block into this new one. 1833 1834 if (opt_break_data) |break_data| { 1835 // This is a comptime break which we now change to a runtime break 1836 // since it crosses a runtime branch. 1837 // It may pass through our currently being analyzed block_inline or it 1838 // may point directly to it. In the latter case, this modifies the 1839 // block that we looked up in the post_hoc_blocks map above. 1840 try sema.addRuntimeBreak(block, break_data.block_inst, break_data.operand); 1841 } 1842 1843 try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]); 1844 block.instructions.items.len = block_index; 1845 1846 const block_result = try sema.resolveAnalyzedBlock(block, block.nodeOffset(inst_data.src_node), &labeled_block.block, &labeled_block.label.merges, need_debug_scope); 1847 { 1848 // Destroy the ad-hoc block entry so that it does not interfere with 1849 // the next iteration of comptime control flow, if any. 1850 labeled_block.destroy(gpa); 1851 assert(sema.post_hoc_blocks.remove(new_block_inst)); 1852 } 1853 1854 break :blk block_result; 1855 } 1856 1857 const break_data = opt_break_data orelse break; 1858 if (inst == break_data.block_inst) { 1859 break :blk try sema.resolveInst(break_data.operand); 1860 } else { 1861 // `comptime_break_inst` preserved from `analyzeBodyInner` above. 1862 return error.ComptimeBreak; 1863 } 1864 }, 1865 .condbr => if (block.isComptime()) { 1866 continue :inst .condbr_inline; 1867 } else { 1868 try sema.zirCondbr(block, inst); 1869 break; 1870 }, 1871 .condbr_inline => { 1872 const inst_data = datas[@intFromEnum(inst)].pl_node; 1873 const cond_src = block.src(.{ .node_offset_if_cond = inst_data.src_node }); 1874 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 1875 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); 1876 const else_body = sema.code.bodySlice( 1877 extra.end + then_body.len, 1878 extra.data.else_body_len, 1879 ); 1880 const uncasted_cond = try sema.resolveInst(extra.data.condition); 1881 const cond = try sema.coerce(block, .bool, uncasted_cond, cond_src); 1882 const cond_val = try sema.resolveConstDefinedValue( 1883 block, 1884 cond_src, 1885 cond, 1886 // If this block is comptime, it's more helpful to just give the outer message. 1887 // This is particularly true if this came from a comptime `condbr` above. 1888 if (block.isComptime()) null else .{ .simple = .inline_loop_operand }, 1889 ); 1890 const inline_body = if (cond_val.toBool()) then_body else else_body; 1891 1892 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src); 1893 const old_runtime_index = block.runtime_index; 1894 defer block.runtime_index = old_runtime_index; 1895 1896 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; 1897 break :inst result; 1898 }, 1899 .@"try" => blk: { 1900 if (!block.isComptime()) break :blk try sema.zirTry(block, inst); 1901 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 1902 const src = block.nodeOffset(inst_data.src_node); 1903 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node }); 1904 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 1905 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1906 const err_union = try sema.resolveInst(extra.data.operand); 1907 const err_union_ty = sema.typeOf(err_union); 1908 if (err_union_ty.zigTypeTag(zcu) != .error_union) { 1909 return sema.failWithOwnedErrorMsg(block, msg: { 1910 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); 1911 errdefer msg.destroy(sema.gpa); 1912 try sema.addDeclaredHereNote(msg, err_union_ty); 1913 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); 1914 break :msg msg; 1915 }); 1916 } 1917 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); 1918 assert(is_non_err != .none); 1919 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null); 1920 if (is_non_err_val.toBool()) { 1921 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); 1922 } 1923 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; 1924 break :blk result; 1925 }, 1926 .try_ptr => blk: { 1927 if (!block.isComptime()) break :blk try sema.zirTryPtr(block, inst); 1928 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 1929 const src = block.nodeOffset(inst_data.src_node); 1930 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node }); 1931 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 1932 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1933 const operand = try sema.resolveInst(extra.data.operand); 1934 const err_union = try sema.analyzeLoad(block, src, operand, operand_src); 1935 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); 1936 assert(is_non_err != .none); 1937 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null); 1938 if (is_non_err_val.toBool()) { 1939 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); 1940 } 1941 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; 1942 break :blk result; 1943 }, 1944 .@"defer" => blk: { 1945 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"defer"; 1946 const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len); 1947 if (sema.analyzeBodyInner(block, defer_body)) |_| { 1948 // The defer terminated noreturn - no more analysis needed. 1949 break; 1950 } else |err| switch (err) { 1951 error.ComptimeBreak => {}, 1952 else => |e| return e, 1953 } 1954 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) { 1955 return error.ComptimeBreak; 1956 } 1957 break :blk .void_value; 1958 }, 1959 .defer_err_code => blk: { 1960 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code; 1961 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; 1962 const defer_body = sema.code.bodySlice(extra.index, extra.len); 1963 const err_code = try sema.resolveInst(inst_data.err_code); 1964 try map.ensureSpaceForInstructions(sema.gpa, defer_body); 1965 map.putAssumeCapacity(extra.remapped_err_code, err_code); 1966 if (sema.analyzeBodyInner(block, defer_body)) |_| { 1967 // The defer terminated noreturn - no more analysis needed. 1968 break; 1969 } else |err| switch (err) { 1970 error.ComptimeBreak => {}, 1971 else => |e| return e, 1972 } 1973 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) { 1974 return error.ComptimeBreak; 1975 } 1976 break :blk .void_value; 1977 }, 1978 }; 1979 if (sema.isNoReturn(air_inst)) { 1980 // We're going to assume that the body itself is noreturn, so let's ensure that now 1981 assert(block.instructions.items.len > 0); 1982 assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef())); 1983 break; 1984 } 1985 map.putAssumeCapacity(inst, air_inst); 1986 i += 1; 1987 } 1988 } 1989 1990 pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { 1991 if (zir_ref == .none) { 1992 return .none; 1993 } else { 1994 return resolveInst(sema, zir_ref); 1995 } 1996 } 1997 1998 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { 1999 assert(zir_ref != .none); 2000 if (zir_ref.toIndex()) |i| { 2001 return sema.inst_map.get(i).?; 2002 } 2003 // First section of indexes correspond to a set number of constant values. 2004 // We intentionally map the same indexes to the same values between ZIR and AIR. 2005 return @enumFromInt(@intFromEnum(zir_ref)); 2006 } 2007 2008 fn resolveConstBool( 2009 sema: *Sema, 2010 block: *Block, 2011 src: LazySrcLoc, 2012 zir_ref: Zir.Inst.Ref, 2013 reason: ComptimeReason, 2014 ) !bool { 2015 const air_inst = try sema.resolveInst(zir_ref); 2016 const wanted_type: Type = .bool; 2017 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 2018 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 2019 return val.toBool(); 2020 } 2021 2022 fn resolveConstString( 2023 sema: *Sema, 2024 block: *Block, 2025 src: LazySrcLoc, 2026 zir_ref: Zir.Inst.Ref, 2027 reason: ComptimeReason, 2028 ) ![]u8 { 2029 const air_inst = try sema.resolveInst(zir_ref); 2030 return sema.toConstString(block, src, air_inst, reason); 2031 } 2032 2033 pub fn toConstString( 2034 sema: *Sema, 2035 block: *Block, 2036 src: LazySrcLoc, 2037 air_inst: Air.Inst.Ref, 2038 reason: ComptimeReason, 2039 ) ![]u8 { 2040 const pt = sema.pt; 2041 const coerced_inst = try sema.coerce(block, .slice_const_u8, air_inst, src); 2042 const slice_val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 2043 const arr_val = try sema.derefSliceAsArray(block, src, slice_val, reason); 2044 return arr_val.toAllocatedBytes(arr_val.typeOf(pt.zcu), sema.arena, pt); 2045 } 2046 2047 pub fn resolveConstStringIntern( 2048 sema: *Sema, 2049 block: *Block, 2050 src: LazySrcLoc, 2051 zir_ref: Zir.Inst.Ref, 2052 reason: ComptimeReason, 2053 ) !InternPool.NullTerminatedString { 2054 const air_inst = try sema.resolveInst(zir_ref); 2055 const wanted_type: Type = .slice_const_u8; 2056 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 2057 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 2058 return sema.sliceToIpString(block, src, val, reason); 2059 } 2060 2061 fn resolveTypeOrPoison(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !?Type { 2062 const air_inst = try sema.resolveInst(zir_ref); 2063 const ty = try sema.analyzeAsType(block, src, air_inst); 2064 if (ty.isGenericPoison()) return null; 2065 return ty; 2066 } 2067 2068 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { 2069 return (try sema.resolveTypeOrPoison(block, src, zir_ref)).?; 2070 } 2071 2072 fn resolveDestType( 2073 sema: *Sema, 2074 block: *Block, 2075 src: LazySrcLoc, 2076 zir_ref: Zir.Inst.Ref, 2077 strat: enum { remove_eu_opt, remove_eu, remove_opt }, 2078 builtin_name: []const u8, 2079 ) !Type { 2080 const pt = sema.pt; 2081 const zcu = pt.zcu; 2082 const remove_eu = switch (strat) { 2083 .remove_eu_opt, .remove_eu => true, 2084 .remove_opt => false, 2085 }; 2086 const remove_opt = switch (strat) { 2087 .remove_eu_opt, .remove_opt => true, 2088 .remove_eu => false, 2089 }; 2090 2091 const raw_ty = try sema.resolveTypeOrPoison(block, src, zir_ref) orelse { 2092 // Cast builtins use their result type as the destination type, but 2093 // it could be an anytype argument, which we can't catch in AstGen. 2094 const msg = msg: { 2095 const msg = try sema.errMsg(src, "{s} must have a known result type", .{builtin_name}); 2096 errdefer msg.destroy(sema.gpa); 2097 switch (sema.genericPoisonReason(block, zir_ref)) { 2098 .anytype_param => |call_src| try sema.errNote(call_src, msg, "result type is unknown due to anytype parameter", .{}), 2099 .anyopaque_ptr => |ptr_src| try sema.errNote(ptr_src, msg, "result type is unknown due to opaque pointer type", .{}), 2100 .unknown => {}, 2101 } 2102 try sema.errNote(src, msg, "use @as to provide explicit result type", .{}); 2103 break :msg msg; 2104 }; 2105 return sema.failWithOwnedErrorMsg(block, msg); 2106 }; 2107 2108 if (remove_eu and raw_ty.zigTypeTag(zcu) == .error_union) { 2109 const eu_child = raw_ty.errorUnionPayload(zcu); 2110 if (remove_opt and eu_child.zigTypeTag(zcu) == .optional) { 2111 return eu_child.childType(zcu); 2112 } 2113 return eu_child; 2114 } 2115 if (remove_opt and raw_ty.zigTypeTag(zcu) == .optional) { 2116 return raw_ty.childType(zcu); 2117 } 2118 return raw_ty; 2119 } 2120 2121 const GenericPoisonReason = union(enum) { 2122 anytype_param: LazySrcLoc, 2123 anyopaque_ptr: LazySrcLoc, 2124 unknown, 2125 }; 2126 2127 /// Backtracks through ZIR instructions to determine the reason a generic poison 2128 /// type was created. Used for error reporting. 2129 fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoisonReason { 2130 var cur = ref; 2131 while (true) { 2132 const inst = cur.toIndex() orelse return .unknown; 2133 switch (sema.code.instructions.items(.tag)[@intFromEnum(inst)]) { 2134 .validate_array_init_ref_ty => { 2135 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 2136 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; 2137 cur = extra.ptr_ty; 2138 }, 2139 .array_init_elem_type => { 2140 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 2141 cur = bin.lhs; 2142 }, 2143 .indexable_ptr_elem_type, .splat_op_result_ty => { 2144 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 2145 cur = un_node.operand; 2146 }, 2147 .struct_init_field_type => { 2148 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 2149 const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data; 2150 cur = extra.container_type; 2151 }, 2152 .elem_type => { 2153 // There are two cases here: the pointer type may already have been 2154 // generic poison, or it may have been an anyopaque pointer. 2155 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 2156 const operand_ref = try sema.resolveInst(un_node.operand); 2157 const operand_val = operand_ref.toInterned() orelse return .unknown; 2158 if (operand_val == .generic_poison_type) { 2159 // The pointer was generic poison - keep looking. 2160 cur = un_node.operand; 2161 } else { 2162 // This must be an anyopaque pointer! 2163 return .{ .anyopaque_ptr = block.nodeOffset(un_node.src_node) }; 2164 } 2165 }, 2166 .call, .field_call => { 2167 // A function call can never return generic poison, so we must be 2168 // evaluating an `anytype` function parameter. 2169 // TODO: better source location - function decl rather than call 2170 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 2171 return .{ .anytype_param = block.nodeOffset(pl_node.src_node) }; 2172 }, 2173 else => return .unknown, 2174 } 2175 } 2176 } 2177 2178 fn analyzeAsType( 2179 sema: *Sema, 2180 block: *Block, 2181 src: LazySrcLoc, 2182 air_inst: Air.Inst.Ref, 2183 ) !Type { 2184 const wanted_type: Type = .type; 2185 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 2186 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ .simple = .type }); 2187 return val.toType(); 2188 } 2189 2190 pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { 2191 const pt = sema.pt; 2192 const zcu = pt.zcu; 2193 const comp = zcu.comp; 2194 const gpa = sema.gpa; 2195 const ip = &zcu.intern_pool; 2196 if (!comp.config.any_error_tracing) return; 2197 2198 assert(!block.isComptime()); 2199 var err_trace_block = block.makeSubBlock(); 2200 defer err_trace_block.instructions.deinit(gpa); 2201 2202 const src: LazySrcLoc = LazySrcLoc.unneeded; 2203 2204 // var addrs: [err_return_trace_addr_count]usize = undefined; 2205 const err_return_trace_addr_count = 32; 2206 const addr_arr_ty = try pt.arrayType(.{ 2207 .len = err_return_trace_addr_count, 2208 .child = .usize_type, 2209 }); 2210 const addrs_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(addr_arr_ty)); 2211 2212 // var st: StackTrace = undefined; 2213 const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); 2214 try stack_trace_ty.resolveFields(pt); 2215 const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty)); 2216 2217 // st.instruction_addresses = &addrs; 2218 const instruction_addresses_field_name = try ip.getOrPutString(gpa, pt.tid, "instruction_addresses", .no_embedded_nulls); 2219 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true); 2220 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); 2221 2222 // st.index = 0; 2223 const index_field_name = try ip.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); 2224 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true); 2225 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store); 2226 2227 // @errorReturnTrace() = &st; 2228 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr); 2229 2230 try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items); 2231 } 2232 2233 /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value. 2234 fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2235 const zcu = sema.pt.zcu; 2236 assert(inst != .none); 2237 2238 if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { 2239 return opv; 2240 } 2241 2242 if (inst.toInterned()) |ip_index| { 2243 const val: Value = .fromInterned(ip_index); 2244 assert(val.getVariable(zcu) == null); 2245 return val; 2246 } else { 2247 // Runtime-known value. 2248 const air_tags = sema.air_instructions.items(.tag); 2249 switch (air_tags[@intFromEnum(inst.toIndex().?)]) { 2250 .inferred_alloc => unreachable, // assertion failure 2251 .inferred_alloc_comptime => unreachable, // assertion failure 2252 else => {}, 2253 } 2254 return null; 2255 } 2256 } 2257 2258 /// Like `resolveValue`, but emits an error if the value is not comptime-known. 2259 fn resolveConstValue( 2260 sema: *Sema, 2261 block: *Block, 2262 src: LazySrcLoc, 2263 inst: Air.Inst.Ref, 2264 reason: ?ComptimeReason, 2265 ) CompileError!Value { 2266 return try sema.resolveValue(inst) orelse { 2267 return sema.failWithNeededComptime(block, src, reason); 2268 }; 2269 } 2270 2271 /// Like `resolveValue`, but emits an error if the value is comptime-known to be undefined. 2272 fn resolveDefinedValue( 2273 sema: *Sema, 2274 block: *Block, 2275 src: LazySrcLoc, 2276 air_ref: Air.Inst.Ref, 2277 ) CompileError!?Value { 2278 const pt = sema.pt; 2279 const zcu = pt.zcu; 2280 const val = try sema.resolveValue(air_ref) orelse return null; 2281 if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null); 2282 return val; 2283 } 2284 2285 /// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined. 2286 fn resolveConstDefinedValue( 2287 sema: *Sema, 2288 block: *Block, 2289 src: LazySrcLoc, 2290 air_ref: Air.Inst.Ref, 2291 reason: ?ComptimeReason, 2292 ) CompileError!Value { 2293 const val = try sema.resolveConstValue(block, src, air_ref, reason); 2294 if (val.isUndef(sema.pt.zcu)) return sema.failWithUseOfUndef(block, src, null); 2295 return val; 2296 } 2297 2298 /// Like `resolveValue`, but recursively resolves lazy values before returning. 2299 fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2300 return try sema.resolveLazyValue((try sema.resolveValue(inst)) orelse return null); 2301 } 2302 2303 /// Value Tag may be `undef` or `variable`. 2304 pub fn resolveFinalDeclValue( 2305 sema: *Sema, 2306 block: *Block, 2307 src: LazySrcLoc, 2308 air_ref: Air.Inst.Ref, 2309 ) CompileError!Value { 2310 const zcu = sema.pt.zcu; 2311 const val = try sema.resolveConstValue(block, src, air_ref, .{ .simple = .container_var_init }); 2312 if (val.canMutateComptimeVarState(zcu)) { 2313 const ip = &zcu.intern_pool; 2314 const nav = ip.getNav(sema.owner.unwrap().nav_val); 2315 return sema.failWithContainsReferenceToComptimeVar(block, src, nav.name, "global variable", val); 2316 } 2317 return val; 2318 } 2319 2320 fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: ?ComptimeReason) CompileError { 2321 const msg, const fail_block = msg: { 2322 const msg = try sema.errMsg(src, "unable to resolve comptime value", .{}); 2323 errdefer msg.destroy(sema.gpa); 2324 const fail_block = if (reason) |r| b: { 2325 try r.explain(sema, src, msg); 2326 break :b block; 2327 } else b: { 2328 break :b try block.explainWhyBlockIsComptime(msg); 2329 }; 2330 break :msg .{ msg, fail_block }; 2331 }; 2332 return sema.failWithOwnedErrorMsg(fail_block, msg); 2333 } 2334 2335 pub fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc, vector_index: ?usize) CompileError { 2336 return sema.failWithOwnedErrorMsg(block, msg: { 2337 const msg = try sema.errMsg(src, "use of undefined value here causes illegal behavior", .{}); 2338 errdefer msg.destroy(sema.gpa); 2339 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); 2340 break :msg msg; 2341 }); 2342 } 2343 2344 pub fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2345 return sema.fail(block, src, "division by zero here causes illegal behavior", .{}); 2346 } 2347 2348 pub fn failWithTooLargeShiftAmount( 2349 sema: *Sema, 2350 block: *Block, 2351 operand_ty: Type, 2352 shift_amt: Value, 2353 shift_src: LazySrcLoc, 2354 vector_index: ?usize, 2355 ) CompileError { 2356 return sema.failWithOwnedErrorMsg(block, msg: { 2357 const msg = try sema.errMsg( 2358 shift_src, 2359 "shift amount '{f}' is too large for operand type '{f}'", 2360 .{ shift_amt.fmtValueSema(sema.pt, sema), operand_ty.fmt(sema.pt) }, 2361 ); 2362 errdefer msg.destroy(sema.gpa); 2363 if (vector_index) |i| try sema.errNote(shift_src, msg, "when computing vector element at index '{d}'", .{i}); 2364 break :msg msg; 2365 }); 2366 } 2367 2368 pub fn failWithNegativeShiftAmount(sema: *Sema, block: *Block, src: LazySrcLoc, shift_amt: Value, vector_index: ?usize) CompileError { 2369 return sema.failWithOwnedErrorMsg(block, msg: { 2370 const msg = try sema.errMsg(src, "shift by negative amount '{f}'", .{shift_amt.fmtValueSema(sema.pt, sema)}); 2371 errdefer msg.destroy(sema.gpa); 2372 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); 2373 break :msg msg; 2374 }); 2375 } 2376 2377 pub fn failWithUnsupportedComptimeShiftAmount(sema: *Sema, block: *Block, src: LazySrcLoc, vector_index: ?usize) CompileError { 2378 return sema.failWithOwnedErrorMsg(block, msg: { 2379 const msg = try sema.errMsg( 2380 src, 2381 "this implementation only supports comptime shift amounts of up to 2^{d} - 1 bits", 2382 .{@min(@bitSizeOf(usize), 64)}, 2383 ); 2384 errdefer msg.destroy(sema.gpa); 2385 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); 2386 break :msg msg; 2387 }); 2388 } 2389 2390 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { 2391 const pt = sema.pt; 2392 return sema.fail(block, src, "remainder division with '{f}' and '{f}': signed integers and floats must use @rem or @mod", .{ 2393 lhs_ty.fmt(pt), rhs_ty.fmt(pt), 2394 }); 2395 } 2396 2397 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError { 2398 const pt = sema.pt; 2399 const msg = msg: { 2400 const msg = try sema.errMsg(src, "expected optional type, found '{f}'", .{ 2401 non_optional_ty.fmt(pt), 2402 }); 2403 errdefer msg.destroy(sema.gpa); 2404 if (non_optional_ty.zigTypeTag(pt.zcu) == .error_union) { 2405 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 2406 } 2407 try addDeclaredHereNote(sema, msg, non_optional_ty); 2408 break :msg msg; 2409 }; 2410 return sema.failWithOwnedErrorMsg(block, msg); 2411 } 2412 2413 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { 2414 const pt = sema.pt; 2415 const msg = msg: { 2416 const msg = try sema.errMsg(src, "type '{f}' does not support array initialization syntax", .{ 2417 ty.fmt(pt), 2418 }); 2419 errdefer msg.destroy(sema.gpa); 2420 if (ty.isSlice(pt.zcu)) { 2421 try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.elemType2(pt.zcu).fmt(pt)}); 2422 } 2423 break :msg msg; 2424 }; 2425 return sema.failWithOwnedErrorMsg(block, msg); 2426 } 2427 2428 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { 2429 const pt = sema.pt; 2430 return sema.fail(block, src, "type '{f}' does not support struct initialization syntax", .{ 2431 ty.fmt(pt), 2432 }); 2433 } 2434 2435 fn failWithErrorSetCodeMissing( 2436 sema: *Sema, 2437 block: *Block, 2438 src: LazySrcLoc, 2439 dest_err_set_ty: Type, 2440 src_err_set_ty: Type, 2441 ) CompileError { 2442 const pt = sema.pt; 2443 return sema.fail(block, src, "expected type '{f}', found type '{f}'", .{ 2444 dest_err_set_ty.fmt(pt), src_err_set_ty.fmt(pt), 2445 }); 2446 } 2447 2448 pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError { 2449 const pt = sema.pt; 2450 return sema.failWithOwnedErrorMsg(block, msg: { 2451 const msg = try sema.errMsg(src, "overflow of integer type '{f}' with value '{f}'", .{ 2452 int_ty.fmt(pt), val.fmtValueSema(pt, sema), 2453 }); 2454 errdefer msg.destroy(sema.gpa); 2455 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); 2456 break :msg msg; 2457 }); 2458 } 2459 2460 fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError { 2461 const pt = sema.pt; 2462 const zcu = pt.zcu; 2463 const msg = msg: { 2464 const msg = try sema.errMsg(init_src, "value stored in comptime field does not match the default value of the field", .{}); 2465 errdefer msg.destroy(sema.gpa); 2466 2467 const struct_type = zcu.typeToStruct(container_ty) orelse break :msg msg; 2468 try sema.errNote(.{ 2469 .base_node_inst = struct_type.zir_index, 2470 .offset = .{ .container_field_value = @intCast(field_index) }, 2471 }, msg, "default value set here", .{}); 2472 break :msg msg; 2473 }; 2474 return sema.failWithOwnedErrorMsg(block, msg); 2475 } 2476 2477 fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2478 const msg = msg: { 2479 const msg = try sema.errMsg(src, "async has not been implemented in the self-hosted compiler yet", .{}); 2480 errdefer msg.destroy(sema.gpa); 2481 break :msg msg; 2482 }; 2483 return sema.failWithOwnedErrorMsg(block, msg); 2484 } 2485 2486 fn failWithInvalidFieldAccess( 2487 sema: *Sema, 2488 block: *Block, 2489 src: LazySrcLoc, 2490 object_ty: Type, 2491 field_name: InternPool.NullTerminatedString, 2492 ) CompileError { 2493 const pt = sema.pt; 2494 const zcu = pt.zcu; 2495 const inner_ty = if (object_ty.isSinglePointer(zcu)) object_ty.childType(zcu) else object_ty; 2496 2497 if (inner_ty.zigTypeTag(zcu) == .optional) opt: { 2498 const child_ty = inner_ty.optionalChild(zcu); 2499 if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :opt; 2500 const msg = msg: { 2501 const msg = try sema.errMsg(src, "optional type '{f}' does not support field access", .{object_ty.fmt(pt)}); 2502 errdefer msg.destroy(sema.gpa); 2503 try sema.errNote(src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 2504 break :msg msg; 2505 }; 2506 return sema.failWithOwnedErrorMsg(block, msg); 2507 } else if (inner_ty.zigTypeTag(zcu) == .error_union) err: { 2508 const child_ty = inner_ty.errorUnionPayload(zcu); 2509 if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :err; 2510 const msg = msg: { 2511 const msg = try sema.errMsg(src, "error union type '{f}' does not support field access", .{object_ty.fmt(pt)}); 2512 errdefer msg.destroy(sema.gpa); 2513 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 2514 break :msg msg; 2515 }; 2516 return sema.failWithOwnedErrorMsg(block, msg); 2517 } 2518 return sema.fail(block, src, "type '{f}' does not support field access", .{object_ty.fmt(pt)}); 2519 } 2520 2521 fn typeSupportsFieldAccess(zcu: *const Zcu, ty: Type, field_name: InternPool.NullTerminatedString) bool { 2522 const ip = &zcu.intern_pool; 2523 switch (ty.zigTypeTag(zcu)) { 2524 .array => return field_name.eqlSlice("len", ip), 2525 .pointer => { 2526 const ptr_info = ty.ptrInfo(zcu); 2527 if (ptr_info.flags.size == .slice) { 2528 return field_name.eqlSlice("ptr", ip) or field_name.eqlSlice("len", ip); 2529 } else if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { 2530 return field_name.eqlSlice("len", ip); 2531 } else return false; 2532 }, 2533 .type, .@"struct", .@"union" => return true, 2534 else => return false, 2535 } 2536 } 2537 2538 fn failWithComptimeErrorRetTrace( 2539 sema: *Sema, 2540 block: *Block, 2541 src: LazySrcLoc, 2542 name: InternPool.NullTerminatedString, 2543 ) CompileError { 2544 const pt = sema.pt; 2545 const zcu = pt.zcu; 2546 const msg = msg: { 2547 const msg = try sema.errMsg(src, "caught unexpected error '{f}'", .{name.fmt(&zcu.intern_pool)}); 2548 errdefer msg.destroy(sema.gpa); 2549 2550 for (sema.comptime_err_ret_trace.items) |src_loc| { 2551 try sema.errNote(src_loc, msg, "error returned here", .{}); 2552 } 2553 break :msg msg; 2554 }; 2555 return sema.failWithOwnedErrorMsg(block, msg); 2556 } 2557 2558 fn failWithInvalidPtrArithmetic(sema: *Sema, block: *Block, src: LazySrcLoc, arithmetic: []const u8, supports: []const u8) CompileError { 2559 const msg = msg: { 2560 const msg = try sema.errMsg(src, "invalid {s} arithmetic operator", .{arithmetic}); 2561 errdefer msg.destroy(sema.gpa); 2562 try sema.errNote(src, msg, "{s} arithmetic only supports {s}", .{ arithmetic, supports }); 2563 break :msg msg; 2564 }; 2565 return sema.failWithOwnedErrorMsg(block, msg); 2566 } 2567 2568 /// We don't return a pointer to the new error note because the pointer 2569 /// becomes invalid when you add another one. 2570 pub fn errNote( 2571 sema: *Sema, 2572 src: LazySrcLoc, 2573 parent: *Zcu.ErrorMsg, 2574 comptime format: []const u8, 2575 args: anytype, 2576 ) error{OutOfMemory}!void { 2577 return sema.pt.zcu.errNote(src, parent, format, args); 2578 } 2579 2580 fn addFieldErrNote( 2581 sema: *Sema, 2582 container_ty: Type, 2583 field_index: usize, 2584 parent: *Zcu.ErrorMsg, 2585 comptime format: []const u8, 2586 args: anytype, 2587 ) !void { 2588 @branchHint(.cold); 2589 const type_src = container_ty.srcLocOrNull(sema.pt.zcu) orelse return; 2590 const field_src: LazySrcLoc = .{ 2591 .base_node_inst = type_src.base_node_inst, 2592 .offset = .{ .container_field_name = @intCast(field_index) }, 2593 }; 2594 try sema.errNote(field_src, parent, format, args); 2595 } 2596 2597 pub fn errMsg( 2598 sema: *Sema, 2599 src: LazySrcLoc, 2600 comptime format: []const u8, 2601 args: anytype, 2602 ) Allocator.Error!*Zcu.ErrorMsg { 2603 assert(src.offset != .unneeded); 2604 return Zcu.ErrorMsg.create(sema.gpa, src, format, args); 2605 } 2606 2607 pub fn fail( 2608 sema: *Sema, 2609 block: *Block, 2610 src: LazySrcLoc, 2611 comptime format: []const u8, 2612 args: anytype, 2613 ) CompileError { 2614 const err_msg = try sema.errMsg(src, format, args); 2615 inline for (args) |arg| { 2616 if (@TypeOf(arg) == Type.Formatter) { 2617 try addDeclaredHereNote(sema, err_msg, arg.data.ty); 2618 } 2619 } 2620 return sema.failWithOwnedErrorMsg(block, err_msg); 2621 } 2622 2623 pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) error{ AnalysisFail, OutOfMemory } { 2624 @branchHint(.cold); 2625 const gpa = sema.gpa; 2626 const zcu = sema.pt.zcu; 2627 2628 if (build_options.enable_debug_extensions and zcu.comp.debug_compile_errors) { 2629 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 2630 wip_errors.init(gpa) catch @panic("out of memory"); 2631 Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory"); 2632 std.debug.print("compile error during Sema:\n", .{}); 2633 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory"); 2634 error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); 2635 crash_report.compilerPanic("unexpected compile error occurred", null); 2636 } 2637 2638 if (block) |start_block| { 2639 var block_it = start_block; 2640 while (block_it.inlining) |inlining| { 2641 const note_str = note: { 2642 if (inlining.is_generic_instantiation) break :note "generic function instantiated here"; 2643 if (inlining.call_block.isComptime()) break :note "called at comptime here"; 2644 break :note "called inline here"; 2645 }; 2646 try sema.errNote(inlining.call_src, err_msg, "{s}", .{note_str}); 2647 block_it = inlining.call_block; 2648 } 2649 } 2650 2651 err_msg.reference_trace_root = sema.owner.toOptional(); 2652 2653 const gop = try zcu.failed_analysis.getOrPut(gpa, sema.owner); 2654 if (gop.found_existing) { 2655 // If there are multiple errors for the same Decl, prefer the first one added. 2656 sema.err = null; 2657 err_msg.destroy(gpa); 2658 } else { 2659 sema.err = err_msg; 2660 gop.value_ptr.* = err_msg; 2661 } 2662 2663 return error.AnalysisFail; 2664 } 2665 2666 /// Given an ErrorMsg, modify its message and source location to the given values, turning the 2667 /// original message into a note. Notes on the original message are preserved as further notes. 2668 /// Reference trace is preserved. 2669 fn reparentOwnedErrorMsg( 2670 sema: *Sema, 2671 src: LazySrcLoc, 2672 msg: *Zcu.ErrorMsg, 2673 comptime format: []const u8, 2674 args: anytype, 2675 ) !void { 2676 const msg_str = try std.fmt.allocPrint(sema.gpa, format, args); 2677 2678 const orig_notes = msg.notes.len; 2679 msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1); 2680 @memmove(msg.notes[1..][0..orig_notes], msg.notes[0..orig_notes]); 2681 msg.notes[0] = .{ 2682 .src_loc = msg.src_loc, 2683 .msg = msg.msg, 2684 }; 2685 2686 msg.src_loc = src; 2687 msg.msg = msg_str; 2688 } 2689 2690 const align_ty: Type = .u29; 2691 2692 pub fn analyzeAsAlign( 2693 sema: *Sema, 2694 block: *Block, 2695 src: LazySrcLoc, 2696 air_ref: Air.Inst.Ref, 2697 ) !Alignment { 2698 const alignment_big = try sema.analyzeAsInt( 2699 block, 2700 src, 2701 air_ref, 2702 align_ty, 2703 .{ .simple = .@"align" }, 2704 ); 2705 return sema.validateAlign(block, src, alignment_big); 2706 } 2707 2708 fn validateAlign( 2709 sema: *Sema, 2710 block: *Block, 2711 src: LazySrcLoc, 2712 alignment: u64, 2713 ) !Alignment { 2714 if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); 2715 if (!std.math.isPowerOfTwo(alignment)) { 2716 return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{ 2717 alignment, 2718 }); 2719 } 2720 return Alignment.fromNonzeroByteUnits(alignment); 2721 } 2722 2723 fn resolveAlign( 2724 sema: *Sema, 2725 block: *Block, 2726 src: LazySrcLoc, 2727 zir_ref: Zir.Inst.Ref, 2728 ) !Alignment { 2729 const air_ref = try sema.resolveInst(zir_ref); 2730 return sema.analyzeAsAlign(block, src, air_ref); 2731 } 2732 2733 fn resolveInt( 2734 sema: *Sema, 2735 block: *Block, 2736 src: LazySrcLoc, 2737 zir_ref: Zir.Inst.Ref, 2738 dest_ty: Type, 2739 reason: ComptimeReason, 2740 ) !u64 { 2741 const air_ref = try sema.resolveInst(zir_ref); 2742 return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason); 2743 } 2744 2745 fn analyzeAsInt( 2746 sema: *Sema, 2747 block: *Block, 2748 src: LazySrcLoc, 2749 air_ref: Air.Inst.Ref, 2750 dest_ty: Type, 2751 reason: ComptimeReason, 2752 ) !u64 { 2753 const coerced = try sema.coerce(block, dest_ty, air_ref, src); 2754 const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); 2755 return try val.toUnsignedIntSema(sema.pt); 2756 } 2757 2758 fn analyzeValueAsCallconv( 2759 sema: *Sema, 2760 block: *Block, 2761 src: LazySrcLoc, 2762 unresolved_val: Value, 2763 ) !std.builtin.CallingConvention { 2764 return interpretBuiltinType(sema, block, src, unresolved_val, std.builtin.CallingConvention); 2765 } 2766 2767 fn interpretBuiltinType( 2768 sema: *Sema, 2769 block: *Block, 2770 src: LazySrcLoc, 2771 unresolved_val: Value, 2772 comptime T: type, 2773 ) !T { 2774 const resolved_val = try sema.resolveLazyValue(unresolved_val); 2775 return resolved_val.interpret(T, sema.pt) catch |err| switch (err) { 2776 error.OutOfMemory => |e| return e, 2777 error.UndefinedValue => return sema.failWithUseOfUndef(block, src, null), 2778 error.TypeMismatch => @panic("std.builtin is corrupt"), 2779 }; 2780 } 2781 2782 fn zirTupleDecl( 2783 sema: *Sema, 2784 block: *Block, 2785 extended: Zir.Inst.Extended.InstData, 2786 ) CompileError!Air.Inst.Ref { 2787 const gpa = sema.gpa; 2788 const pt = sema.pt; 2789 const zcu = pt.zcu; 2790 const fields_len = extended.small; 2791 const extra = sema.code.extraData(Zir.Inst.TupleDecl, extended.operand); 2792 var extra_index = extra.end; 2793 2794 const types = try sema.arena.alloc(InternPool.Index, fields_len); 2795 const inits = try sema.arena.alloc(InternPool.Index, fields_len); 2796 2797 const extra_as_refs: []const Zir.Inst.Ref = @ptrCast(sema.code.extra); 2798 2799 for (types, inits, 0..) |*field_ty, *field_init, field_index| { 2800 const zir_field_ty, const zir_field_init = extra_as_refs[extra_index..][0..2].*; 2801 extra_index += 2; 2802 2803 const type_src = block.src(.{ .tuple_field_type = .{ 2804 .tuple_decl_node_offset = extra.data.src_node, 2805 .elem_index = @intCast(field_index), 2806 } }); 2807 const init_src = block.src(.{ .tuple_field_init = .{ 2808 .tuple_decl_node_offset = extra.data.src_node, 2809 .elem_index = @intCast(field_index), 2810 } }); 2811 2812 const field_type = try sema.resolveType(block, type_src, zir_field_ty); 2813 try sema.validateTupleFieldType(block, field_type, type_src); 2814 2815 field_ty.* = field_type.toIntern(); 2816 field_init.* = init: { 2817 if (zir_field_init != .none) { 2818 const uncoerced_field_init = try sema.resolveInst(zir_field_init); 2819 const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src); 2820 const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value }); 2821 if (field_init_val.canMutateComptimeVarState(zcu)) { 2822 const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); 2823 return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val); 2824 } 2825 break :init field_init_val.toIntern(); 2826 } 2827 if (try sema.typeHasOnePossibleValue(field_type)) |opv| { 2828 break :init opv.toIntern(); 2829 } 2830 break :init .none; 2831 }; 2832 } 2833 2834 return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{ 2835 .types = types, 2836 .values = inits, 2837 })); 2838 } 2839 2840 fn validateTupleFieldType( 2841 sema: *Sema, 2842 block: *Block, 2843 field_ty: Type, 2844 field_ty_src: LazySrcLoc, 2845 ) CompileError!void { 2846 const gpa = sema.gpa; 2847 const zcu = sema.pt.zcu; 2848 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 2849 return sema.failWithOwnedErrorMsg(block, msg: { 2850 const msg = try sema.errMsg(field_ty_src, "opaque types have unknown size and therefore cannot be directly embedded in tuples", .{}); 2851 errdefer msg.destroy(gpa); 2852 2853 try sema.addDeclaredHereNote(msg, field_ty); 2854 break :msg msg; 2855 }); 2856 } 2857 if (field_ty.zigTypeTag(zcu) == .noreturn) { 2858 return sema.failWithOwnedErrorMsg(block, msg: { 2859 const msg = try sema.errMsg(field_ty_src, "tuple fields cannot be 'noreturn'", .{}); 2860 errdefer msg.destroy(gpa); 2861 2862 try sema.addDeclaredHereNote(msg, field_ty); 2863 break :msg msg; 2864 }); 2865 } 2866 } 2867 2868 /// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`, 2869 /// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`. 2870 fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue { 2871 const pt = sema.pt; 2872 const zcu = pt.zcu; 2873 const ip = &zcu.intern_pool; 2874 const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type); 2875 const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu); 2876 2877 const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len); 2878 2879 for (sema.code.extra[extra_index..][0..captures_len], sema.code.extra[extra_index + captures_len ..][0..captures_len], captures) |raw, raw_name, *capture| { 2880 const zir_capture: Zir.Inst.Capture = @bitCast(raw); 2881 const zir_name: Zir.NullTerminatedString = @enumFromInt(raw_name); 2882 const zir_name_slice = sema.code.nullTerminatedString(zir_name); 2883 capture.* = switch (zir_capture.unwrap()) { 2884 .nested => |parent_idx| parent_captures.get(ip)[parent_idx], 2885 .instruction_load => |ptr_inst| InternPool.CaptureValue.wrap(capture: { 2886 const ptr_ref = try sema.resolveInst(ptr_inst.toRef()); 2887 const ptr_val = try sema.resolveValue(ptr_ref) orelse { 2888 break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }; 2889 }; 2890 // TODO: better source location 2891 const unresolved_loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse { 2892 break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }; 2893 }; 2894 const loaded_val = try sema.resolveLazyValue(unresolved_loaded_val); 2895 if (loaded_val.canMutateComptimeVarState(zcu)) { 2896 const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls); 2897 return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val); 2898 } 2899 break :capture .{ .@"comptime" = loaded_val.toIntern() }; 2900 }), 2901 .instruction => |inst| InternPool.CaptureValue.wrap(capture: { 2902 const air_ref = try sema.resolveInst(inst.toRef()); 2903 if (try sema.resolveValueResolveLazy(air_ref)) |val| { 2904 if (val.canMutateComptimeVarState(zcu)) { 2905 const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls); 2906 return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val); 2907 } 2908 break :capture .{ .@"comptime" = val.toIntern() }; 2909 } 2910 break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() }; 2911 }), 2912 .decl_val => |str| capture: { 2913 const decl_name = try ip.getOrPutString( 2914 sema.gpa, 2915 pt.tid, 2916 sema.code.nullTerminatedString(str), 2917 .no_embedded_nulls, 2918 ); 2919 const nav = try sema.lookupIdentifier(block, decl_name); 2920 break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav }); 2921 }, 2922 .decl_ref => |str| capture: { 2923 const decl_name = try ip.getOrPutString( 2924 sema.gpa, 2925 pt.tid, 2926 sema.code.nullTerminatedString(str), 2927 .no_embedded_nulls, 2928 ); 2929 const nav = try sema.lookupIdentifier(block, decl_name); 2930 break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav }); 2931 }, 2932 }; 2933 } 2934 2935 return captures; 2936 } 2937 2938 fn zirStructDecl( 2939 sema: *Sema, 2940 block: *Block, 2941 extended: Zir.Inst.Extended.InstData, 2942 inst: Zir.Inst.Index, 2943 ) CompileError!Air.Inst.Ref { 2944 const pt = sema.pt; 2945 const zcu = pt.zcu; 2946 const gpa = sema.gpa; 2947 const ip = &zcu.intern_pool; 2948 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 2949 const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand); 2950 2951 const tracked_inst = try block.trackZir(inst); 2952 const src: LazySrcLoc = .{ 2953 .base_node_inst = tracked_inst, 2954 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 2955 }; 2956 2957 var extra_index = extra.end; 2958 2959 const captures_len = if (small.has_captures_len) blk: { 2960 const captures_len = sema.code.extra[extra_index]; 2961 extra_index += 1; 2962 break :blk captures_len; 2963 } else 0; 2964 const fields_len = if (small.has_fields_len) blk: { 2965 const fields_len = sema.code.extra[extra_index]; 2966 extra_index += 1; 2967 break :blk fields_len; 2968 } else 0; 2969 const decls_len = if (small.has_decls_len) blk: { 2970 const decls_len = sema.code.extra[extra_index]; 2971 extra_index += 1; 2972 break :blk decls_len; 2973 } else 0; 2974 2975 const captures = try sema.getCaptures(block, src, extra_index, captures_len); 2976 extra_index += captures_len * 2; 2977 2978 if (small.has_backing_int) { 2979 const backing_int_body_len = sema.code.extra[extra_index]; 2980 extra_index += 1; // backing_int_body_len 2981 if (backing_int_body_len == 0) { 2982 extra_index += 1; // backing_int_ref 2983 } else { 2984 extra_index += backing_int_body_len; // backing_int_body_inst 2985 } 2986 } 2987 2988 const struct_init: InternPool.StructTypeInit = .{ 2989 .layout = small.layout, 2990 .fields_len = fields_len, 2991 .known_non_opv = small.known_non_opv, 2992 .requires_comptime = if (small.known_comptime_only) .yes else .unknown, 2993 .any_comptime_fields = small.any_comptime_fields, 2994 .any_default_inits = small.any_default_inits, 2995 .inits_resolved = false, 2996 .any_aligned_fields = small.any_aligned_fields, 2997 .key = .{ .declared = .{ 2998 .zir_index = tracked_inst, 2999 .captures = captures, 3000 } }, 3001 }; 3002 const wip_ty = switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) { 3003 .existing => |ty| { 3004 const new_ty = try pt.ensureTypeUpToDate(ty); 3005 3006 // Make sure we update the namespace if the declaration is re-analyzed, to pick 3007 // up on e.g. changed comptime decls. 3008 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); 3009 3010 try sema.declareDependency(.{ .interned = new_ty }); 3011 try sema.addTypeReferenceEntry(src, new_ty); 3012 return Air.internedToRef(new_ty); 3013 }, 3014 .wip => |wip| wip, 3015 }; 3016 errdefer wip_ty.cancel(ip, pt.tid); 3017 3018 const type_name = try sema.createTypeName( 3019 block, 3020 small.name_strategy, 3021 "struct", 3022 inst, 3023 wip_ty.index, 3024 ); 3025 wip_ty.setName(ip, type_name.name, type_name.nav); 3026 3027 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ 3028 .parent = block.namespace.toOptional(), 3029 .owner_type = wip_ty.index, 3030 .file_scope = block.getFileScopeIndex(zcu), 3031 .generation = zcu.generation, 3032 }); 3033 errdefer pt.destroyNamespace(new_namespace_index); 3034 3035 if (pt.zcu.comp.incremental) { 3036 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); 3037 } 3038 3039 const decls = sema.code.bodySlice(extra_index, decls_len); 3040 try pt.scanNamespace(new_namespace_index, decls); 3041 3042 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 3043 codegen_type: { 3044 if (zcu.comp.config.use_llvm) break :codegen_type; 3045 if (block.ownerModule().strip) break :codegen_type; 3046 // This job depends on any resolve_type_fully jobs queued up before it. 3047 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 3048 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 3049 } 3050 try sema.declareDependency(.{ .interned = wip_ty.index }); 3051 try sema.addTypeReferenceEntry(src, wip_ty.index); 3052 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 3053 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 3054 } 3055 3056 pub fn createTypeName( 3057 sema: *Sema, 3058 block: *Block, 3059 name_strategy: Zir.Inst.NameStrategy, 3060 anon_prefix: []const u8, 3061 inst: ?Zir.Inst.Index, 3062 /// This is used purely to give the type a unique name in the `anon` case. 3063 type_index: InternPool.Index, 3064 ) CompileError!struct { 3065 name: InternPool.NullTerminatedString, 3066 nav: InternPool.Nav.Index.Optional, 3067 } { 3068 const pt = sema.pt; 3069 const zcu = pt.zcu; 3070 const gpa = zcu.gpa; 3071 const ip = &zcu.intern_pool; 3072 3073 switch (name_strategy) { 3074 .anon => {}, // handled after switch 3075 .parent => return .{ 3076 .name = block.type_name_ctx, 3077 .nav = sema.owner.unwrap().nav_val.toOptional(), 3078 }, 3079 .func => func_strat: { 3080 const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail); 3081 const zir_tags = sema.code.instructions.items(.tag); 3082 3083 var aw: std.Io.Writer.Allocating = .init(gpa); 3084 defer aw.deinit(); 3085 const w = &aw.writer; 3086 w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; 3087 3088 var arg_i: usize = 0; 3089 for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) { 3090 .param, .param_comptime, .param_anytype, .param_anytype_comptime => { 3091 const arg = sema.inst_map.get(zir_inst).?; 3092 // If this is being called in a generic function then analyzeCall will 3093 // have already resolved the args and this will work. 3094 // If not then this is a struct type being returned from a non-generic 3095 // function and the name doesn't matter since it will later 3096 // result in a compile error. 3097 const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat 3098 3099 if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory; 3100 3101 // Limiting the depth here helps avoid type names getting too long, which 3102 // in turn helps to avoid unreasonably long symbol names for namespaced 3103 // symbols. Such names should ideally be human-readable, and additionally, 3104 // some tooling may not support very long symbol names. 3105 w.print("{f}", .{Value.fmtValueSemaFull(.{ 3106 .val = arg_val, 3107 .pt = pt, 3108 .opt_sema = sema, 3109 .depth = 1, 3110 })}) catch return error.OutOfMemory; 3111 3112 arg_i += 1; 3113 continue; 3114 }, 3115 else => continue, 3116 }; 3117 3118 w.writeByte(')') catch return error.OutOfMemory; 3119 return .{ 3120 .name = try ip.getOrPutString(gpa, pt.tid, aw.written(), .no_embedded_nulls), 3121 .nav = .none, 3122 }; 3123 }, 3124 .dbg_var => { 3125 // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions. 3126 const ref = inst.?.toRef(); 3127 const zir_tags = sema.code.instructions.items(.tag); 3128 const zir_data = sema.code.instructions.items(.data); 3129 for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) { 3130 .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) { 3131 return .{ 3132 .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}.{s}", .{ 3133 block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code), 3134 }, .no_embedded_nulls), 3135 .nav = .none, 3136 }; 3137 }, 3138 else => {}, 3139 }; 3140 // fall through to anon strat 3141 }, 3142 } 3143 3144 // anon strat handling 3145 3146 // It would be neat to have "struct:line:column" but this name has 3147 // to survive incremental updates, where it may have been shifted down 3148 // or up to a different line, but unchanged, and thus not unnecessarily 3149 // semantically analyzed. 3150 // TODO: that would be possible, by detecting line number changes and renaming 3151 // types appropriately. However, `@typeName` becomes a problem then. If we remove 3152 // that builtin from the language, we can consider this. 3153 3154 return .{ 3155 .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}__{s}_{d}", .{ 3156 block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index), 3157 }, .no_embedded_nulls), 3158 .nav = .none, 3159 }; 3160 } 3161 3162 fn zirEnumDecl( 3163 sema: *Sema, 3164 block: *Block, 3165 extended: Zir.Inst.Extended.InstData, 3166 inst: Zir.Inst.Index, 3167 ) CompileError!Air.Inst.Ref { 3168 const tracy = trace(@src()); 3169 defer tracy.end(); 3170 3171 const pt = sema.pt; 3172 const zcu = pt.zcu; 3173 const gpa = sema.gpa; 3174 const ip = &zcu.intern_pool; 3175 const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); 3176 const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand); 3177 var extra_index: usize = extra.end; 3178 3179 const tracked_inst = try block.trackZir(inst); 3180 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; 3181 3182 const tag_type_ref = if (small.has_tag_type) blk: { 3183 const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3184 extra_index += 1; 3185 break :blk tag_type_ref; 3186 } else .none; 3187 3188 const captures_len = if (small.has_captures_len) blk: { 3189 const captures_len = sema.code.extra[extra_index]; 3190 extra_index += 1; 3191 break :blk captures_len; 3192 } else 0; 3193 3194 const body_len = if (small.has_body_len) blk: { 3195 const body_len = sema.code.extra[extra_index]; 3196 extra_index += 1; 3197 break :blk body_len; 3198 } else 0; 3199 3200 const fields_len = if (small.has_fields_len) blk: { 3201 const fields_len = sema.code.extra[extra_index]; 3202 extra_index += 1; 3203 break :blk fields_len; 3204 } else 0; 3205 3206 const decls_len = if (small.has_decls_len) blk: { 3207 const decls_len = sema.code.extra[extra_index]; 3208 extra_index += 1; 3209 break :blk decls_len; 3210 } else 0; 3211 3212 const captures = try sema.getCaptures(block, src, extra_index, captures_len); 3213 extra_index += captures_len * 2; 3214 3215 const decls = sema.code.bodySlice(extra_index, decls_len); 3216 extra_index += decls_len; 3217 3218 const body = sema.code.bodySlice(extra_index, body_len); 3219 extra_index += body.len; 3220 3221 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; 3222 const body_end = extra_index; 3223 extra_index += bit_bags_count; 3224 3225 const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { 3226 if (bag != 0) break true; 3227 } else false; 3228 3229 const enum_init: InternPool.EnumTypeInit = .{ 3230 .has_values = any_values, 3231 .tag_mode = if (small.nonexhaustive) 3232 .nonexhaustive 3233 else if (tag_type_ref == .none) 3234 .auto 3235 else 3236 .explicit, 3237 .fields_len = fields_len, 3238 .key = .{ .declared = .{ 3239 .zir_index = tracked_inst, 3240 .captures = captures, 3241 } }, 3242 }; 3243 const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) { 3244 .existing => |ty| { 3245 const new_ty = try pt.ensureTypeUpToDate(ty); 3246 3247 // Make sure we update the namespace if the declaration is re-analyzed, to pick 3248 // up on e.g. changed comptime decls. 3249 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); 3250 3251 try sema.declareDependency(.{ .interned = new_ty }); 3252 try sema.addTypeReferenceEntry(src, new_ty); 3253 3254 // Since this is an enum, it has to be resolved immediately. 3255 // `ensureTypeUpToDate` has resolved the new type if necessary. 3256 // We just need to check for resolution failures. 3257 const ty_unit: AnalUnit = .wrap(.{ .type = new_ty }); 3258 if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) { 3259 return error.AnalysisFail; 3260 } 3261 3262 return Air.internedToRef(new_ty); 3263 }, 3264 .wip => |wip| wip, 3265 }; 3266 3267 // Once this is `true`, we will not delete the decl or type even upon failure, since we 3268 // have finished constructing the type and are in the process of analyzing it. 3269 var done = false; 3270 3271 errdefer if (!done) wip_ty.cancel(ip, pt.tid); 3272 3273 const type_name = try sema.createTypeName( 3274 block, 3275 small.name_strategy, 3276 "enum", 3277 inst, 3278 wip_ty.index, 3279 ); 3280 wip_ty.setName(ip, type_name.name, type_name.nav); 3281 3282 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ 3283 .parent = block.namespace.toOptional(), 3284 .owner_type = wip_ty.index, 3285 .file_scope = block.getFileScopeIndex(zcu), 3286 .generation = zcu.generation, 3287 }); 3288 errdefer if (!done) pt.destroyNamespace(new_namespace_index); 3289 3290 try pt.scanNamespace(new_namespace_index, decls); 3291 3292 try sema.declareDependency(.{ .interned = wip_ty.index }); 3293 try sema.addTypeReferenceEntry(src, wip_ty.index); 3294 3295 // We've finished the initial construction of this type, and are about to perform analysis. 3296 // Set the namespace appropriately, and don't destroy anything on failure. 3297 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 3298 wip_ty.prepare(ip, new_namespace_index); 3299 done = true; 3300 3301 { 3302 const tracked_unit = zcu.trackUnitSema(type_name.name.toSlice(ip), null); 3303 defer tracked_unit.end(zcu); 3304 try Sema.resolveDeclaredEnum( 3305 pt, 3306 wip_ty, 3307 inst, 3308 tracked_inst, 3309 new_namespace_index, 3310 type_name.name, 3311 small, 3312 body, 3313 tag_type_ref, 3314 any_values, 3315 fields_len, 3316 sema.code, 3317 body_end, 3318 ); 3319 } 3320 3321 codegen_type: { 3322 if (zcu.comp.config.use_llvm) break :codegen_type; 3323 if (block.ownerModule().strip) break :codegen_type; 3324 // This job depends on any resolve_type_fully jobs queued up before it. 3325 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 3326 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 3327 } 3328 return Air.internedToRef(wip_ty.index); 3329 } 3330 3331 fn zirUnionDecl( 3332 sema: *Sema, 3333 block: *Block, 3334 extended: Zir.Inst.Extended.InstData, 3335 inst: Zir.Inst.Index, 3336 ) CompileError!Air.Inst.Ref { 3337 const tracy = trace(@src()); 3338 defer tracy.end(); 3339 3340 const pt = sema.pt; 3341 const zcu = pt.zcu; 3342 const gpa = sema.gpa; 3343 const ip = &zcu.intern_pool; 3344 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); 3345 const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand); 3346 var extra_index: usize = extra.end; 3347 3348 const tracked_inst = try block.trackZir(inst); 3349 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; 3350 3351 extra_index += @intFromBool(small.has_tag_type); 3352 const captures_len = if (small.has_captures_len) blk: { 3353 const captures_len = sema.code.extra[extra_index]; 3354 extra_index += 1; 3355 break :blk captures_len; 3356 } else 0; 3357 extra_index += @intFromBool(small.has_body_len); 3358 const fields_len = if (small.has_fields_len) blk: { 3359 const fields_len = sema.code.extra[extra_index]; 3360 extra_index += 1; 3361 break :blk fields_len; 3362 } else 0; 3363 3364 const decls_len = if (small.has_decls_len) blk: { 3365 const decls_len = sema.code.extra[extra_index]; 3366 extra_index += 1; 3367 break :blk decls_len; 3368 } else 0; 3369 3370 const captures = try sema.getCaptures(block, src, extra_index, captures_len); 3371 extra_index += captures_len * 2; 3372 3373 const union_init: InternPool.UnionTypeInit = .{ 3374 .flags = .{ 3375 .layout = small.layout, 3376 .status = .none, 3377 .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) 3378 .tagged 3379 else if (small.layout != .auto) 3380 .none 3381 else switch (block.wantSafeTypes()) { 3382 true => .safety, 3383 false => .none, 3384 }, 3385 .any_aligned_fields = small.any_aligned_fields, 3386 .requires_comptime = .unknown, 3387 .assumed_runtime_bits = false, 3388 .assumed_pointer_aligned = false, 3389 .alignment = .none, 3390 }, 3391 .fields_len = fields_len, 3392 .enum_tag_ty = .none, // set later 3393 .field_types = &.{}, // set later 3394 .field_aligns = &.{}, // set later 3395 .key = .{ .declared = .{ 3396 .zir_index = tracked_inst, 3397 .captures = captures, 3398 } }, 3399 }; 3400 const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) { 3401 .existing => |ty| { 3402 const new_ty = try pt.ensureTypeUpToDate(ty); 3403 3404 // Make sure we update the namespace if the declaration is re-analyzed, to pick 3405 // up on e.g. changed comptime decls. 3406 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); 3407 3408 try sema.declareDependency(.{ .interned = new_ty }); 3409 try sema.addTypeReferenceEntry(src, new_ty); 3410 return Air.internedToRef(new_ty); 3411 }, 3412 .wip => |wip| wip, 3413 }; 3414 errdefer wip_ty.cancel(ip, pt.tid); 3415 3416 const type_name = try sema.createTypeName( 3417 block, 3418 small.name_strategy, 3419 "union", 3420 inst, 3421 wip_ty.index, 3422 ); 3423 wip_ty.setName(ip, type_name.name, type_name.nav); 3424 3425 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ 3426 .parent = block.namespace.toOptional(), 3427 .owner_type = wip_ty.index, 3428 .file_scope = block.getFileScopeIndex(zcu), 3429 .generation = zcu.generation, 3430 }); 3431 errdefer pt.destroyNamespace(new_namespace_index); 3432 3433 if (pt.zcu.comp.incremental) { 3434 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); 3435 } 3436 3437 const decls = sema.code.bodySlice(extra_index, decls_len); 3438 try pt.scanNamespace(new_namespace_index, decls); 3439 3440 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 3441 codegen_type: { 3442 if (zcu.comp.config.use_llvm) break :codegen_type; 3443 if (block.ownerModule().strip) break :codegen_type; 3444 // This job depends on any resolve_type_fully jobs queued up before it. 3445 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 3446 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 3447 } 3448 try sema.declareDependency(.{ .interned = wip_ty.index }); 3449 try sema.addTypeReferenceEntry(src, wip_ty.index); 3450 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 3451 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 3452 } 3453 3454 fn zirOpaqueDecl( 3455 sema: *Sema, 3456 block: *Block, 3457 extended: Zir.Inst.Extended.InstData, 3458 inst: Zir.Inst.Index, 3459 ) CompileError!Air.Inst.Ref { 3460 const tracy = trace(@src()); 3461 defer tracy.end(); 3462 3463 const pt = sema.pt; 3464 const zcu = pt.zcu; 3465 const gpa = sema.gpa; 3466 const ip = &zcu.intern_pool; 3467 3468 const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small); 3469 const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand); 3470 var extra_index: usize = extra.end; 3471 3472 const tracked_inst = try block.trackZir(inst); 3473 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; 3474 3475 const captures_len = if (small.has_captures_len) blk: { 3476 const captures_len = sema.code.extra[extra_index]; 3477 extra_index += 1; 3478 break :blk captures_len; 3479 } else 0; 3480 3481 const decls_len = if (small.has_decls_len) blk: { 3482 const decls_len = sema.code.extra[extra_index]; 3483 extra_index += 1; 3484 break :blk decls_len; 3485 } else 0; 3486 3487 const captures = try sema.getCaptures(block, src, extra_index, captures_len); 3488 extra_index += captures_len * 2; 3489 3490 const opaque_init: InternPool.OpaqueTypeInit = .{ 3491 .key = .{ .declared = .{ 3492 .zir_index = tracked_inst, 3493 .captures = captures, 3494 } }, 3495 }; 3496 const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) { 3497 .existing => |ty| { 3498 // Make sure we update the namespace if the declaration is re-analyzed, to pick 3499 // up on e.g. changed comptime decls. 3500 try pt.ensureNamespaceUpToDate(Type.fromInterned(ty).getNamespaceIndex(zcu)); 3501 3502 try sema.declareDependency(.{ .interned = ty }); 3503 try sema.addTypeReferenceEntry(src, ty); 3504 return Air.internedToRef(ty); 3505 }, 3506 .wip => |wip| wip, 3507 }; 3508 errdefer wip_ty.cancel(ip, pt.tid); 3509 3510 const type_name = try sema.createTypeName( 3511 block, 3512 small.name_strategy, 3513 "opaque", 3514 inst, 3515 wip_ty.index, 3516 ); 3517 wip_ty.setName(ip, type_name.name, type_name.nav); 3518 3519 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ 3520 .parent = block.namespace.toOptional(), 3521 .owner_type = wip_ty.index, 3522 .file_scope = block.getFileScopeIndex(zcu), 3523 .generation = zcu.generation, 3524 }); 3525 errdefer pt.destroyNamespace(new_namespace_index); 3526 3527 const decls = sema.code.bodySlice(extra_index, decls_len); 3528 try pt.scanNamespace(new_namespace_index, decls); 3529 3530 codegen_type: { 3531 if (zcu.comp.config.use_llvm) break :codegen_type; 3532 if (block.ownerModule().strip) break :codegen_type; 3533 // This job depends on any resolve_type_fully jobs queued up before it. 3534 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 3535 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 3536 } 3537 try sema.addTypeReferenceEntry(src, wip_ty.index); 3538 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 3539 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 3540 } 3541 3542 fn zirErrorSetDecl( 3543 sema: *Sema, 3544 inst: Zir.Inst.Index, 3545 ) CompileError!Air.Inst.Ref { 3546 const tracy = trace(@src()); 3547 defer tracy.end(); 3548 3549 const pt = sema.pt; 3550 const zcu = pt.zcu; 3551 const gpa = sema.gpa; 3552 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 3553 const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); 3554 3555 var names: InferredErrorSet.NameMap = .{}; 3556 try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len); 3557 3558 var extra_index: u32 = @intCast(extra.end); 3559 const extra_index_end = extra_index + extra.data.fields_len; 3560 while (extra_index < extra_index_end) : (extra_index += 1) { 3561 const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]); 3562 const name = sema.code.nullTerminatedString(name_index); 3563 const name_ip = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); 3564 _ = try pt.getErrorValue(name_ip); 3565 const result = names.getOrPutAssumeCapacity(name_ip); 3566 assert(!result.found_existing); // verified in AstGen 3567 } 3568 3569 return Air.internedToRef((try pt.errorSetFromUnsortedNames(names.keys())).toIntern()); 3570 } 3571 3572 fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3573 const tracy = trace(@src()); 3574 defer tracy.end(); 3575 3576 const pt = sema.pt; 3577 3578 const src = block.nodeOffset(sema.code.instructions.items(.data)[@intFromEnum(inst)].node); 3579 3580 if (block.isComptime() or try sema.fn_ret_ty.comptimeOnlySema(pt)) { 3581 try sema.fn_ret_ty.resolveFields(pt); 3582 return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none); 3583 } 3584 3585 const target = pt.zcu.getTarget(); 3586 const ptr_type = try pt.ptrTypeSema(.{ 3587 .child = sema.fn_ret_ty.toIntern(), 3588 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 3589 }); 3590 3591 if (block.inlining != null) { 3592 // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr. 3593 // TODO when functions gain result location support, the inlining struct in 3594 // Block should contain the return pointer, and we would pass that through here. 3595 return block.addTy(.alloc, ptr_type); 3596 } 3597 3598 return block.addTy(.ret_ptr, ptr_type); 3599 } 3600 3601 fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3602 const tracy = trace(@src()); 3603 defer tracy.end(); 3604 3605 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 3606 const operand = try sema.resolveInst(inst_data.operand); 3607 return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand); 3608 } 3609 3610 fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3611 const tracy = trace(@src()); 3612 defer tracy.end(); 3613 3614 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3615 const operand = try sema.resolveInst(inst_data.operand); 3616 const src = block.nodeOffset(inst_data.src_node); 3617 3618 return sema.ensureResultUsed(block, sema.typeOf(operand), src); 3619 } 3620 3621 fn ensureResultUsed( 3622 sema: *Sema, 3623 block: *Block, 3624 ty: Type, 3625 src: LazySrcLoc, 3626 ) CompileError!void { 3627 const pt = sema.pt; 3628 const zcu = pt.zcu; 3629 switch (ty.zigTypeTag(zcu)) { 3630 .void, .noreturn => return, 3631 .error_set => return sema.fail(block, src, "error set is ignored", .{}), 3632 .error_union => { 3633 const msg = msg: { 3634 const msg = try sema.errMsg(src, "error union is ignored", .{}); 3635 errdefer msg.destroy(sema.gpa); 3636 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 3637 break :msg msg; 3638 }; 3639 return sema.failWithOwnedErrorMsg(block, msg); 3640 }, 3641 else => { 3642 const msg = msg: { 3643 const msg = try sema.errMsg(src, "value of type '{f}' ignored", .{ty.fmt(pt)}); 3644 errdefer msg.destroy(sema.gpa); 3645 try sema.errNote(src, msg, "all non-void values must be used", .{}); 3646 try sema.errNote(src, msg, "to discard the value, assign it to '_'", .{}); 3647 break :msg msg; 3648 }; 3649 return sema.failWithOwnedErrorMsg(block, msg); 3650 }, 3651 } 3652 } 3653 3654 fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3655 const tracy = trace(@src()); 3656 defer tracy.end(); 3657 3658 const pt = sema.pt; 3659 const zcu = pt.zcu; 3660 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3661 const operand = try sema.resolveInst(inst_data.operand); 3662 const src = block.nodeOffset(inst_data.src_node); 3663 const operand_ty = sema.typeOf(operand); 3664 switch (operand_ty.zigTypeTag(zcu)) { 3665 .error_set => return sema.fail(block, src, "error set is discarded", .{}), 3666 .error_union => { 3667 const msg = msg: { 3668 const msg = try sema.errMsg(src, "error union is discarded", .{}); 3669 errdefer msg.destroy(sema.gpa); 3670 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 3671 break :msg msg; 3672 }; 3673 return sema.failWithOwnedErrorMsg(block, msg); 3674 }, 3675 else => return, 3676 } 3677 } 3678 3679 fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3680 const tracy = trace(@src()); 3681 defer tracy.end(); 3682 3683 const pt = sema.pt; 3684 const zcu = pt.zcu; 3685 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3686 const src = block.nodeOffset(inst_data.src_node); 3687 const operand = try sema.resolveInst(inst_data.operand); 3688 const operand_ty = sema.typeOf(operand); 3689 const err_union_ty = if (operand_ty.zigTypeTag(zcu) == .pointer) 3690 operand_ty.childType(zcu) 3691 else 3692 operand_ty; 3693 if (err_union_ty.zigTypeTag(zcu) != .error_union) return; 3694 const payload_ty = err_union_ty.errorUnionPayload(zcu).zigTypeTag(zcu); 3695 if (payload_ty != .void and payload_ty != .noreturn) { 3696 const msg = msg: { 3697 const msg = try sema.errMsg(src, "error union payload is ignored", .{}); 3698 errdefer msg.destroy(sema.gpa); 3699 try sema.errNote(src, msg, "payload value can be explicitly ignored with '|_|'", .{}); 3700 break :msg msg; 3701 }; 3702 return sema.failWithOwnedErrorMsg(block, msg); 3703 } 3704 } 3705 3706 fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3707 const tracy = trace(@src()); 3708 defer tracy.end(); 3709 3710 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3711 const src = block.nodeOffset(inst_data.src_node); 3712 const object = try sema.resolveInst(inst_data.operand); 3713 3714 return indexablePtrLen(sema, block, src, object); 3715 } 3716 3717 fn indexablePtrLen( 3718 sema: *Sema, 3719 block: *Block, 3720 src: LazySrcLoc, 3721 object: Air.Inst.Ref, 3722 ) CompileError!Air.Inst.Ref { 3723 const pt = sema.pt; 3724 const zcu = pt.zcu; 3725 const object_ty = sema.typeOf(object); 3726 const is_pointer_to = object_ty.isSinglePointer(zcu); 3727 const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty; 3728 try sema.checkIndexable(block, src, indexable_ty); 3729 const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls); 3730 return sema.fieldVal(block, src, object, field_name, src); 3731 } 3732 3733 fn indexablePtrLenOrNone( 3734 sema: *Sema, 3735 block: *Block, 3736 src: LazySrcLoc, 3737 operand: Air.Inst.Ref, 3738 ) CompileError!Air.Inst.Ref { 3739 const pt = sema.pt; 3740 const zcu = pt.zcu; 3741 const operand_ty = sema.typeOf(operand); 3742 try checkMemOperand(sema, block, src, operand_ty); 3743 switch (operand_ty.ptrSize(zcu)) { 3744 .many, .c => return .none, 3745 .one, .slice => {}, 3746 } 3747 const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls); 3748 return sema.fieldVal(block, src, operand, field_name, src); 3749 } 3750 3751 fn zirAllocExtended( 3752 sema: *Sema, 3753 block: *Block, 3754 extended: Zir.Inst.Extended.InstData, 3755 ) CompileError!Air.Inst.Ref { 3756 const pt = sema.pt; 3757 const gpa = sema.gpa; 3758 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); 3759 const var_src = block.nodeOffset(extra.data.src_node); 3760 const ty_src = block.src(.{ .node_offset_var_decl_ty = extra.data.src_node }); 3761 const align_src = block.src(.{ .node_offset_var_decl_align = extra.data.src_node }); 3762 const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small); 3763 3764 var extra_index: usize = extra.end; 3765 3766 const var_ty: Type = if (small.has_type) blk: { 3767 const type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3768 extra_index += 1; 3769 break :blk try sema.resolveType(block, ty_src, type_ref); 3770 } else undefined; 3771 3772 const alignment = if (small.has_align) blk: { 3773 const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3774 extra_index += 1; 3775 break :blk try sema.resolveAlign(block, align_src, align_ref); 3776 } else .none; 3777 3778 if (block.isComptime() or small.is_comptime) { 3779 if (small.has_type) { 3780 return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment); 3781 } else { 3782 try sema.air_instructions.append(gpa, .{ 3783 .tag = .inferred_alloc_comptime, 3784 .data = .{ .inferred_alloc_comptime = .{ 3785 .alignment = alignment, 3786 .is_const = small.is_const, 3787 .ptr = undefined, 3788 } }, 3789 }); 3790 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 3791 } 3792 } 3793 3794 if (small.has_type and try var_ty.comptimeOnlySema(pt)) { 3795 return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment); 3796 } 3797 3798 if (small.has_type) { 3799 if (!small.is_const) { 3800 try sema.validateVarType(block, ty_src, var_ty, false); 3801 } 3802 const target = pt.zcu.getTarget(); 3803 try var_ty.resolveLayout(pt); 3804 if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { 3805 const store_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node }); 3806 return sema.fail(block, store_src, "local variable in naked function", .{}); 3807 } 3808 const ptr_type = try sema.pt.ptrTypeSema(.{ 3809 .child = var_ty.toIntern(), 3810 .flags = .{ 3811 .alignment = alignment, 3812 .address_space = target_util.defaultAddressSpace(target, .local), 3813 }, 3814 }); 3815 const ptr = try block.addTy(.alloc, ptr_type); 3816 if (small.is_const) { 3817 const ptr_inst = ptr.toIndex().?; 3818 try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); 3819 try sema.base_allocs.put(gpa, ptr_inst, ptr_inst); 3820 } 3821 return ptr; 3822 } 3823 3824 const result_index = try block.addInstAsIndex(.{ 3825 .tag = .inferred_alloc, 3826 .data = .{ .inferred_alloc = .{ 3827 .alignment = alignment, 3828 .is_const = small.is_const, 3829 } }, 3830 }); 3831 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); 3832 if (small.is_const) { 3833 try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); 3834 try sema.base_allocs.put(gpa, result_index, result_index); 3835 } 3836 return result_index.toRef(); 3837 } 3838 3839 fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3840 const tracy = trace(@src()); 3841 defer tracy.end(); 3842 3843 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3844 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); 3845 const var_src = block.nodeOffset(inst_data.src_node); 3846 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 3847 return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); 3848 } 3849 3850 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3851 const pt = sema.pt; 3852 const zcu = pt.zcu; 3853 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3854 const alloc = try sema.resolveInst(inst_data.operand); 3855 const alloc_ty = sema.typeOf(alloc); 3856 const ptr_info = alloc_ty.ptrInfo(zcu); 3857 const elem_ty: Type = .fromInterned(ptr_info.child); 3858 3859 // If the alloc was created in a comptime scope, we already created a comptime alloc for it. 3860 // However, if the final constructed value does not reference comptime-mutable memory, we wish 3861 // to promote it to an anon decl. 3862 already_ct: { 3863 const ptr_val = try sema.resolveValue(alloc) orelse break :already_ct; 3864 3865 // If this was a comptime inferred alloc, then `storeToInferredAllocComptime` 3866 // might have already done our job and created an anon decl ref. 3867 switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) { 3868 .ptr => |ptr| switch (ptr.base_addr) { 3869 .uav => { 3870 // The comptime-ification was already done for us. 3871 // Just make sure the pointer is const. 3872 return sema.makePtrConst(block, alloc); 3873 }, 3874 else => {}, 3875 }, 3876 else => {}, 3877 } 3878 3879 if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct; 3880 const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; 3881 assert(ptr.byte_offset == 0); 3882 const alloc_index = ptr.base_addr.comptime_alloc; 3883 const ct_alloc = sema.getComptimeAlloc(alloc_index); 3884 const interned = try ct_alloc.val.intern(pt, sema.arena); 3885 if (interned.canMutateComptimeVarState(zcu)) { 3886 // Preserve the comptime alloc, just make the pointer const. 3887 ct_alloc.val = .{ .interned = interned.toIntern() }; 3888 ct_alloc.is_const = true; 3889 return sema.makePtrConst(block, alloc); 3890 } else { 3891 // Promote the constant to an anon decl. 3892 const new_mut_ptr = Air.internedToRef(try pt.intern(.{ .ptr = .{ 3893 .ty = alloc_ty.toIntern(), 3894 .base_addr = .{ .uav = .{ 3895 .val = interned.toIntern(), 3896 .orig_ty = alloc_ty.toIntern(), 3897 } }, 3898 .byte_offset = 0, 3899 } })); 3900 return sema.makePtrConst(block, new_mut_ptr); 3901 } 3902 } 3903 3904 // Otherwise, check if the alloc is comptime-known despite being in a runtime scope. 3905 if (try sema.resolveComptimeKnownAllocPtr(block, alloc, null)) |ptr_val| { 3906 return sema.makePtrConst(block, Air.internedToRef(ptr_val)); 3907 } 3908 3909 if (try elem_ty.comptimeOnlySema(pt)) { 3910 // The value was initialized through RLS, so we didn't detect the runtime condition earlier. 3911 // TODO: source location of runtime control flow 3912 const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node }); 3913 return sema.fail(block, init_src, "value with comptime-only type '{f}' depends on runtime control flow", .{elem_ty.fmt(pt)}); 3914 } 3915 3916 // This is a runtime value. 3917 return sema.makePtrConst(block, alloc); 3918 } 3919 3920 /// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved 3921 /// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`. 3922 fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index { 3923 const pt = sema.pt; 3924 const zcu = pt.zcu; 3925 3926 const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc); 3927 const ptr_info = alloc_ty.ptrInfo(zcu); 3928 const elem_ty: Type = .fromInterned(ptr_info.child); 3929 3930 const alloc_inst = alloc.toIndex() orelse return null; 3931 const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null; 3932 const stores = comptime_info.value.stores.items(.inst); 3933 3934 // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known. 3935 // We will resolve and return its value. 3936 3937 // We expect to have emitted at least one store, unless the elem type is OPV. 3938 if (stores.len == 0) { 3939 const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern(); 3940 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value); 3941 } 3942 3943 // In general, we want to create a comptime alloc of the correct type and 3944 // apply the stores to that alloc in order. However, before going to all 3945 // that effort, let's optimize for the common case of a single store. 3946 3947 simple: { 3948 if (stores.len != 1) break :simple; 3949 const store_inst = sema.air_instructions.get(@intFromEnum(stores[0])); 3950 switch (store_inst.tag) { 3951 .store, .store_safe => {}, 3952 .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => break :simple, // there's OPV stuff going on! 3953 else => unreachable, 3954 } 3955 if (store_inst.data.bin_op.lhs != alloc) break :simple; 3956 3957 const val = store_inst.data.bin_op.rhs.toInterned().?; 3958 assert(zcu.intern_pool.typeOf(val) == elem_ty.toIntern()); 3959 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value); 3960 } 3961 3962 // The simple strategy failed: we must create a mutable comptime alloc and 3963 // perform all of the runtime store operations at comptime. 3964 3965 const ct_alloc = try sema.newComptimeAlloc(block, .unneeded, elem_ty, ptr_info.flags.alignment); 3966 3967 const alloc_ptr = try pt.intern(.{ .ptr = .{ 3968 .ty = alloc_ty.toIntern(), 3969 .base_addr = .{ .comptime_alloc = ct_alloc }, 3970 .byte_offset = 0, 3971 } }); 3972 3973 // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the comptime alloc 3974 var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena); 3975 try ptr_mapping.ensureTotalCapacity(@intCast(stores.len)); 3976 ptr_mapping.putAssumeCapacity(alloc_inst, alloc_ptr); 3977 3978 // Whilst constructing our mapping, we will also initialize optional and error union payloads when 3979 // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters. 3980 var to_map = try std.array_list.Managed(Air.Inst.Index).initCapacity(sema.arena, stores.len); 3981 3982 for (stores) |store_inst_idx| { 3983 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx)); 3984 const ptr_to_map = switch (store_inst.tag) { 3985 .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to. 3986 .set_union_tag => store_inst.data.bin_op.lhs.toIndex().?, // Map the union pointer. 3987 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself. 3988 else => unreachable, 3989 }; 3990 to_map.appendAssumeCapacity(ptr_to_map); 3991 } 3992 3993 const tmp_air = sema.getTmpAir(); 3994 3995 while (to_map.pop()) |air_ptr| { 3996 if (ptr_mapping.contains(air_ptr)) continue; 3997 const PointerMethod = union(enum) { 3998 same_addr, 3999 opt_payload, 4000 eu_payload, 4001 field: u32, 4002 elem: u64, 4003 }; 4004 const inst_tag = tmp_air.instructions.items(.tag)[@intFromEnum(air_ptr)]; 4005 const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) { 4006 .struct_field_ptr => blk: { 4007 const data = tmp_air.extraData( 4008 Air.StructField, 4009 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload, 4010 ).data; 4011 break :blk .{ 4012 data.struct_operand, 4013 .{ .field = data.field_index }, 4014 }; 4015 }, 4016 .struct_field_ptr_index_0, 4017 .struct_field_ptr_index_1, 4018 .struct_field_ptr_index_2, 4019 .struct_field_ptr_index_3, 4020 => .{ 4021 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4022 .{ .field = switch (inst_tag) { 4023 .struct_field_ptr_index_0 => 0, 4024 .struct_field_ptr_index_1 => 1, 4025 .struct_field_ptr_index_2 => 2, 4026 .struct_field_ptr_index_3 => 3, 4027 else => unreachable, 4028 } }, 4029 }, 4030 .ptr_slice_ptr_ptr => .{ 4031 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4032 .{ .field = Value.slice_ptr_index }, 4033 }, 4034 .ptr_slice_len_ptr => .{ 4035 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4036 .{ .field = Value.slice_len_index }, 4037 }, 4038 .ptr_elem_ptr => blk: { 4039 const data = tmp_air.extraData( 4040 Air.Bin, 4041 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload, 4042 ).data; 4043 const idx_val = (try sema.resolveValue(data.rhs)).?; 4044 break :blk .{ 4045 data.lhs, 4046 .{ .elem = try idx_val.toUnsignedIntSema(pt) }, 4047 }; 4048 }, 4049 .bitcast => .{ 4050 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4051 .same_addr, 4052 }, 4053 .optional_payload_ptr_set => .{ 4054 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4055 .opt_payload, 4056 }, 4057 .errunion_payload_ptr_set => .{ 4058 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4059 .eu_payload, 4060 }, 4061 else => unreachable, 4062 }; 4063 4064 const decl_parent_ptr = ptr_mapping.get(air_parent_ptr.toIndex().?) orelse { 4065 // Resolve the parent pointer first. 4066 // Note that we add in what seems like the wrong order, because we're popping from the end of this array. 4067 try to_map.appendSlice(&.{ air_ptr, air_parent_ptr.toIndex().? }); 4068 continue; 4069 }; 4070 const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &zcu.intern_pool).toIntern(); 4071 const new_ptr = switch (method) { 4072 .same_addr => try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, decl_parent_ptr, new_ptr_ty), 4073 .opt_payload => ptr: { 4074 // Set the optional to non-null at comptime. 4075 // If the payload is OPV, we must use that value instead of undef. 4076 const opt_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); 4077 const payload_ty = opt_ty.optionalChild(zcu); 4078 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); 4079 const opt_val = try pt.intern(.{ .opt = .{ 4080 .ty = opt_ty.toIntern(), 4081 .val = payload_val.toIntern(), 4082 } }); 4083 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(opt_val), opt_ty); 4084 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrOptPayload(pt)).toIntern(); 4085 }, 4086 .eu_payload => ptr: { 4087 // Set the error union to non-error at comptime. 4088 // If the payload is OPV, we must use that value instead of undef. 4089 const eu_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); 4090 const payload_ty = eu_ty.errorUnionPayload(zcu); 4091 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); 4092 const eu_val = try pt.intern(.{ .error_union = .{ 4093 .ty = eu_ty.toIntern(), 4094 .val = .{ .payload = payload_val.toIntern() }, 4095 } }); 4096 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(eu_val), eu_ty); 4097 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrEuPayload(pt)).toIntern(); 4098 }, 4099 .field => |idx| ptr: { 4100 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); 4101 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| { 4102 // As this is a union field, we must store to the pointer now to set the tag. 4103 // The payload value will be stored later, so undef is a sufficent payload for now. 4104 const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]); 4105 const payload_val = try pt.undefValue(payload_ty); 4106 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx); 4107 const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val); 4108 try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty); 4109 } 4110 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern(); 4111 }, 4112 .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(), 4113 }; 4114 try ptr_mapping.put(air_ptr, new_ptr); 4115 } 4116 4117 // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime. 4118 // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set` 4119 // instructions were already done above. 4120 4121 for (stores) |store_inst_idx| { 4122 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx)); 4123 switch (store_inst.tag) { 4124 .optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above 4125 .set_union_tag => { 4126 // Usually, we can ignore these, because the creation of the field pointer above 4127 // already did it for us. However, if the field is OPV, this is relevant, because 4128 // there is not going to be a store to the field. So we must initialize the union 4129 // tag if the field is OPV. 4130 const union_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?; 4131 const union_ptr_val: Value = .fromInterned(ptr_mapping.get(union_ptr_inst).?); 4132 const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?); 4133 const union_ty = union_ptr_val.typeOf(zcu).childType(zcu); 4134 const field_ty = union_ty.unionFieldType(tag_val, zcu).?; 4135 if (try sema.typeHasOnePossibleValue(field_ty)) |payload_val| { 4136 const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val); 4137 try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty); 4138 } 4139 }, 4140 .store, .store_safe => { 4141 const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?; 4142 const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?; 4143 const new_ptr = ptr_mapping.get(air_ptr_inst).?; 4144 try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu)); 4145 }, 4146 else => unreachable, 4147 } 4148 } 4149 4150 // The value is finalized - load it! 4151 const val = (try sema.pointerDeref(block, LazySrcLoc.unneeded, Value.fromInterned(alloc_ptr), alloc_ty)).?.toIntern(); 4152 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, ct_alloc, alloc_inst, comptime_info.value); 4153 } 4154 4155 /// Given the resolved comptime-known value, rewrites the dead AIR to not 4156 /// create a runtime stack allocation. Also places the resulting value into 4157 /// either an anon decl ref or a comptime alloc depending on whether it 4158 /// references comptime-mutable memory. If `existing_comptime_alloc` is 4159 /// passed, it is a scratch allocation which already contains `result_val`. 4160 /// Same return type as `resolveComptimeKnownAllocPtr` so we can tail call. 4161 fn finishResolveComptimeKnownAllocPtr( 4162 sema: *Sema, 4163 block: *Block, 4164 alloc_ty: Type, 4165 result_val: InternPool.Index, 4166 existing_comptime_alloc: ?ComptimeAllocIndex, 4167 alloc_inst: Air.Inst.Index, 4168 comptime_info: MaybeComptimeAlloc, 4169 ) CompileError!?InternPool.Index { 4170 const pt = sema.pt; 4171 const zcu = pt.zcu; 4172 4173 // We're almost done - we have the resolved comptime value. We just need to 4174 // eliminate the now-dead runtime instructions. 4175 4176 // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to 4177 // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this 4178 // too because it doesn't really matter what they become. 4179 const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{ 4180 .ty = .fromIntern(alloc_ty.toIntern()), 4181 .operand = .zero_usize, 4182 } } }; 4183 4184 sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst); 4185 for (comptime_info.stores.items(.inst)) |store_inst| { 4186 sema.air_instructions.set(@intFromEnum(store_inst), nop_inst); 4187 } 4188 4189 if (Value.fromInterned(result_val).canMutateComptimeVarState(zcu)) { 4190 const alloc_index = existing_comptime_alloc orelse a: { 4191 const idx = try sema.newComptimeAlloc(block, .unneeded, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu)); 4192 const alloc = sema.getComptimeAlloc(idx); 4193 alloc.val = .{ .interned = result_val }; 4194 break :a idx; 4195 }; 4196 sema.getComptimeAlloc(alloc_index).is_const = true; 4197 return try pt.intern(.{ .ptr = .{ 4198 .ty = alloc_ty.toIntern(), 4199 .base_addr = .{ .comptime_alloc = alloc_index }, 4200 .byte_offset = 0, 4201 } }); 4202 } else { 4203 return try pt.intern(.{ .ptr = .{ 4204 .ty = alloc_ty.toIntern(), 4205 .base_addr = .{ .uav = .{ 4206 .orig_ty = alloc_ty.toIntern(), 4207 .val = result_val, 4208 } }, 4209 .byte_offset = 0, 4210 } }); 4211 } 4212 } 4213 4214 fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type { 4215 var ptr_info = ptr_ty.ptrInfo(sema.pt.zcu); 4216 ptr_info.flags.is_const = true; 4217 return sema.pt.ptrTypeSema(ptr_info); 4218 } 4219 4220 fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { 4221 const alloc_ty = sema.typeOf(alloc); 4222 const const_ptr_ty = try sema.makePtrTyConst(alloc_ty); 4223 4224 // Detect if a comptime value simply needs to have its type changed. 4225 if (try sema.resolveValue(alloc)) |val| { 4226 return Air.internedToRef((try sema.pt.getCoerced(val, const_ptr_ty)).toIntern()); 4227 } 4228 4229 return block.addBitCast(const_ptr_ty, alloc); 4230 } 4231 4232 fn zirAllocInferredComptime( 4233 sema: *Sema, 4234 is_const: bool, 4235 ) CompileError!Air.Inst.Ref { 4236 const gpa = sema.gpa; 4237 4238 try sema.air_instructions.append(gpa, .{ 4239 .tag = .inferred_alloc_comptime, 4240 .data = .{ .inferred_alloc_comptime = .{ 4241 .alignment = .none, 4242 .is_const = is_const, 4243 .ptr = undefined, 4244 } }, 4245 }); 4246 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 4247 } 4248 4249 fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4250 const tracy = trace(@src()); 4251 defer tracy.end(); 4252 4253 const pt = sema.pt; 4254 4255 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4256 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); 4257 const var_src = block.nodeOffset(inst_data.src_node); 4258 4259 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 4260 if (block.isComptime() or try var_ty.comptimeOnlySema(pt)) { 4261 return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); 4262 } 4263 if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { 4264 const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); 4265 return sema.fail(block, mut_src, "local variable in naked function", .{}); 4266 } 4267 const target = pt.zcu.getTarget(); 4268 const ptr_type = try pt.ptrTypeSema(.{ 4269 .child = var_ty.toIntern(), 4270 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 4271 }); 4272 const ptr = try block.addTy(.alloc, ptr_type); 4273 const ptr_inst = ptr.toIndex().?; 4274 try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); 4275 try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst); 4276 return ptr; 4277 } 4278 4279 fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4280 const tracy = trace(@src()); 4281 defer tracy.end(); 4282 4283 const pt = sema.pt; 4284 4285 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4286 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); 4287 const var_src = block.nodeOffset(inst_data.src_node); 4288 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 4289 if (block.isComptime()) { 4290 return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); 4291 } 4292 if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { 4293 const store_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); 4294 return sema.fail(block, store_src, "local variable in naked function", .{}); 4295 } 4296 try sema.validateVarType(block, ty_src, var_ty, false); 4297 const target = pt.zcu.getTarget(); 4298 const ptr_type = try pt.ptrTypeSema(.{ 4299 .child = var_ty.toIntern(), 4300 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 4301 }); 4302 return block.addTy(.alloc, ptr_type); 4303 } 4304 4305 fn zirAllocInferred( 4306 sema: *Sema, 4307 block: *Block, 4308 is_const: bool, 4309 ) CompileError!Air.Inst.Ref { 4310 const tracy = trace(@src()); 4311 defer tracy.end(); 4312 4313 const gpa = sema.gpa; 4314 4315 if (block.isComptime()) { 4316 try sema.air_instructions.append(gpa, .{ 4317 .tag = .inferred_alloc_comptime, 4318 .data = .{ .inferred_alloc_comptime = .{ 4319 .alignment = .none, 4320 .is_const = is_const, 4321 .ptr = undefined, 4322 } }, 4323 }); 4324 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 4325 } 4326 4327 const result_index = try block.addInstAsIndex(.{ 4328 .tag = .inferred_alloc, 4329 .data = .{ .inferred_alloc = .{ 4330 .alignment = .none, 4331 .is_const = is_const, 4332 } }, 4333 }); 4334 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); 4335 if (is_const) { 4336 try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); 4337 try sema.base_allocs.put(sema.gpa, result_index, result_index); 4338 } 4339 return result_index.toRef(); 4340 } 4341 4342 fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4343 const tracy = trace(@src()); 4344 defer tracy.end(); 4345 4346 const pt = sema.pt; 4347 const zcu = pt.zcu; 4348 const gpa = sema.gpa; 4349 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4350 const src = block.nodeOffset(inst_data.src_node); 4351 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); 4352 const ptr = try sema.resolveInst(inst_data.operand); 4353 const ptr_inst = ptr.toIndex().?; 4354 const target = zcu.getTarget(); 4355 4356 switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) { 4357 .inferred_alloc_comptime => { 4358 // The work was already done for us by `Sema.storeToInferredAllocComptime`. 4359 // All we need to do is return the pointer. 4360 const iac = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc_comptime; 4361 const resolved_ptr = iac.ptr; 4362 4363 if (std.debug.runtime_safety) { 4364 // The inferred_alloc_comptime should never be referenced again 4365 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ .tag = undefined, .data = undefined }); 4366 } 4367 4368 const val = switch (zcu.intern_pool.indexToKey(resolved_ptr).ptr.base_addr) { 4369 .uav => |a| a.val, 4370 .comptime_alloc => |i| val: { 4371 const alloc = sema.getComptimeAlloc(i); 4372 break :val (try alloc.val.intern(pt, sema.arena)).toIntern(); 4373 }, 4374 else => unreachable, 4375 }; 4376 if (zcu.intern_pool.isFuncBody(val)) { 4377 const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val)); 4378 if (try ty.fnHasRuntimeBitsSema(pt)) { 4379 const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(val); 4380 try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); 4381 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); 4382 } 4383 } 4384 4385 return Air.internedToRef(resolved_ptr); 4386 }, 4387 .inferred_alloc => { 4388 const ia1 = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc; 4389 const ia2 = sema.unresolved_inferred_allocs.fetchSwapRemove(ptr_inst).?.value; 4390 const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len); 4391 for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| { 4392 assert(sema.air_instructions.items(.tag)[@intFromEnum(store_inst)] == .store); 4393 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 4394 peer_val.* = bin_op.rhs; 4395 } 4396 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none); 4397 4398 const final_ptr_ty = try pt.ptrTypeSema(.{ 4399 .child = final_elem_ty.toIntern(), 4400 .flags = .{ 4401 .alignment = ia1.alignment, 4402 .address_space = target_util.defaultAddressSpace(target, .local), 4403 }, 4404 }); 4405 4406 if (!ia1.is_const) { 4407 try sema.validateVarType(block, ty_src, final_elem_ty, false); 4408 } else if (try sema.resolveComptimeKnownAllocPtr(block, ptr, final_ptr_ty)) |ptr_val| { 4409 const const_ptr_ty = try sema.makePtrTyConst(final_ptr_ty); 4410 const new_const_ptr = try pt.getCoerced(Value.fromInterned(ptr_val), const_ptr_ty); 4411 4412 // Unless the block is comptime, `alloc_inferred` always produces 4413 // a runtime constant. The final inferred type needs to be 4414 // fully resolved so it can be lowered in codegen. 4415 try final_elem_ty.resolveFully(pt); 4416 4417 return Air.internedToRef(new_const_ptr.toIntern()); 4418 } 4419 4420 if (try final_elem_ty.comptimeOnlySema(pt)) { 4421 // The alloc wasn't comptime-known per the above logic, so the 4422 // type cannot be comptime-only. 4423 // TODO: source location of runtime control flow 4424 return sema.fail(block, src, "value with comptime-only type '{f}' depends on runtime control flow", .{final_elem_ty.fmt(pt)}); 4425 } 4426 if (sema.func_is_naked and try final_elem_ty.hasRuntimeBitsSema(pt)) { 4427 const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); 4428 return sema.fail(block, mut_src, "local variable in naked function", .{}); 4429 } 4430 // Change it to a normal alloc. 4431 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ 4432 .tag = .alloc, 4433 .data = .{ .ty = final_ptr_ty }, 4434 }); 4435 4436 // Now we need to go back over all the store instructions, and do the logic as if 4437 // the new result ptr type was available. 4438 4439 for (ia2.prongs.items) |placeholder_inst| { 4440 var replacement_block = block.makeSubBlock(); 4441 defer replacement_block.instructions.deinit(gpa); 4442 4443 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .store); 4444 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].bin_op; 4445 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store); 4446 4447 // If only one instruction is produced then we can replace the store 4448 // placeholder instruction with this instruction; no need for an entire block. 4449 if (replacement_block.instructions.items.len == 1) { 4450 const only_inst = replacement_block.instructions.items[0]; 4451 sema.air_instructions.set(@intFromEnum(placeholder_inst), sema.air_instructions.get(@intFromEnum(only_inst))); 4452 continue; 4453 } 4454 4455 // Here we replace the placeholder store instruction with a block 4456 // that does the actual store logic. 4457 _ = try replacement_block.addBr(placeholder_inst, .void_value); 4458 try sema.air_extra.ensureUnusedCapacity( 4459 gpa, 4460 @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len, 4461 ); 4462 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{ 4463 .tag = .block, 4464 .data = .{ .ty_pl = .{ 4465 .ty = .void_type, 4466 .payload = sema.addExtraAssumeCapacity(Air.Block{ 4467 .body_len = @intCast(replacement_block.instructions.items.len), 4468 }), 4469 } }, 4470 }); 4471 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); 4472 } 4473 4474 if (ia1.is_const) { 4475 return sema.makePtrConst(block, ptr); 4476 } else { 4477 return ptr; 4478 } 4479 }, 4480 else => unreachable, 4481 } 4482 } 4483 4484 fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4485 const pt = sema.pt; 4486 const zcu = pt.zcu; 4487 const gpa = sema.gpa; 4488 const ip = &zcu.intern_pool; 4489 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4490 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 4491 const all_args = sema.code.refSlice(extra.end, extra.data.operands_len); 4492 const arg_pairs: []const [2]Zir.Inst.Ref = @as([*]const [2]Zir.Inst.Ref, @ptrCast(all_args))[0..@divExact(all_args.len, 2)]; 4493 const src = block.nodeOffset(inst_data.src_node); 4494 4495 var len: Air.Inst.Ref = .none; 4496 var len_val: ?Value = null; 4497 var len_idx: u32 = undefined; 4498 var any_runtime = false; 4499 4500 const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, arg_pairs.len); 4501 defer gpa.free(runtime_arg_lens); 4502 4503 // First pass to look for comptime values. 4504 for (arg_pairs, 0..) |zir_arg_pair, i_usize| { 4505 const i: u32 = @intCast(i_usize); 4506 runtime_arg_lens[i] = .none; 4507 if (zir_arg_pair[0] == .none) continue; 4508 4509 const arg_src = block.src(.{ .for_input = .{ 4510 .for_node_offset = inst_data.src_node, 4511 .input_index = i, 4512 } }); 4513 4514 const arg_len_uncoerced = if (zir_arg_pair[1] == .none) l: { 4515 // This argument is an indexable. 4516 const object = try sema.resolveInst(zir_arg_pair[0]); 4517 const object_ty = sema.typeOf(object); 4518 if (!object_ty.isIndexable(zcu)) { 4519 // Instead of using checkIndexable we customize this error. 4520 const msg = msg: { 4521 const msg = try sema.errMsg(arg_src, "type '{f}' is not indexable and not a range", .{object_ty.fmt(pt)}); 4522 errdefer msg.destroy(sema.gpa); 4523 try sema.errNote(arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{}); 4524 4525 if (object_ty.zigTypeTag(zcu) == .error_union) { 4526 try sema.errNote(arg_src, msg, "consider using 'try', 'catch', or 'if'", .{}); 4527 } 4528 4529 break :msg msg; 4530 }; 4531 return sema.failWithOwnedErrorMsg(block, msg); 4532 } 4533 if (!object_ty.indexableHasLen(zcu)) continue; 4534 break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), arg_src); 4535 } else l: { 4536 // This argument is a range. 4537 const range_start = try sema.resolveInst(zir_arg_pair[0]); 4538 const range_end = try sema.resolveInst(zir_arg_pair[1]); 4539 break :l try sema.analyzeArithmetic(block, .sub, range_end, range_start, arg_src, arg_src, arg_src, true); 4540 }; 4541 const arg_len = try sema.coerce(block, .usize, arg_len_uncoerced, arg_src); 4542 if (len == .none) { 4543 len = arg_len; 4544 len_idx = i; 4545 } 4546 if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| { 4547 if (len_val) |v| { 4548 if (!(try sema.valuesEqual(arg_val, v, .usize))) { 4549 const msg = msg: { 4550 const msg = try sema.errMsg(src, "non-matching for loop lengths", .{}); 4551 errdefer msg.destroy(gpa); 4552 const a_src = block.src(.{ .for_input = .{ 4553 .for_node_offset = inst_data.src_node, 4554 .input_index = len_idx, 4555 } }); 4556 try sema.errNote(a_src, msg, "length {f} here", .{ 4557 v.fmtValueSema(pt, sema), 4558 }); 4559 try sema.errNote(arg_src, msg, "length {f} here", .{ 4560 arg_val.fmtValueSema(pt, sema), 4561 }); 4562 break :msg msg; 4563 }; 4564 return sema.failWithOwnedErrorMsg(block, msg); 4565 } 4566 } else { 4567 len = arg_len; 4568 len_val = arg_val; 4569 len_idx = i; 4570 } 4571 continue; 4572 } 4573 runtime_arg_lens[i] = arg_len; 4574 any_runtime = true; 4575 } 4576 4577 if (len == .none) { 4578 const msg = msg: { 4579 const msg = try sema.errMsg(src, "unbounded for loop", .{}); 4580 errdefer msg.destroy(gpa); 4581 for (arg_pairs, 0..) |zir_arg_pair, i_usize| { 4582 const i: u32 = @intCast(i_usize); 4583 if (zir_arg_pair[0] == .none) continue; 4584 if (zir_arg_pair[1] != .none) continue; 4585 const object = try sema.resolveInst(zir_arg_pair[0]); 4586 const object_ty = sema.typeOf(object); 4587 const arg_src = block.src(.{ .for_input = .{ 4588 .for_node_offset = inst_data.src_node, 4589 .input_index = i, 4590 } }); 4591 try sema.errNote(arg_src, msg, "type '{f}' has no upper bound", .{ 4592 object_ty.fmt(pt), 4593 }); 4594 } 4595 break :msg msg; 4596 }; 4597 return sema.failWithOwnedErrorMsg(block, msg); 4598 } 4599 4600 // Now for the runtime checks. 4601 if (any_runtime and block.wantSafety()) { 4602 for (runtime_arg_lens, 0..) |arg_len, i| { 4603 if (arg_len == .none) continue; 4604 if (i == len_idx) continue; 4605 const ok = try block.addBinOp(.cmp_eq, len, arg_len); 4606 try sema.addSafetyCheck(block, src, ok, .for_len_mismatch); 4607 } 4608 } 4609 4610 return len; 4611 } 4612 4613 /// Given any single pointer, retrieve a pointer to the payload of any optional 4614 /// or error union pointed to, initializing these pointers along the way. 4615 /// Given a `*E!?T`, returns a (valid) `*T`. 4616 /// May invalidate already-stored payload data. 4617 fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref { 4618 const pt = sema.pt; 4619 const zcu = pt.zcu; 4620 var base_ptr = ptr; 4621 while (true) switch (sema.typeOf(base_ptr).childType(zcu).zigTypeTag(zcu)) { 4622 .error_union => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), 4623 .optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), 4624 else => break, 4625 }; 4626 try sema.checkKnownAllocPtr(block, ptr, base_ptr); 4627 return base_ptr; 4628 } 4629 4630 fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4631 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4632 const ptr = try sema.resolveInst(un_node.operand); 4633 return sema.optEuBasePtrInit(block, ptr, block.nodeOffset(un_node.src_node)); 4634 } 4635 4636 fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4637 const pt = sema.pt; 4638 const zcu = pt.zcu; 4639 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4640 const src = block.nodeOffset(pl_node.src_node); 4641 const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; 4642 const uncoerced_val = try sema.resolveInst(extra.rhs); 4643 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.lhs) orelse return uncoerced_val; 4644 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); 4645 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction 4646 const elem_ty = ptr_ty.childType(zcu); 4647 switch (ptr_ty.ptrSize(zcu)) { 4648 .one => { 4649 const uncoerced_ty = sema.typeOf(uncoerced_val); 4650 if (elem_ty.zigTypeTag(zcu) == .array and elem_ty.childType(zcu).toIntern() == uncoerced_ty.toIntern()) { 4651 // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion. 4652 return uncoerced_val; 4653 } 4654 // If the destination type is anyopaque, don't coerce - the pointer will coerce instead. 4655 if (elem_ty.toIntern() == .anyopaque_type) { 4656 return uncoerced_val; 4657 } else { 4658 return sema.coerce(block, elem_ty, uncoerced_val, src); 4659 } 4660 }, 4661 .slice, .many => { 4662 // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`. 4663 const val_ty = sema.typeOf(uncoerced_val); 4664 switch (val_ty.zigTypeTag(zcu)) { 4665 .array, .vector => {}, 4666 else => if (!val_ty.isTuple(zcu)) { 4667 return sema.fail(block, src, "expected array of '{f}', found '{f}'", .{ elem_ty.fmt(pt), val_ty.fmt(pt) }); 4668 }, 4669 } 4670 const want_ty = try pt.arrayType(.{ 4671 .len = val_ty.arrayLen(zcu), 4672 .child = elem_ty.toIntern(), 4673 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 4674 }); 4675 return sema.coerce(block, want_ty, uncoerced_val, src); 4676 }, 4677 .c => { 4678 // There's nothing meaningful to do here, because we don't know if this is meant to be a 4679 // single-pointer or a many-pointer. 4680 return uncoerced_val; 4681 }, 4682 } 4683 } 4684 4685 fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { 4686 const pt = sema.pt; 4687 const zcu = pt.zcu; 4688 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4689 const src = block.nodeOffset(un_node.src_node); 4690 4691 const operand_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type; 4692 4693 const payload_ty = if (is_ref) ty: { 4694 if (!operand_ty.isSinglePointer(zcu)) { 4695 return .generic_poison_type; // we can't get a meaningful result type here, since it will be `*E![n]T`, and we don't know `n`. 4696 } 4697 break :ty operand_ty.childType(zcu); 4698 } else operand_ty; 4699 4700 const err_set_ty: Type = err_set: { 4701 // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals 4702 // until we hit an error union or set. 4703 var cur_ty = sema.fn_ret_ty; 4704 while (true) { 4705 switch (cur_ty.zigTypeTag(zcu)) { 4706 .error_set => break :err_set cur_ty, 4707 .error_union => break :err_set cur_ty.errorUnionSet(zcu), 4708 .optional => cur_ty = cur_ty.optionalChild(zcu), 4709 else => { 4710 // This function cannot return an error. 4711 // `try` is still valid if the error case is impossible, i.e. no error is returned. 4712 // So, the result type has an error set of `error{}`. 4713 break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(zcu.gpa, pt.tid, &.{})); 4714 }, 4715 } 4716 } 4717 }; 4718 4719 const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty); 4720 4721 if (is_ref) { 4722 var ptr_info = operand_ty.ptrInfo(zcu); 4723 ptr_info.child = eu_ty.toIntern(); 4724 const eu_ptr_ty = try pt.ptrTypeSema(ptr_info); 4725 return Air.internedToRef(eu_ptr_ty.toIntern()); 4726 } else { 4727 return Air.internedToRef(eu_ty.toIntern()); 4728 } 4729 } 4730 4731 fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 4732 const pt = sema.pt; 4733 const zcu = pt.zcu; 4734 const un_tok = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 4735 const src = block.tokenOffset(un_tok.src_tok); 4736 // In case of GenericPoison, we don't actually have a type, so this will be 4737 // treated as an untyped address-of operator. 4738 const ty_operand = try sema.resolveTypeOrPoison(block, src, un_tok.operand) orelse return; 4739 if (ty_operand.optEuBaseType(zcu).zigTypeTag(zcu) != .pointer) { 4740 return sema.failWithOwnedErrorMsg(block, msg: { 4741 const msg = try sema.errMsg(src, "expected type '{f}', found pointer", .{ty_operand.fmt(pt)}); 4742 errdefer msg.destroy(sema.gpa); 4743 try sema.errNote(src, msg, "address-of operator always returns a pointer", .{}); 4744 break :msg msg; 4745 }); 4746 } 4747 } 4748 4749 fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 4750 if (!block.isComptime()) return; 4751 4752 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4753 const src = block.nodeOffset(un_node.src_node); 4754 const init_ref = try sema.resolveInst(un_node.operand); 4755 if (!try sema.isComptimeKnown(init_ref)) { 4756 return sema.failWithNeededComptime(block, src, null); 4757 } 4758 } 4759 4760 fn zirValidateArrayInitRefTy( 4761 sema: *Sema, 4762 block: *Block, 4763 inst: Zir.Inst.Index, 4764 ) CompileError!Air.Inst.Ref { 4765 const pt = sema.pt; 4766 const zcu = pt.zcu; 4767 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4768 const src = block.nodeOffset(pl_node.src_node); 4769 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; 4770 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.ptr_ty) orelse return .generic_poison_type; 4771 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); 4772 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction 4773 switch (zcu.intern_pool.indexToKey(ptr_ty.toIntern())) { 4774 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 4775 .slice, .many => { 4776 // Use array of correct length 4777 const arr_ty = try pt.arrayType(.{ 4778 .len = extra.elem_count, 4779 .child = ptr_ty.childType(zcu).toIntern(), 4780 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 4781 }); 4782 return Air.internedToRef(arr_ty.toIntern()); 4783 }, 4784 else => {}, 4785 }, 4786 else => {}, 4787 } 4788 // Otherwise, we just want the pointer child type 4789 const ret_ty = ptr_ty.childType(zcu); 4790 if (ret_ty.toIntern() == .anyopaque_type) { 4791 // The actual array type is unknown, which we represent with a generic poison. 4792 return .generic_poison_type; 4793 } 4794 const arr_ty = ret_ty.optEuBaseType(zcu); 4795 try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty); 4796 return Air.internedToRef(ret_ty.toIntern()); 4797 } 4798 4799 fn zirValidateArrayInitTy( 4800 sema: *Sema, 4801 block: *Block, 4802 inst: Zir.Inst.Index, 4803 is_result_ty: bool, 4804 ) CompileError!void { 4805 const pt = sema.pt; 4806 const zcu = pt.zcu; 4807 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4808 const src = block.nodeOffset(inst_data.src_node); 4809 const ty_src: LazySrcLoc = if (is_result_ty) src else block.src(.{ .node_offset_init_ty = inst_data.src_node }); 4810 const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data; 4811 // It's okay for the type to be poison: this will result in an anonymous array init. 4812 const ty = try sema.resolveTypeOrPoison(block, ty_src, extra.ty) orelse return; 4813 const arr_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty; 4814 return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty); 4815 } 4816 4817 fn validateArrayInitTy( 4818 sema: *Sema, 4819 block: *Block, 4820 src: LazySrcLoc, 4821 ty_src: LazySrcLoc, 4822 init_count: u32, 4823 ty: Type, 4824 ) CompileError!void { 4825 const pt = sema.pt; 4826 const zcu = pt.zcu; 4827 switch (ty.zigTypeTag(zcu)) { 4828 .array => { 4829 const array_len = ty.arrayLen(zcu); 4830 if (init_count != array_len) { 4831 return sema.fail(block, src, "expected {d} array elements; found {d}", .{ 4832 array_len, init_count, 4833 }); 4834 } 4835 return; 4836 }, 4837 .vector => { 4838 const array_len = ty.arrayLen(zcu); 4839 if (init_count != array_len) { 4840 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{ 4841 array_len, init_count, 4842 }); 4843 } 4844 return; 4845 }, 4846 .@"struct" => if (ty.isTuple(zcu)) { 4847 try ty.resolveFields(pt); 4848 const array_len = ty.arrayLen(zcu); 4849 if (init_count > array_len) { 4850 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ 4851 array_len, init_count, 4852 }); 4853 } 4854 return; 4855 }, 4856 else => {}, 4857 } 4858 return sema.failWithArrayInitNotSupported(block, ty_src, ty); 4859 } 4860 4861 fn zirValidateStructInitTy( 4862 sema: *Sema, 4863 block: *Block, 4864 inst: Zir.Inst.Index, 4865 is_result_ty: bool, 4866 ) CompileError!void { 4867 const pt = sema.pt; 4868 const zcu = pt.zcu; 4869 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4870 const src = block.nodeOffset(inst_data.src_node); 4871 // It's okay for the type to be poison: this will result in an anonymous struct init. 4872 const ty = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return; 4873 const struct_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty; 4874 4875 switch (struct_ty.zigTypeTag(zcu)) { 4876 .@"struct", .@"union" => return, 4877 else => {}, 4878 } 4879 return sema.failWithStructInitNotSupported(block, src, struct_ty); 4880 } 4881 4882 fn zirValidatePtrStructInit( 4883 sema: *Sema, 4884 block: *Block, 4885 inst: Zir.Inst.Index, 4886 ) CompileError!void { 4887 const tracy = trace(@src()); 4888 defer tracy.end(); 4889 4890 const pt = sema.pt; 4891 const zcu = pt.zcu; 4892 const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4893 const init_src = block.nodeOffset(validate_inst.src_node); 4894 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); 4895 const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); 4896 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node; 4897 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4898 const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); 4899 const agg_ty = sema.typeOf(object_ptr).childType(zcu).optEuBaseType(zcu); 4900 switch (agg_ty.zigTypeTag(zcu)) { 4901 .@"struct" => return sema.validateStructInit( 4902 block, 4903 agg_ty, 4904 init_src, 4905 instrs, 4906 object_ptr, 4907 ), 4908 .@"union" => return sema.validateUnionInit( 4909 block, 4910 agg_ty, 4911 init_src, 4912 instrs, 4913 ), 4914 else => unreachable, 4915 } 4916 } 4917 4918 fn validateUnionInit( 4919 sema: *Sema, 4920 block: *Block, 4921 union_ty: Type, 4922 init_src: LazySrcLoc, 4923 instrs: []const Zir.Inst.Index, 4924 ) CompileError!void { 4925 if (instrs.len == 1) { 4926 // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`. 4927 return; 4928 } 4929 const msg = msg: { 4930 const msg = try sema.errMsg( 4931 init_src, 4932 "cannot initialize multiple union fields at once; unions can only have one active field", 4933 .{}, 4934 ); 4935 errdefer msg.destroy(sema.gpa); 4936 4937 for (instrs[1..]) |inst| { 4938 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4939 const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node }); 4940 try sema.errNote(inst_src, msg, "additional initializer here", .{}); 4941 } 4942 try sema.addDeclaredHereNote(msg, union_ty); 4943 break :msg msg; 4944 }; 4945 return sema.failWithOwnedErrorMsg(block, msg); 4946 } 4947 4948 fn validateStructInit( 4949 sema: *Sema, 4950 block: *Block, 4951 struct_ty: Type, 4952 init_src: LazySrcLoc, 4953 instrs: []const Zir.Inst.Index, 4954 struct_ptr: Air.Inst.Ref, 4955 ) CompileError!void { 4956 const pt = sema.pt; 4957 const zcu = pt.zcu; 4958 const gpa = sema.gpa; 4959 const ip = &zcu.intern_pool; 4960 4961 // Tracks whether each field was explicitly initialized. 4962 const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu)); 4963 defer gpa.free(found_fields); 4964 @memset(found_fields, false); 4965 4966 for (instrs) |field_ptr| { 4967 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node; 4968 const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node }); 4969 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4970 const field_name = try ip.getOrPutString( 4971 gpa, 4972 pt.tid, 4973 sema.code.nullTerminatedString(field_ptr_extra.field_name_start), 4974 .no_embedded_nulls, 4975 ); 4976 const field_index = if (struct_ty.isTuple(zcu)) 4977 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src) 4978 else 4979 try sema.structFieldIndex(block, struct_ty, field_name, field_src); 4980 assert(found_fields[field_index] == false); 4981 found_fields[field_index] = true; 4982 } 4983 4984 // Our job is simply to deal with default field values. Specifically, any field which was not 4985 // explicitly initialized must have its default value stored to the field pointer, or, if the 4986 // field has no default value, a compile error must be emitted instead. 4987 4988 // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However, 4989 // that work was actually all redundant: 4990 // 4991 // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing 4992 // the struct through RLS; there is no need to turn the field stores into one store. Comptime-known 4993 // consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends. 4994 // 4995 // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known. 4996 // But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the 4997 // field stores were to comptime-mutable pointers, so have already errored if not comptime-known. 4998 // 4999 // * If the value is runtime-known, then comptime-known fields must be validated as runtime values. 5000 // But this was already handled for every field store by the machinery in `checkComptimeKnownStore`. 5001 5002 var root_msg: ?*Zcu.ErrorMsg = null; 5003 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 5004 5005 for (found_fields, 0..) |explicit, i_usize| { 5006 if (explicit) continue; 5007 const i: u32 = @intCast(i_usize); 5008 5009 try struct_ty.resolveStructFieldInits(pt); 5010 const default_val = struct_ty.structFieldDefaultValue(i, zcu); 5011 if (default_val.toIntern() == .unreachable_value) { 5012 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse { 5013 const template = "missing tuple field with index {d}"; 5014 if (root_msg) |msg| { 5015 try sema.errNote(init_src, msg, template, .{i}); 5016 } else { 5017 root_msg = try sema.errMsg(init_src, template, .{i}); 5018 } 5019 continue; 5020 }; 5021 const template = "missing struct field: {f}"; 5022 const args = .{field_name.fmt(ip)}; 5023 if (root_msg) |msg| { 5024 try sema.errNote(init_src, msg, template, args); 5025 } else { 5026 root_msg = try sema.errMsg(init_src, template, args); 5027 } 5028 continue; 5029 } 5030 5031 const field_src = init_src; // TODO better source location 5032 const default_field_ptr = if (struct_ty.isTuple(zcu)) 5033 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) 5034 else 5035 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty); 5036 try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr); 5037 try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store); 5038 } 5039 5040 if (root_msg) |msg| { 5041 try sema.addDeclaredHereNote(msg, struct_ty); 5042 root_msg = null; 5043 return sema.failWithOwnedErrorMsg(block, msg); 5044 } 5045 } 5046 5047 fn zirValidatePtrArrayInit( 5048 sema: *Sema, 5049 block: *Block, 5050 inst: Zir.Inst.Index, 5051 ) CompileError!void { 5052 const pt = sema.pt; 5053 const zcu = pt.zcu; 5054 const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5055 const init_src = block.nodeOffset(validate_inst.src_node); 5056 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); 5057 const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); 5058 const first_elem_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node; 5059 const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; 5060 const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr); 5061 const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu); 5062 const array_len = array_ty.arrayLen(zcu); 5063 5064 // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR 5065 // to initialize them, or emitting a compile error if an unspecified field has no default. For 5066 // tuples, there are literally default field values, although they're guaranteed to be comptime 5067 // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never 5068 // specified so we always need to initialize here. For vectors, there's no such thing. 5069 5070 switch (array_ty.zigTypeTag(zcu)) { 5071 .@"struct" => if (instrs.len != array_len) { 5072 var root_msg: ?*Zcu.ErrorMsg = null; 5073 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 5074 5075 try array_ty.resolveStructFieldInits(pt); 5076 var i = instrs.len; 5077 while (i < array_len) : (i += 1) { 5078 const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern(); 5079 if (default_val == .unreachable_value) { 5080 const template = "missing tuple field with index {d}"; 5081 if (root_msg) |msg| { 5082 try sema.errNote(init_src, msg, template, .{i}); 5083 } else { 5084 root_msg = try sema.errMsg(init_src, template, .{i}); 5085 } 5086 continue; 5087 } 5088 } 5089 5090 if (root_msg) |msg| { 5091 root_msg = null; 5092 return sema.failWithOwnedErrorMsg(block, msg); 5093 } 5094 }, 5095 5096 .array => if (instrs.len != array_len) { 5097 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{ 5098 array_len, instrs.len, 5099 }); 5100 } else if (array_ty.sentinel(zcu)) |sentinel| { 5101 const array_len_ref = try pt.intRef(.usize, array_len); 5102 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true); 5103 try sema.checkKnownAllocPtr(block, array_ptr, sentinel_ptr); 5104 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, .fromValue(sentinel), init_src, .store); 5105 }, 5106 5107 .vector => if (instrs.len != array_len) { 5108 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{ 5109 array_len, instrs.len, 5110 }); 5111 }, 5112 5113 else => unreachable, 5114 } 5115 } 5116 5117 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5118 const pt = sema.pt; 5119 const zcu = pt.zcu; 5120 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5121 const src = block.nodeOffset(inst_data.src_node); 5122 const operand = try sema.resolveInst(inst_data.operand); 5123 const operand_ty = sema.typeOf(operand); 5124 5125 if (operand_ty.zigTypeTag(zcu) != .pointer) { 5126 return sema.fail(block, src, "cannot dereference non-pointer type '{f}'", .{operand_ty.fmt(pt)}); 5127 } else switch (operand_ty.ptrSize(zcu)) { 5128 .one, .c => {}, 5129 .many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{f}'", .{operand_ty.fmt(pt)}), 5130 .slice => return sema.fail(block, src, "index syntax required for slice type '{f}'", .{operand_ty.fmt(pt)}), 5131 } 5132 5133 if ((try sema.typeHasOnePossibleValue(operand_ty.childType(zcu))) != null) { 5134 // No need to validate the actual pointer value, we don't need it! 5135 return; 5136 } 5137 5138 const elem_ty = operand_ty.elemType2(zcu); 5139 if (try sema.resolveValue(operand)) |val| { 5140 if (val.isUndef(zcu)) { 5141 return sema.fail(block, src, "cannot dereference undefined value", .{}); 5142 } 5143 } else if (try elem_ty.comptimeOnlySema(pt)) { 5144 const msg = msg: { 5145 const msg = try sema.errMsg( 5146 src, 5147 "values of type '{f}' must be comptime-known, but operand value is runtime-known", 5148 .{elem_ty.fmt(pt)}, 5149 ); 5150 errdefer msg.destroy(sema.gpa); 5151 5152 try sema.explainWhyTypeIsComptime(msg, src, elem_ty); 5153 break :msg msg; 5154 }; 5155 return sema.failWithOwnedErrorMsg(block, msg); 5156 } 5157 } 5158 5159 fn typeIsDestructurable(ty: Type, zcu: *const Zcu) bool { 5160 return switch (ty.zigTypeTag(zcu)) { 5161 .array, .vector => true, 5162 .@"struct" => ty.isTuple(zcu), 5163 else => false, 5164 }; 5165 } 5166 5167 fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5168 const pt = sema.pt; 5169 const zcu = pt.zcu; 5170 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5171 const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data; 5172 const src = block.nodeOffset(inst_data.src_node); 5173 const destructure_src = block.nodeOffset(extra.destructure_node); 5174 const operand = try sema.resolveInst(extra.operand); 5175 const operand_ty = sema.typeOf(operand); 5176 5177 if (!typeIsDestructurable(operand_ty, zcu)) { 5178 return sema.failWithOwnedErrorMsg(block, msg: { 5179 const msg = try sema.errMsg(src, "type '{f}' cannot be destructured", .{operand_ty.fmt(pt)}); 5180 errdefer msg.destroy(sema.gpa); 5181 try sema.errNote(destructure_src, msg, "result destructured here", .{}); 5182 if (operand_ty.zigTypeTag(pt.zcu) == .error_union) { 5183 const base_op_ty = operand_ty.errorUnionPayload(zcu); 5184 if (typeIsDestructurable(base_op_ty, zcu)) 5185 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 5186 } 5187 break :msg msg; 5188 }); 5189 } 5190 5191 if (operand_ty.arrayLen(zcu) != extra.expect_len) { 5192 return sema.failWithOwnedErrorMsg(block, msg: { 5193 const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{ 5194 extra.expect_len, operand_ty.arrayLen(zcu), 5195 }); 5196 errdefer msg.destroy(sema.gpa); 5197 try sema.errNote(destructure_src, msg, "result destructured here", .{}); 5198 break :msg msg; 5199 }); 5200 } 5201 } 5202 5203 fn failWithBadMemberAccess( 5204 sema: *Sema, 5205 block: *Block, 5206 agg_ty: Type, 5207 field_src: LazySrcLoc, 5208 field_name: InternPool.NullTerminatedString, 5209 ) CompileError { 5210 const pt = sema.pt; 5211 const zcu = pt.zcu; 5212 const ip = &zcu.intern_pool; 5213 const kw_name = switch (agg_ty.zigTypeTag(zcu)) { 5214 .@"union" => "union", 5215 .@"struct" => "struct", 5216 .@"opaque" => "opaque", 5217 .@"enum" => "enum", 5218 else => unreachable, 5219 }; 5220 if (agg_ty.typeDeclInst(zcu)) |inst| if ((inst.resolve(ip) orelse return error.AnalysisFail) == .main_struct_inst) { 5221 return sema.fail(block, field_src, "root source file struct '{f}' has no member named '{f}'", .{ 5222 agg_ty.fmt(pt), field_name.fmt(ip), 5223 }); 5224 }; 5225 5226 return sema.fail(block, field_src, "{s} '{f}' has no member named '{f}'", .{ 5227 kw_name, agg_ty.fmt(pt), field_name.fmt(ip), 5228 }); 5229 } 5230 5231 fn failWithBadStructFieldAccess( 5232 sema: *Sema, 5233 block: *Block, 5234 struct_ty: Type, 5235 struct_type: InternPool.LoadedStructType, 5236 field_src: LazySrcLoc, 5237 field_name: InternPool.NullTerminatedString, 5238 ) CompileError { 5239 const pt = sema.pt; 5240 const zcu = pt.zcu; 5241 const ip = &zcu.intern_pool; 5242 5243 const msg = msg: { 5244 const msg = try sema.errMsg( 5245 field_src, 5246 "no field named '{f}' in struct '{f}'", 5247 .{ field_name.fmt(ip), struct_type.name.fmt(ip) }, 5248 ); 5249 errdefer msg.destroy(sema.gpa); 5250 try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{}); 5251 break :msg msg; 5252 }; 5253 return sema.failWithOwnedErrorMsg(block, msg); 5254 } 5255 5256 fn failWithBadUnionFieldAccess( 5257 sema: *Sema, 5258 block: *Block, 5259 union_ty: Type, 5260 union_obj: InternPool.LoadedUnionType, 5261 field_src: LazySrcLoc, 5262 field_name: InternPool.NullTerminatedString, 5263 ) CompileError { 5264 const pt = sema.pt; 5265 const zcu = pt.zcu; 5266 const ip = &zcu.intern_pool; 5267 const gpa = sema.gpa; 5268 5269 const msg = msg: { 5270 const msg = try sema.errMsg( 5271 field_src, 5272 "no field named '{f}' in union '{f}'", 5273 .{ field_name.fmt(ip), union_obj.name.fmt(ip) }, 5274 ); 5275 errdefer msg.destroy(gpa); 5276 try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{}); 5277 break :msg msg; 5278 }; 5279 return sema.failWithOwnedErrorMsg(block, msg); 5280 } 5281 5282 fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void { 5283 const zcu = sema.pt.zcu; 5284 const src_loc = decl_ty.srcLocOrNull(zcu) orelse return; 5285 const category = switch (decl_ty.zigTypeTag(zcu)) { 5286 .@"union" => "union", 5287 .@"struct" => "struct", 5288 .@"enum" => "enum", 5289 .@"opaque" => "opaque", 5290 else => unreachable, 5291 }; 5292 try sema.errNote(src_loc, parent, "{s} declared here", .{category}); 5293 } 5294 5295 fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5296 const tracy = trace(@src()); 5297 defer tracy.end(); 5298 5299 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5300 const src = block.nodeOffset(pl_node.src_node); 5301 const bin = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; 5302 const ptr = try sema.resolveInst(bin.lhs); 5303 const operand = try sema.resolveInst(bin.rhs); 5304 const ptr_inst = ptr.toIndex().?; 5305 const air_datas = sema.air_instructions.items(.data); 5306 5307 switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) { 5308 .inferred_alloc_comptime => { 5309 const iac = &air_datas[@intFromEnum(ptr_inst)].inferred_alloc_comptime; 5310 return sema.storeToInferredAllocComptime(block, src, operand, iac); 5311 }, 5312 .inferred_alloc => { 5313 const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; 5314 return sema.storeToInferredAlloc(block, src, ptr, operand, ia); 5315 }, 5316 else => unreachable, 5317 } 5318 } 5319 5320 fn storeToInferredAlloc( 5321 sema: *Sema, 5322 block: *Block, 5323 src: LazySrcLoc, 5324 ptr: Air.Inst.Ref, 5325 operand: Air.Inst.Ref, 5326 inferred_alloc: *InferredAlloc, 5327 ) CompileError!void { 5328 // Create a store instruction as a placeholder. This will be replaced by a 5329 // proper store sequence once we know the stored type. 5330 const dummy_store = try block.addBinOp(.store, ptr, operand); 5331 try sema.checkComptimeKnownStore(block, dummy_store, src); 5332 // Add the stored instruction to the set we will use to resolve peer types 5333 // for the inferred allocation. 5334 try inferred_alloc.prongs.append(sema.arena, dummy_store.toIndex().?); 5335 } 5336 5337 fn storeToInferredAllocComptime( 5338 sema: *Sema, 5339 block: *Block, 5340 src: LazySrcLoc, 5341 operand: Air.Inst.Ref, 5342 iac: *Air.Inst.Data.InferredAllocComptime, 5343 ) CompileError!void { 5344 const pt = sema.pt; 5345 const zcu = pt.zcu; 5346 const operand_ty = sema.typeOf(operand); 5347 // There will be only one store_to_inferred_ptr because we are running at comptime. 5348 // The alloc will turn into a Decl or a ComptimeAlloc. 5349 const operand_val = try sema.resolveValue(operand) orelse { 5350 return sema.failWithNeededComptime(block, src, .{ .simple = .stored_to_comptime_var }); 5351 }; 5352 const alloc_ty = try pt.ptrTypeSema(.{ 5353 .child = operand_ty.toIntern(), 5354 .flags = .{ 5355 .alignment = iac.alignment, 5356 .is_const = iac.is_const, 5357 }, 5358 }); 5359 if (iac.is_const and !operand_val.canMutateComptimeVarState(zcu)) { 5360 iac.ptr = try pt.intern(.{ .ptr = .{ 5361 .ty = alloc_ty.toIntern(), 5362 .base_addr = .{ .uav = .{ 5363 .val = operand_val.toIntern(), 5364 .orig_ty = alloc_ty.toIntern(), 5365 } }, 5366 .byte_offset = 0, 5367 } }); 5368 } else { 5369 const alloc_index = try sema.newComptimeAlloc(block, src, operand_ty, iac.alignment); 5370 sema.getComptimeAlloc(alloc_index).val = .{ .interned = operand_val.toIntern() }; 5371 iac.ptr = try pt.intern(.{ .ptr = .{ 5372 .ty = alloc_ty.toIntern(), 5373 .base_addr = .{ .comptime_alloc = alloc_index }, 5374 .byte_offset = 0, 5375 } }); 5376 } 5377 } 5378 5379 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5380 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5381 const src = block.nodeOffset(inst_data.src_node); 5382 const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, .u32, .{ .simple = .operand_setEvalBranchQuota })); 5383 sema.branch_quota = @max(sema.branch_quota, quota); 5384 sema.allow_memoize = false; 5385 } 5386 5387 fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5388 const tracy = trace(@src()); 5389 defer tracy.end(); 5390 5391 const zir_tags = sema.code.instructions.items(.tag); 5392 const zir_datas = sema.code.instructions.items(.data); 5393 const inst_data = zir_datas[@intFromEnum(inst)].pl_node; 5394 const src = block.nodeOffset(inst_data.src_node); 5395 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 5396 const ptr = try sema.resolveInst(extra.lhs); 5397 const operand = try sema.resolveInst(extra.rhs); 5398 5399 const is_ret = if (extra.lhs.toIndex()) |ptr_index| 5400 zir_tags[@intFromEnum(ptr_index)] == .ret_ptr 5401 else 5402 false; 5403 5404 const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); 5405 const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node }); 5406 const air_tag: Air.Inst.Tag = if (is_ret) 5407 .ret_ptr 5408 else if (block.wantSafety()) 5409 .store_safe 5410 else 5411 .store; 5412 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 5413 } 5414 5415 fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5416 const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code); 5417 return sema.addStrLit( 5418 try sema.pt.zcu.intern_pool.getOrPutString(sema.gpa, sema.pt.tid, bytes, .maybe_embedded_nulls), 5419 bytes.len, 5420 ); 5421 } 5422 5423 fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) CompileError!Air.Inst.Ref { 5424 return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool)); 5425 } 5426 5427 pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref { 5428 const pt = sema.pt; 5429 const array_ty = try pt.arrayType(.{ 5430 .len = len, 5431 .sentinel = .zero_u8, 5432 .child = .u8_type, 5433 }); 5434 const val = try pt.intern(.{ .aggregate = .{ 5435 .ty = array_ty.toIntern(), 5436 .storage = .{ .bytes = string }, 5437 } }); 5438 return sema.uavRef(val); 5439 } 5440 5441 fn uavRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref { 5442 return Air.internedToRef(try sema.pt.refValue(val)); 5443 } 5444 5445 fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5446 _ = block; 5447 const tracy = trace(@src()); 5448 defer tracy.end(); 5449 5450 const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].int; 5451 return sema.pt.intRef(.comptime_int, int); 5452 } 5453 5454 fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5455 _ = block; 5456 const tracy = trace(@src()); 5457 defer tracy.end(); 5458 5459 const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].str; 5460 const byte_count = int.len * @sizeOf(std.math.big.Limb); 5461 const limb_bytes = sema.code.string_bytes[@intFromEnum(int.start)..][0..byte_count]; 5462 5463 // TODO: this allocation and copy is only needed because the limbs may be unaligned. 5464 // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these 5465 // two lines can be removed. 5466 const limbs = try sema.arena.alloc(std.math.big.Limb, int.len); 5467 @memcpy(mem.sliceAsBytes(limbs), limb_bytes); 5468 5469 return Air.internedToRef((try sema.pt.intValue_big(.comptime_int, .{ 5470 .limbs = limbs, 5471 .positive = true, 5472 })).toIntern()); 5473 } 5474 5475 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5476 _ = block; 5477 const number = sema.code.instructions.items(.data)[@intFromEnum(inst)].float; 5478 return Air.internedToRef((try sema.pt.floatValue( 5479 .comptime_float, 5480 number, 5481 )).toIntern()); 5482 } 5483 5484 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5485 _ = block; 5486 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5487 const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data; 5488 const number = extra.get(); 5489 return Air.internedToRef((try sema.pt.floatValue(.comptime_float, number)).toIntern()); 5490 } 5491 5492 fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5493 const tracy = trace(@src()); 5494 defer tracy.end(); 5495 5496 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5497 const src = block.nodeOffset(inst_data.src_node); 5498 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 5499 const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .compile_error_string }); 5500 return sema.fail(block, src, "{s}", .{msg}); 5501 } 5502 5503 fn zirCompileLog( 5504 sema: *Sema, 5505 block: *Block, 5506 extended: Zir.Inst.Extended.InstData, 5507 ) CompileError!Air.Inst.Ref { 5508 const pt = sema.pt; 5509 const zcu = pt.zcu; 5510 const gpa = zcu.gpa; 5511 5512 var aw: std.Io.Writer.Allocating = .init(gpa); 5513 defer aw.deinit(); 5514 const writer = &aw.writer; 5515 5516 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 5517 const src_node = extra.data.src_node; 5518 const args = sema.code.refSlice(extra.end, extended.small); 5519 5520 for (args, 0..) |arg_ref, i| { 5521 if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory; 5522 5523 const arg = try sema.resolveInst(arg_ref); 5524 const arg_ty = sema.typeOf(arg); 5525 if (try sema.resolveValueResolveLazy(arg)) |val| { 5526 writer.print("@as({f}, {f})", .{ 5527 arg_ty.fmt(pt), val.fmtValueSema(pt, sema), 5528 }) catch return error.OutOfMemory; 5529 } else { 5530 writer.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory; 5531 } 5532 } 5533 5534 const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.written(), .no_embedded_nulls); 5535 5536 const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: { 5537 zcu.compile_log_lines.items[@intFromEnum(idx)] = .{ 5538 .next = .none, 5539 .data = line_data, 5540 }; 5541 break :idx idx; 5542 } else idx: { 5543 try zcu.compile_log_lines.append(gpa, .{ 5544 .next = .none, 5545 .data = line_data, 5546 }); 5547 break :idx @enumFromInt(zcu.compile_log_lines.items.len - 1); 5548 }; 5549 5550 const gop = try zcu.compile_logs.getOrPut(gpa, sema.owner); 5551 if (gop.found_existing) { 5552 const prev_line = gop.value_ptr.last_line.get(zcu); 5553 assert(prev_line.next == .none); 5554 prev_line.next = line_idx.toOptional(); 5555 gop.value_ptr.last_line = line_idx; 5556 } else { 5557 gop.value_ptr.* = .{ 5558 .base_node_inst = block.src_base_inst, 5559 .node_offset = src_node, 5560 .first_line = line_idx, 5561 .last_line = line_idx, 5562 }; 5563 } 5564 return .void_value; 5565 } 5566 5567 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5568 const pt = sema.pt; 5569 const zcu = pt.zcu; 5570 5571 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5572 const src = block.nodeOffset(inst_data.src_node); 5573 const msg_inst = try sema.resolveInst(inst_data.operand); 5574 5575 const coerced_msg = try sema.coerce(block, .slice_const_u8, msg_inst, block.builtinCallArgSrc(inst_data.src_node, 0)); 5576 5577 if (block.isComptime()) { 5578 return sema.fail(block, src, "encountered @panic at comptime", .{}); 5579 } 5580 5581 // We only apply the first hint in a branch. 5582 // This allows user-provided hints to override implicit cold hints. 5583 if (sema.branch_hint == null) { 5584 sema.branch_hint = .cold; 5585 } 5586 5587 if (!zcu.backendSupportsFeature(.panic_fn)) { 5588 _ = try block.addNoOp(.trap); 5589 return; 5590 } 5591 5592 try sema.ensureMemoizedStateResolved(src, .panic); 5593 const panic_fn_index = zcu.builtin_decl_values.get(.@"panic.call"); 5594 const opt_usize_ty = try pt.optionalType(.usize_type); 5595 const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{ 5596 .ty = opt_usize_ty.toIntern(), 5597 .val = .none, 5598 } }))); 5599 // `callBuiltin` also calls `addReferenceEntry` to the function body for us. 5600 try sema.callBuiltin( 5601 block, 5602 src, 5603 .fromIntern(panic_fn_index), 5604 .auto, 5605 &.{ coerced_msg, null_ret_addr }, 5606 .@"@panic", 5607 ); 5608 } 5609 5610 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5611 const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node; 5612 const src = block.nodeOffset(src_node); 5613 if (block.isComptime()) 5614 return sema.fail(block, src, "encountered @trap at comptime", .{}); 5615 _ = try block.addNoOp(.trap); 5616 } 5617 5618 fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5619 const tracy = trace(@src()); 5620 defer tracy.end(); 5621 5622 const pt = sema.pt; 5623 const zcu = pt.zcu; 5624 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5625 const src = parent_block.nodeOffset(inst_data.src_node); 5626 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 5627 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5628 const gpa = sema.gpa; 5629 5630 // AIR expects a block outside the loop block too. 5631 // Reserve space for a Loop instruction so that generated Break instructions can 5632 // point to it, even if it doesn't end up getting used because the code ends up being 5633 // comptime evaluated. 5634 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 5635 const loop_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1); 5636 try sema.air_instructions.ensureUnusedCapacity(gpa, 2); 5637 sema.air_instructions.appendAssumeCapacity(.{ 5638 .tag = .block, 5639 .data = undefined, 5640 }); 5641 sema.air_instructions.appendAssumeCapacity(.{ 5642 .tag = .loop, 5643 .data = .{ .ty_pl = .{ 5644 .ty = .noreturn_type, 5645 .payload = undefined, 5646 } }, 5647 }); 5648 var label: Block.Label = .{ 5649 .zir_block = inst, 5650 .merges = .{ 5651 .src_locs = .{}, 5652 .results = .{}, 5653 .br_list = .{}, 5654 .block_inst = block_inst, 5655 }, 5656 }; 5657 var child_block = parent_block.makeSubBlock(); 5658 child_block.label = &label; 5659 child_block.runtime_cond = null; 5660 child_block.runtime_loop = src; 5661 child_block.runtime_index.increment(); 5662 const merges = &child_block.label.?.merges; 5663 5664 defer child_block.instructions.deinit(gpa); 5665 defer merges.deinit(gpa); 5666 5667 var loop_block = child_block.makeSubBlock(); 5668 defer loop_block.instructions.deinit(gpa); 5669 5670 // Use `analyzeBodyInner` directly to push any comptime control flow up the stack. 5671 try sema.analyzeBodyInner(&loop_block, body); 5672 5673 // TODO: since AIR has `repeat` now, we could change ZIR to generate 5674 // more optimal code utilizing `repeat` instructions across blocks! 5675 // For now, if the generated loop body does not terminate `noreturn`, 5676 // then `analyzeBodyInner` is signalling that it ended with `repeat`. 5677 5678 const loop_block_len = loop_block.instructions.items.len; 5679 if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(zcu)) { 5680 // If the loop ended with a noreturn terminator, then there is no way for it to loop, 5681 // so we can just use the block instead. 5682 try child_block.instructions.appendSlice(gpa, loop_block.instructions.items); 5683 } else { 5684 _ = try loop_block.addInst(.{ 5685 .tag = .repeat, 5686 .data = .{ .repeat = .{ 5687 .loop_inst = loop_inst, 5688 } }, 5689 }); 5690 // Note that `loop_block_len` is now off by one. 5691 5692 try child_block.instructions.append(gpa, loop_inst); 5693 5694 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + loop_block_len + 1); 5695 sema.air_instructions.items(.data)[@intFromEnum(loop_inst)].ty_pl.payload = sema.addExtraAssumeCapacity( 5696 Air.Block{ .body_len = @intCast(loop_block_len + 1) }, 5697 ); 5698 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items)); 5699 } 5700 return sema.resolveAnalyzedBlock(parent_block, src, &child_block, merges, false); 5701 } 5702 5703 fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5704 const tracy = trace(@src()); 5705 defer tracy.end(); 5706 5707 const pt = sema.pt; 5708 const zcu = pt.zcu; 5709 const comp = zcu.comp; 5710 const gpa = sema.gpa; 5711 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5712 const src = parent_block.nodeOffset(pl_node.src_node); 5713 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 5714 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5715 5716 // we check this here to avoid undefined symbols 5717 if (!build_options.have_llvm) 5718 return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{}); 5719 5720 var c_import_buf = std.array_list.Managed(u8).init(gpa); 5721 defer c_import_buf.deinit(); 5722 5723 var child_block: Block = .{ 5724 .parent = parent_block, 5725 .sema = sema, 5726 .namespace = parent_block.namespace, 5727 .instructions = .{}, 5728 .inlining = parent_block.inlining, 5729 .comptime_reason = .{ .reason = .{ 5730 .src = src, 5731 .r = .{ .simple = .operand_cImport }, 5732 } }, 5733 .c_import_buf = &c_import_buf, 5734 .runtime_cond = parent_block.runtime_cond, 5735 .runtime_loop = parent_block.runtime_loop, 5736 .runtime_index = parent_block.runtime_index, 5737 .src_base_inst = parent_block.src_base_inst, 5738 .type_name_ctx = parent_block.type_name_ctx, 5739 }; 5740 defer child_block.instructions.deinit(gpa); 5741 5742 _ = try sema.analyzeInlineBody(&child_block, body, inst); 5743 5744 var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err| 5745 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5746 defer c_import_res.deinit(gpa); 5747 5748 if (c_import_res.errors.errorMessageCount() != 0) { 5749 const msg = msg: { 5750 const msg = try sema.errMsg(src, "C import failed", .{}); 5751 errdefer msg.destroy(gpa); 5752 5753 if (!comp.config.link_libc) 5754 try sema.errNote(src, msg, "libc headers not available; compilation does not link against libc", .{}); 5755 5756 const gop = try zcu.cimport_errors.getOrPut(gpa, sema.owner); 5757 if (!gop.found_existing) { 5758 gop.value_ptr.* = c_import_res.errors; 5759 c_import_res.errors = std.zig.ErrorBundle.empty; 5760 } 5761 break :msg msg; 5762 }; 5763 return sema.failWithOwnedErrorMsg(&child_block, msg); 5764 } 5765 const parent_mod = parent_block.ownerModule(); 5766 const digest = Cache.binToHex(c_import_res.digest); 5767 5768 const new_file_index = file: { 5769 const c_import_zig_path = try comp.arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ digest); 5770 const c_import_mod = Package.Module.create(comp.arena, .{ 5771 .paths = .{ 5772 .root = try .fromRoot(comp.arena, comp.dirs, .local_cache, c_import_zig_path), 5773 .root_src_path = "cimport.zig", 5774 }, 5775 .fully_qualified_name = c_import_zig_path, 5776 .cc_argv = parent_mod.cc_argv, 5777 .inherited = .{}, 5778 .global = comp.config, 5779 .parent = parent_mod, 5780 }) catch |err| switch (err) { 5781 error.OutOfMemory => |e| return e, 5782 // None of these are possible because we are creating a package with 5783 // the exact same configuration as the parent package, which already 5784 // passed these checks. 5785 error.ValgrindUnsupportedOnTarget => unreachable, 5786 error.TargetRequiresSingleThreaded => unreachable, 5787 error.BackendRequiresSingleThreaded => unreachable, 5788 error.TargetRequiresPic => unreachable, 5789 error.PieRequiresPic => unreachable, 5790 error.DynamicLinkingRequiresPic => unreachable, 5791 error.TargetHasNoRedZone => unreachable, 5792 error.StackCheckUnsupportedByTarget => unreachable, 5793 error.StackProtectorUnsupportedByTarget => unreachable, 5794 error.StackProtectorUnavailableWithoutLibC => unreachable, 5795 }; 5796 const c_import_file_path: Compilation.Path = try c_import_mod.root.join(gpa, comp.dirs, "cimport.zig"); 5797 errdefer c_import_file_path.deinit(gpa); 5798 const c_import_file = try gpa.create(Zcu.File); 5799 errdefer gpa.destroy(c_import_file); 5800 const c_import_file_index = try zcu.intern_pool.createFile(gpa, pt.tid, .{ 5801 .bin_digest = c_import_file_path.digest(), 5802 .file = c_import_file, 5803 .root_type = .none, 5804 }); 5805 c_import_file.* = .{ 5806 .status = .never_loaded, 5807 .stat = undefined, 5808 .is_builtin = false, 5809 .path = c_import_file_path, 5810 .source = null, 5811 .tree = null, 5812 .zir = null, 5813 .zoir = null, 5814 .mod = c_import_mod, 5815 .sub_file_path = "cimport.zig", 5816 .module_changed = false, 5817 .prev_zir = null, 5818 .zoir_invalidated = false, 5819 }; 5820 break :file c_import_file_index; 5821 }; 5822 pt.updateFile(new_file_index, zcu.fileByIndex(new_file_index)) catch |err| 5823 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5824 5825 try pt.ensureFileAnalyzed(new_file_index); 5826 const ty = zcu.fileRootType(new_file_index); 5827 try sema.declareDependency(.{ .interned = ty }); 5828 try sema.addTypeReferenceEntry(src, ty); 5829 return Air.internedToRef(ty); 5830 } 5831 5832 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5833 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5834 const src = parent_block.nodeOffset(inst_data.src_node); 5835 return sema.failWithUseOfAsync(parent_block, src); 5836 } 5837 5838 fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5839 const tracy = trace(@src()); 5840 defer tracy.end(); 5841 5842 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5843 const src = parent_block.nodeOffset(pl_node.src_node); 5844 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 5845 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5846 const gpa = sema.gpa; 5847 5848 // Reserve space for a Block instruction so that generated Break instructions can 5849 // point to it, even if it doesn't end up getting used because the code ends up being 5850 // comptime evaluated or is an unlabeled block. 5851 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 5852 try sema.air_instructions.append(gpa, .{ 5853 .tag = .block, 5854 .data = undefined, 5855 }); 5856 5857 var label: Block.Label = .{ 5858 .zir_block = inst, 5859 .merges = .{ 5860 .src_locs = .{}, 5861 .results = .{}, 5862 .br_list = .{}, 5863 .block_inst = block_inst, 5864 }, 5865 }; 5866 5867 var child_block: Block = .{ 5868 .parent = parent_block, 5869 .sema = sema, 5870 .namespace = parent_block.namespace, 5871 .instructions = .{}, 5872 .label = &label, 5873 .inlining = parent_block.inlining, 5874 .comptime_reason = parent_block.comptime_reason, 5875 .is_typeof = parent_block.is_typeof, 5876 .want_safety = parent_block.want_safety, 5877 .float_mode = parent_block.float_mode, 5878 .c_import_buf = parent_block.c_import_buf, 5879 .runtime_cond = parent_block.runtime_cond, 5880 .runtime_loop = parent_block.runtime_loop, 5881 .runtime_index = parent_block.runtime_index, 5882 .error_return_trace_index = parent_block.error_return_trace_index, 5883 .src_base_inst = parent_block.src_base_inst, 5884 .type_name_ctx = parent_block.type_name_ctx, 5885 }; 5886 5887 defer child_block.instructions.deinit(gpa); 5888 defer label.merges.deinit(gpa); 5889 5890 return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges); 5891 } 5892 5893 /// Semantically analyze the given ZIR body, emitting any resulting runtime code into the AIR block 5894 /// specified by `child_block` if necessary (and emitting this block into `parent_block`). 5895 /// TODO: `merges` is known from `child_block`, remove this parameter. 5896 fn resolveBlockBody( 5897 sema: *Sema, 5898 parent_block: *Block, 5899 src: LazySrcLoc, 5900 child_block: *Block, 5901 body: []const Zir.Inst.Index, 5902 /// This is the instruction that a break instruction within `body` can 5903 /// use to return from the body. 5904 body_inst: Zir.Inst.Index, 5905 merges: *Block.Merges, 5906 ) CompileError!Air.Inst.Ref { 5907 if (child_block.isComptime()) { 5908 return sema.resolveInlineBody(child_block, body, body_inst); 5909 } else { 5910 assert(sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)] == .block); 5911 var need_debug_scope = false; 5912 child_block.need_debug_scope = &need_debug_scope; 5913 if (sema.analyzeBodyInner(child_block, body)) |_| { 5914 return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope); 5915 } else |err| switch (err) { 5916 error.ComptimeBreak => { 5917 // Comptime control flow is happening, however child_block may still contain 5918 // runtime instructions which need to be copied to the parent block. 5919 if (need_debug_scope and child_block.instructions.items.len > 0) { 5920 // We need a runtime block for scoping reasons. 5921 _ = try child_block.addBr(merges.block_inst, .void_value); 5922 try parent_block.instructions.append(sema.gpa, merges.block_inst); 5923 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).@"struct".fields.len + 5924 child_block.instructions.items.len); 5925 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 5926 .ty = .void_type, 5927 .payload = sema.addExtraAssumeCapacity(Air.Block{ 5928 .body_len = @intCast(child_block.instructions.items.len), 5929 }), 5930 } }; 5931 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 5932 } else { 5933 // We can copy instructions directly to the parent block. 5934 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items); 5935 } 5936 5937 const break_inst = sema.comptime_break_inst; 5938 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; 5939 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 5940 if (extra.block_inst == body_inst) { 5941 return try sema.resolveInst(break_data.operand); 5942 } else { 5943 return error.ComptimeBreak; 5944 } 5945 }, 5946 else => |e| return e, 5947 } 5948 } 5949 } 5950 5951 /// After a body corresponding to an AIR `block` has been analyzed, this function places them into 5952 /// the block pointed at by `merges.block_inst` if necessary, or the block may be elided in favor of 5953 /// inlining the instructions directly into the parent block. Either way, it considers all merges of 5954 /// this block, and combines them appropriately using peer type resolution, returning the final 5955 /// value of the block. 5956 fn resolveAnalyzedBlock( 5957 sema: *Sema, 5958 parent_block: *Block, 5959 src: LazySrcLoc, 5960 child_block: *Block, 5961 merges: *Block.Merges, 5962 need_debug_scope: bool, 5963 ) CompileError!Air.Inst.Ref { 5964 const tracy = trace(@src()); 5965 defer tracy.end(); 5966 5967 const gpa = sema.gpa; 5968 const pt = sema.pt; 5969 const zcu = pt.zcu; 5970 5971 // Blocks must terminate with noreturn instruction. 5972 assert(child_block.instructions.items.len != 0); 5973 assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(zcu)); 5974 5975 const block_tag = sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)]; 5976 switch (block_tag) { 5977 .block => {}, 5978 .dbg_inline_block => assert(need_debug_scope), 5979 else => unreachable, 5980 } 5981 if (merges.results.items.len == 0) { 5982 switch (block_tag) { 5983 .block => { 5984 // No need for a block instruction. We can put the new instructions 5985 // directly into the parent block. 5986 if (need_debug_scope) { 5987 // The code following this block is unreachable, as the block has no 5988 // merges, so we don't necessarily need to emit this as an AIR block. 5989 // However, we need a block *somewhere* to make the scoping correct, 5990 // so forward this request to the parent block. 5991 if (parent_block.need_debug_scope) |ptr| ptr.* = true; 5992 } 5993 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items); 5994 return child_block.instructions.items[child_block.instructions.items.len - 1].toRef(); 5995 }, 5996 .dbg_inline_block => { 5997 // Create a block containing all instruction from the body. 5998 try parent_block.instructions.append(gpa, merges.block_inst); 5999 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len + 6000 child_block.instructions.items.len); 6001 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 6002 .ty = .noreturn_type, 6003 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ 6004 .func = child_block.inlining.?.func, 6005 .body_len = @intCast(child_block.instructions.items.len), 6006 }), 6007 } }; 6008 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 6009 return merges.block_inst.toRef(); 6010 }, 6011 else => unreachable, 6012 } 6013 } 6014 if (merges.results.items.len == 1) { 6015 // If the `break` is trailing, we may be able to elide the AIR block here 6016 // by appending the new instructions directly to the parent block. 6017 if (!need_debug_scope) { 6018 const last_inst_index = child_block.instructions.items.len - 1; 6019 const last_inst = child_block.instructions.items[last_inst_index]; 6020 if (sema.getBreakBlock(last_inst)) |br_block| { 6021 if (br_block == merges.block_inst) { 6022 // Great, the last instruction is the break! Put the instructions 6023 // directly into the parent block. 6024 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items[0..last_inst_index]); 6025 return merges.results.items[0]; 6026 } 6027 } 6028 } 6029 // Okay, we need a runtime block. If the value is comptime-known, the 6030 // block should just return void, and we return the merge result 6031 // directly. Otherwise, we can defer to the logic below. 6032 if (try sema.resolveValue(merges.results.items[0])) |result_val| { 6033 // Create a block containing all instruction from the body. 6034 try parent_block.instructions.append(gpa, merges.block_inst); 6035 switch (block_tag) { 6036 .block => { 6037 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 6038 child_block.instructions.items.len); 6039 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 6040 .ty = .void_type, 6041 .payload = sema.addExtraAssumeCapacity(Air.Block{ 6042 .body_len = @intCast(child_block.instructions.items.len), 6043 }), 6044 } }; 6045 }, 6046 .dbg_inline_block => { 6047 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len + 6048 child_block.instructions.items.len); 6049 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 6050 .ty = .void_type, 6051 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ 6052 .func = child_block.inlining.?.func, 6053 .body_len = @intCast(child_block.instructions.items.len), 6054 }), 6055 } }; 6056 }, 6057 else => unreachable, 6058 } 6059 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 6060 // Rewrite the break to just give value {}; the value is 6061 // comptime-known and will be returned directly. 6062 sema.air_instructions.items(.data)[@intFromEnum(merges.br_list.items[0])].br.operand = .void_value; 6063 return Air.internedToRef(result_val.toIntern()); 6064 } 6065 } 6066 // It is impossible to have the number of results be > 1 in a comptime scope. 6067 assert(!child_block.isComptime()); // Should already got a compile error in the condbr condition. 6068 6069 // Note that we'll always create an AIR block here, so `need_debug_scope` is irrelevant. 6070 6071 // Need to set the type and emit the Block instruction. This allows machine code generation 6072 // to emit a jump instruction to after the block when it encounters the break. 6073 try parent_block.instructions.append(gpa, merges.block_inst); 6074 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items }); 6075 // TODO add note "missing else causes void value" 6076 6077 const type_src = src; // TODO: better source location 6078 if (try resolved_ty.comptimeOnlySema(pt)) { 6079 const msg = msg: { 6080 const msg = try sema.errMsg(type_src, "value with comptime-only type '{f}' depends on runtime control flow", .{resolved_ty.fmt(pt)}); 6081 errdefer msg.destroy(sema.gpa); 6082 6083 const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?; 6084 try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); 6085 6086 try sema.explainWhyTypeIsComptime(msg, type_src, resolved_ty); 6087 6088 break :msg msg; 6089 }; 6090 return sema.failWithOwnedErrorMsg(child_block, msg); 6091 } 6092 for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| { 6093 try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst); 6094 } 6095 6096 try sema.checkMergeAllowed(child_block, type_src, resolved_ty); 6097 6098 const ty_inst = Air.internedToRef(resolved_ty.toIntern()); 6099 switch (block_tag) { 6100 .block => { 6101 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 6102 child_block.instructions.items.len); 6103 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 6104 .ty = ty_inst, 6105 .payload = sema.addExtraAssumeCapacity(Air.Block{ 6106 .body_len = @intCast(child_block.instructions.items.len), 6107 }), 6108 } }; 6109 }, 6110 .dbg_inline_block => { 6111 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len + 6112 child_block.instructions.items.len); 6113 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 6114 .ty = ty_inst, 6115 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ 6116 .func = child_block.inlining.?.func, 6117 .body_len = @intCast(child_block.instructions.items.len), 6118 }), 6119 } }; 6120 }, 6121 else => unreachable, 6122 } 6123 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 6124 // Now that the block has its type resolved, we need to go back into all the break 6125 // instructions, and insert type coercion on the operands. 6126 for (merges.br_list.items) |br| { 6127 const br_operand = sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand; 6128 const br_operand_src = src; 6129 const br_operand_ty = sema.typeOf(br_operand); 6130 if (br_operand_ty.eql(resolved_ty, zcu)) { 6131 // No type coercion needed. 6132 continue; 6133 } 6134 var coerce_block = parent_block.makeSubBlock(); 6135 defer coerce_block.instructions.deinit(gpa); 6136 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src); 6137 // If no instructions were produced, such as in the case of a coercion of a 6138 // constant value to a new type, we can simply point the br operand to it. 6139 if (coerce_block.instructions.items.len == 0) { 6140 sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand = coerced_operand; 6141 continue; 6142 } 6143 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1].toRef() == coerced_operand); 6144 6145 // Convert the br instruction to a block instruction that has the coercion 6146 // and then a new br inside that returns the coerced instruction. 6147 const sub_block_len: u32 = @intCast(coerce_block.instructions.items.len + 1); 6148 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 6149 sub_block_len); 6150 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 6151 const sub_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 6152 6153 sema.air_instructions.items(.tag)[@intFromEnum(br)] = .block; 6154 sema.air_instructions.items(.data)[@intFromEnum(br)] = .{ .ty_pl = .{ 6155 .ty = .noreturn_type, 6156 .payload = sema.addExtraAssumeCapacity(Air.Block{ 6157 .body_len = sub_block_len, 6158 }), 6159 } }; 6160 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); 6161 sema.air_extra.appendAssumeCapacity(@intFromEnum(sub_br_inst)); 6162 6163 sema.air_instructions.appendAssumeCapacity(.{ 6164 .tag = .br, 6165 .data = .{ .br = .{ 6166 .block_inst = merges.block_inst, 6167 .operand = coerced_operand, 6168 } }, 6169 }); 6170 } 6171 6172 if (try sema.typeHasOnePossibleValue(resolved_ty)) |block_only_value| { 6173 return Air.internedToRef(block_only_value.toIntern()); 6174 } 6175 6176 return merges.block_inst.toRef(); 6177 } 6178 6179 fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6180 const tracy = trace(@src()); 6181 defer tracy.end(); 6182 6183 const pt = sema.pt; 6184 const zcu = pt.zcu; 6185 const ip = &zcu.intern_pool; 6186 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 6187 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; 6188 6189 const src = block.nodeOffset(inst_data.src_node); 6190 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0); 6191 const options_src = block.builtinCallArgSrc(inst_data.src_node, 1); 6192 6193 const ptr = try sema.resolveInst(extra.exported); 6194 const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{ .simple = .export_target }); 6195 const ptr_ty = ptr_val.typeOf(zcu); 6196 6197 const options = try sema.resolveExportOptions(block, options_src, extra.options); 6198 6199 { 6200 if (ptr_ty.zigTypeTag(zcu) != .pointer) { 6201 return sema.fail(block, ptr_src, "expected pointer type, found '{f}'", .{ptr_ty.fmt(pt)}); 6202 } 6203 const ptr_ty_info = ptr_ty.ptrInfo(zcu); 6204 if (ptr_ty_info.flags.size == .slice) { 6205 return sema.fail(block, ptr_src, "export target cannot be slice", .{}); 6206 } 6207 if (ptr_ty_info.packed_offset.host_size != 0) { 6208 return sema.fail(block, ptr_src, "export target cannot be bit-pointer", .{}); 6209 } 6210 } 6211 6212 const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr; 6213 switch (ptr_info.base_addr) { 6214 .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}), 6215 .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}), 6216 .uav => |uav| { 6217 if (ptr_info.byte_offset != 0) { 6218 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); 6219 } 6220 if (options.linkage == .internal) return; 6221 const export_ty = Value.fromInterned(uav.val).typeOf(zcu); 6222 if (!try sema.validateExternType(export_ty, .other)) { 6223 return sema.failWithOwnedErrorMsg(block, msg: { 6224 const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); 6225 errdefer msg.destroy(sema.gpa); 6226 try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); 6227 try sema.addDeclaredHereNote(msg, export_ty); 6228 break :msg msg; 6229 }); 6230 } 6231 try sema.exports.append(zcu.gpa, .{ 6232 .opts = options, 6233 .src = src, 6234 .exported = .{ .uav = uav.val }, 6235 .status = .in_progress, 6236 }); 6237 }, 6238 .nav => |nav| { 6239 if (ptr_info.byte_offset != 0) { 6240 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); 6241 } 6242 try sema.analyzeExport(block, src, options, nav); 6243 }, 6244 } 6245 } 6246 6247 pub fn analyzeExport( 6248 sema: *Sema, 6249 block: *Block, 6250 src: LazySrcLoc, 6251 options: Zcu.Export.Options, 6252 orig_nav_index: InternPool.Nav.Index, 6253 ) !void { 6254 const gpa = sema.gpa; 6255 const pt = sema.pt; 6256 const zcu = pt.zcu; 6257 const ip = &zcu.intern_pool; 6258 6259 if (options.linkage == .internal) 6260 return; 6261 6262 try sema.ensureNavResolved(block, src, orig_nav_index, .fully); 6263 6264 const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) { 6265 .variable => |v| v.owner_nav, 6266 .@"extern" => |e| e.owner_nav, 6267 .func => |f| f.owner_nav, 6268 else => orig_nav_index, 6269 }; 6270 6271 const exported_nav = ip.getNav(exported_nav_index); 6272 const export_ty: Type = .fromInterned(exported_nav.typeOf(ip)); 6273 6274 if (!try sema.validateExternType(export_ty, .other)) { 6275 return sema.failWithOwnedErrorMsg(block, msg: { 6276 const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); 6277 errdefer msg.destroy(gpa); 6278 6279 try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); 6280 6281 try sema.addDeclaredHereNote(msg, export_ty); 6282 break :msg msg; 6283 }); 6284 } 6285 6286 // TODO: some backends might support re-exporting extern decls 6287 if (exported_nav.getExtern(ip) != null) { 6288 return sema.fail(block, src, "export target cannot be extern", .{}); 6289 } 6290 6291 try sema.maybeQueueFuncBodyAnalysis(block, src, exported_nav_index); 6292 6293 try sema.exports.append(gpa, .{ 6294 .opts = options, 6295 .src = src, 6296 .exported = .{ .nav = exported_nav_index }, 6297 .status = .in_progress, 6298 }); 6299 } 6300 6301 fn zirDisableInstrumentation(sema: *Sema) CompileError!void { 6302 const pt = sema.pt; 6303 const zcu = pt.zcu; 6304 const ip = &zcu.intern_pool; 6305 const func = switch (sema.owner.unwrap()) { 6306 .func => |func| func, 6307 .@"comptime", 6308 .nav_val, 6309 .nav_ty, 6310 .type, 6311 .memoized_state, 6312 => return, // does nothing outside a function 6313 }; 6314 ip.funcSetDisableInstrumentation(func); 6315 sema.allow_memoize = false; 6316 } 6317 6318 fn zirDisableIntrinsics(sema: *Sema) CompileError!void { 6319 const pt = sema.pt; 6320 const zcu = pt.zcu; 6321 const ip = &zcu.intern_pool; 6322 const func = switch (sema.owner.unwrap()) { 6323 .func => |func| func, 6324 .@"comptime", 6325 .nav_val, 6326 .nav_ty, 6327 .type, 6328 .memoized_state, 6329 => return, // does nothing outside a function 6330 }; 6331 ip.funcSetDisableIntrinsics(func); 6332 sema.allow_memoize = false; 6333 } 6334 6335 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 6336 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 6337 const src = block.builtinCallArgSrc(extra.node, 0); 6338 block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, .FloatMode, .{ .simple = .operand_setFloatMode }); 6339 } 6340 6341 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6342 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 6343 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 6344 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{ .simple = .operand_setRuntimeSafety }); 6345 } 6346 6347 fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { 6348 const tracy = trace(@src()); 6349 defer tracy.end(); 6350 6351 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break"; 6352 const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; 6353 const operand = try sema.resolveInst(inst_data.operand); 6354 const zir_block = extra.block_inst; 6355 6356 var block = start_block; 6357 while (true) { 6358 if (block.label) |label| { 6359 if (label.zir_block == zir_block) { 6360 const br_ref = try start_block.addBr(label.merges.block_inst, operand); 6361 const src_loc = if (extra.operand_src_node.unwrap()) |operand_src_node| 6362 start_block.nodeOffset(operand_src_node) 6363 else 6364 null; 6365 try label.merges.src_locs.append(sema.gpa, src_loc); 6366 try label.merges.results.append(sema.gpa, operand); 6367 try label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); 6368 block.runtime_index.increment(); 6369 if (block.runtime_cond == null and block.runtime_loop == null) { 6370 block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; 6371 block.runtime_loop = start_block.runtime_loop; 6372 } 6373 return; 6374 } 6375 } 6376 block = block.parent.?; 6377 } 6378 } 6379 6380 fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { 6381 const tracy = trace(@src()); 6382 defer tracy.end(); 6383 6384 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break"; 6385 const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; 6386 const operand_src = start_block.nodeOffset(extra.operand_src_node.unwrap().?); 6387 const uncoerced_operand = try sema.resolveInst(inst_data.operand); 6388 const switch_inst = extra.block_inst; 6389 6390 switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) { 6391 .switch_block, .switch_block_ref => {}, 6392 else => unreachable, // assertion failure 6393 } 6394 6395 const switch_payload_index = sema.code.instructions.items(.data)[@intFromEnum(switch_inst)].pl_node.payload_index; 6396 const switch_operand_ref = sema.code.extraData(Zir.Inst.SwitchBlock, switch_payload_index).data.operand; 6397 const switch_operand_ty = sema.typeOf(try sema.resolveInst(switch_operand_ref)); 6398 6399 const operand = try sema.coerce(start_block, switch_operand_ty, uncoerced_operand, operand_src); 6400 6401 try sema.validateRuntimeValue(start_block, operand_src, operand); 6402 6403 // We want to generate a `switch_dispatch` instruction with the switch condition, 6404 // possibly preceded by a store to the stack alloc containing the raw operand. 6405 // However, to avoid too much special-case state in Sema, this is handled by the 6406 // `switch` lowering logic. As such, we will find the `Block` corresponding to the 6407 // parent `switch_block[_ref]` instruction, create a dummy `br`, and add a merge 6408 // to signal to the switch logic to rewrite this into an appropriate dispatch. 6409 6410 var block = start_block; 6411 while (true) { 6412 if (block.label) |label| { 6413 if (label.zir_block == switch_inst) { 6414 const br_ref = try start_block.addBr(label.merges.block_inst, operand); 6415 try label.merges.extra_insts.append(sema.gpa, br_ref.toIndex().?); 6416 try label.merges.extra_src_locs.append(sema.gpa, operand_src); 6417 block.runtime_index.increment(); 6418 if (block.runtime_cond == null and block.runtime_loop == null) { 6419 block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; 6420 block.runtime_loop = start_block.runtime_loop; 6421 } 6422 return; 6423 } 6424 } 6425 block = block.parent.?; 6426 } 6427 } 6428 6429 fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6430 if (block.isComptime() or block.ownerModule().strip) return; 6431 6432 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; 6433 6434 if (block.instructions.items.len != 0) { 6435 const idx = block.instructions.items[block.instructions.items.len - 1]; 6436 if (sema.air_instructions.items(.tag)[@intFromEnum(idx)] == .dbg_stmt) { 6437 // The previous dbg_stmt didn't correspond to any actual code, so replace it. 6438 sema.air_instructions.items(.data)[@intFromEnum(idx)].dbg_stmt = .{ 6439 .line = inst_data.line, 6440 .column = inst_data.column, 6441 }; 6442 return; 6443 } 6444 } 6445 6446 _ = try block.addInst(.{ 6447 .tag = .dbg_stmt, 6448 .data = .{ .dbg_stmt = .{ 6449 .line = inst_data.line, 6450 .column = inst_data.column, 6451 } }, 6452 }); 6453 } 6454 6455 fn zirDbgEmptyStmt(_: *Sema, block: *Block, _: Zir.Inst.Index) CompileError!void { 6456 if (block.isComptime() or block.ownerModule().strip) return; 6457 _ = try block.addNoOp(.dbg_empty_stmt); 6458 } 6459 6460 fn zirDbgVar( 6461 sema: *Sema, 6462 block: *Block, 6463 inst: Zir.Inst.Index, 6464 air_tag: Air.Inst.Tag, 6465 ) CompileError!void { 6466 const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op; 6467 const operand = try sema.resolveInst(str_op.operand); 6468 const name = str_op.getStr(sema.code); 6469 try sema.addDbgVar(block, operand, air_tag, name); 6470 } 6471 6472 fn addDbgVar( 6473 sema: *Sema, 6474 block: *Block, 6475 operand: Air.Inst.Ref, 6476 air_tag: Air.Inst.Tag, 6477 name: []const u8, 6478 ) CompileError!void { 6479 if (block.isComptime() or block.ownerModule().strip) return; 6480 6481 const pt = sema.pt; 6482 const zcu = pt.zcu; 6483 const operand_ty = sema.typeOf(operand); 6484 const val_ty = switch (air_tag) { 6485 .dbg_var_ptr => operand_ty.childType(zcu), 6486 .dbg_var_val, .dbg_arg_inline => operand_ty, 6487 else => unreachable, 6488 }; 6489 if (try val_ty.comptimeOnlySema(pt)) return; 6490 if (!(try val_ty.hasRuntimeBitsSema(pt))) return; 6491 if (try sema.resolveValue(operand)) |operand_val| { 6492 if (operand_val.canMutateComptimeVarState(zcu)) return; 6493 } 6494 6495 // To ensure the lexical scoping is known to backends, this alloc must be 6496 // within a real runtime block. We set a flag which communicates information 6497 // to the closest lexically enclosing block: 6498 // * If it is a `block_inline`, communicates to logic in `analyzeBodyInner` 6499 // to create a post-hoc block. 6500 // * Otherwise, communicates to logic in `resolveBlockBody` to create a 6501 // real `block` instruction. 6502 if (block.need_debug_scope) |ptr| ptr.* = true; 6503 6504 // Add the name to the AIR. 6505 const name_nts = try sema.appendAirString(name); 6506 6507 _ = try block.addInst(.{ 6508 .tag = air_tag, 6509 .data = .{ .pl_op = .{ 6510 .payload = @intFromEnum(name_nts), 6511 .operand = operand, 6512 } }, 6513 }); 6514 } 6515 6516 pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTerminatedString { 6517 if (str.len == 0) return .none; 6518 const nts: Air.NullTerminatedString = @enumFromInt(sema.air_extra.items.len); 6519 const elements_used = str.len / 4 + 1; 6520 const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used); 6521 const buffer = mem.sliceAsBytes(elements); 6522 @memcpy(buffer[0..str.len], str); 6523 buffer[str.len] = 0; 6524 return nts; 6525 } 6526 6527 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 6528 const pt = sema.pt; 6529 const zcu = pt.zcu; 6530 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 6531 const src = block.tokenOffset(inst_data.src_tok); 6532 const decl_name = try zcu.intern_pool.getOrPutString( 6533 sema.gpa, 6534 pt.tid, 6535 inst_data.get(sema.code), 6536 .no_embedded_nulls, 6537 ); 6538 const nav_index = try sema.lookupIdentifier(block, decl_name); 6539 return sema.analyzeNavRef(block, src, nav_index); 6540 } 6541 6542 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 6543 const pt = sema.pt; 6544 const zcu = pt.zcu; 6545 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 6546 const src = block.tokenOffset(inst_data.src_tok); 6547 const decl_name = try zcu.intern_pool.getOrPutString( 6548 sema.gpa, 6549 pt.tid, 6550 inst_data.get(sema.code), 6551 .no_embedded_nulls, 6552 ); 6553 const nav = try sema.lookupIdentifier(block, decl_name); 6554 return sema.analyzeNavVal(block, src, nav); 6555 } 6556 6557 fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index { 6558 const pt = sema.pt; 6559 const zcu = pt.zcu; 6560 var namespace = block.namespace; 6561 while (true) { 6562 if (try sema.lookupInNamespace(block, namespace, name)) |lookup| { 6563 assert(lookup.accessible); 6564 return lookup.nav; 6565 } 6566 namespace = zcu.namespacePtr(namespace).parent.unwrap() orelse break; 6567 } 6568 unreachable; // AstGen detects use of undeclared identifiers. 6569 } 6570 6571 /// This looks up a member of a specific namespace. 6572 fn lookupInNamespace( 6573 sema: *Sema, 6574 block: *Block, 6575 namespace_index: InternPool.NamespaceIndex, 6576 ident_name: InternPool.NullTerminatedString, 6577 ) CompileError!?struct { 6578 nav: InternPool.Nav.Index, 6579 /// If `false`, the declaration is in a different file and is not `pub`. 6580 /// We still return the declaration for better error reporting. 6581 accessible: bool, 6582 } { 6583 const pt = sema.pt; 6584 const zcu = pt.zcu; 6585 6586 try pt.ensureNamespaceUpToDate(namespace_index); 6587 6588 const namespace = zcu.namespacePtr(namespace_index); 6589 6590 const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu }; 6591 6592 const src_file = zcu.namespacePtr(block.namespace).file_scope; 6593 6594 if (Type.fromInterned(namespace.owner_type).typeDeclInst(zcu)) |type_decl_inst| { 6595 try sema.declareDependency(.{ .namespace_name = .{ 6596 .namespace = type_decl_inst, 6597 .name = ident_name, 6598 } }); 6599 } 6600 6601 if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { 6602 return .{ 6603 .nav = nav_index, 6604 .accessible = true, 6605 }; 6606 } else if (namespace.priv_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { 6607 return .{ 6608 .nav = nav_index, 6609 .accessible = src_file == namespace.file_scope, 6610 }; 6611 } 6612 6613 return null; 6614 } 6615 6616 fn funcDeclSrcInst(sema: *Sema, func_inst: Air.Inst.Ref) !?InternPool.TrackedInst.Index { 6617 const pt = sema.pt; 6618 const zcu = pt.zcu; 6619 const ip = &zcu.intern_pool; 6620 const func_val = try sema.resolveValue(func_inst) orelse return null; 6621 if (func_val.isUndef(zcu)) return null; 6622 const nav = switch (ip.indexToKey(func_val.toIntern())) { 6623 .@"extern" => |e| e.owner_nav, 6624 .func => |f| f.owner_nav, 6625 .ptr => |ptr| switch (ptr.base_addr) { 6626 .nav => |nav| if (ptr.byte_offset == 0) nav else return null, 6627 else => return null, 6628 }, 6629 else => return null, 6630 }; 6631 return ip.getNav(nav).srcInst(ip); 6632 } 6633 6634 pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref { 6635 const pt = sema.pt; 6636 const zcu = pt.zcu; 6637 const gpa = sema.gpa; 6638 6639 if (block.isComptime() or block.is_typeof) { 6640 const index_val = try pt.intValue_u64(.usize, sema.comptime_err_ret_trace.items.len); 6641 return Air.internedToRef(index_val.toIntern()); 6642 } 6643 6644 if (!block.ownerModule().error_tracing) return .none; 6645 6646 const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); 6647 try stack_trace_ty.resolveFields(pt); 6648 const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); 6649 const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) { 6650 error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"), 6651 error.ComptimeReturn, error.ComptimeBreak => unreachable, 6652 error.OutOfMemory => |e| return e, 6653 }; 6654 6655 return try block.addInst(.{ 6656 .tag = .save_err_return_trace_index, 6657 .data = .{ .ty_pl = .{ 6658 .ty = Air.internedToRef(stack_trace_ty.toIntern()), 6659 .payload = @intCast(field_index), 6660 } }, 6661 }); 6662 } 6663 6664 /// Add instructions to block to "pop" the error return trace. 6665 /// If `operand` is provided, only pops if operand is non-error. 6666 fn popErrorReturnTrace( 6667 sema: *Sema, 6668 block: *Block, 6669 src: LazySrcLoc, 6670 operand: Air.Inst.Ref, 6671 saved_error_trace_index: Air.Inst.Ref, 6672 ) CompileError!void { 6673 const pt = sema.pt; 6674 const zcu = pt.zcu; 6675 const gpa = sema.gpa; 6676 var is_non_error: ?bool = null; 6677 var is_non_error_inst: Air.Inst.Ref = undefined; 6678 if (operand != .none) { 6679 is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand); 6680 if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val| 6681 is_non_error = cond_val.toBool(); 6682 } else is_non_error = true; // no operand means pop unconditionally 6683 6684 if (is_non_error == true) { 6685 // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or 6686 // the result is comptime-known to be a non-error. Either way, pop unconditionally. 6687 6688 const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); 6689 try stack_trace_ty.resolveFields(pt); 6690 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); 6691 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); 6692 const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); 6693 const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true); 6694 try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store); 6695 } else if (is_non_error == null) { 6696 // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need 6697 // to pop any error trace that may have been propagated from our arguments. 6698 6699 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len); 6700 const cond_block_inst = try block.addInstAsIndex(.{ 6701 .tag = .block, 6702 .data = .{ 6703 .ty_pl = .{ 6704 .ty = .void_type, 6705 .payload = undefined, // updated below 6706 }, 6707 }, 6708 }); 6709 6710 var then_block = block.makeSubBlock(); 6711 defer then_block.instructions.deinit(gpa); 6712 6713 // If non-error, then pop the error return trace by restoring the index. 6714 const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); 6715 try stack_trace_ty.resolveFields(pt); 6716 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); 6717 const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); 6718 const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); 6719 const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true); 6720 try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store); 6721 _ = try then_block.addBr(cond_block_inst, .void_value); 6722 6723 // Otherwise, do nothing 6724 var else_block = block.makeSubBlock(); 6725 defer else_block.instructions.deinit(gpa); 6726 _ = try else_block.addBr(cond_block_inst, .void_value); 6727 6728 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 6729 then_block.instructions.items.len + else_block.instructions.items.len + 6730 @typeInfo(Air.Block).@"struct".fields.len + 1); // +1 for the sole .cond_br instruction in the .block 6731 6732 const cond_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 6733 try sema.air_instructions.append(gpa, .{ 6734 .tag = .cond_br, 6735 .data = .{ 6736 .pl_op = .{ 6737 .operand = is_non_error_inst, 6738 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 6739 .then_body_len = @intCast(then_block.instructions.items.len), 6740 .else_body_len = @intCast(else_block.instructions.items.len), 6741 .branch_hints = .{ 6742 // Weight against error branch. 6743 .true = .likely, 6744 .false = .unlikely, 6745 // Code coverage is not valuable on either branch. 6746 .then_cov = .none, 6747 .else_cov = .none, 6748 }, 6749 }), 6750 }, 6751 }, 6752 }); 6753 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 6754 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 6755 6756 sema.air_instructions.items(.data)[@intFromEnum(cond_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); 6757 sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst)); 6758 } 6759 } 6760 6761 fn zirCall( 6762 sema: *Sema, 6763 block: *Block, 6764 inst: Zir.Inst.Index, 6765 comptime kind: enum { direct, field }, 6766 ) CompileError!Air.Inst.Ref { 6767 const tracy = trace(@src()); 6768 defer tracy.end(); 6769 6770 const pt = sema.pt; 6771 const zcu = pt.zcu; 6772 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 6773 const callee_src = block.src(.{ .node_offset_call_func = inst_data.src_node }); 6774 const call_src = block.nodeOffset(inst_data.src_node); 6775 const ExtraType = switch (kind) { 6776 .direct => Zir.Inst.Call, 6777 .field => Zir.Inst.FieldCall, 6778 }; 6779 const extra = sema.code.extraData(ExtraType, inst_data.payload_index); 6780 const args_len = extra.data.flags.args_len; 6781 6782 const modifier: std.builtin.CallModifier = @enumFromInt(extra.data.flags.packed_modifier); 6783 const ensure_result_used = extra.data.flags.ensure_result_used; 6784 const pop_error_return_trace = extra.data.flags.pop_error_return_trace; 6785 6786 const callee: ResolvedFieldCallee = switch (kind) { 6787 .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, 6788 .field => blk: { 6789 const object_ptr = try sema.resolveInst(extra.data.obj_ptr); 6790 const field_name = try zcu.intern_pool.getOrPutString( 6791 sema.gpa, 6792 pt.tid, 6793 sema.code.nullTerminatedString(extra.data.field_name_start), 6794 .no_embedded_nulls, 6795 ); 6796 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); 6797 break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); 6798 }, 6799 }; 6800 const func: Air.Inst.Ref = switch (callee) { 6801 .direct => |func_inst| func_inst, 6802 .method => |method| method.func_inst, 6803 }; 6804 6805 const callee_ty = sema.typeOf(func); 6806 const total_args = args_len + @intFromBool(callee == .method); 6807 const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method); 6808 6809 // The block index before the call, so we can potentially insert an error trace save here later. 6810 const block_index: Air.Inst.Index = @enumFromInt(block.instructions.items.len); 6811 6812 // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the 6813 // error trace potentially dirty). 6814 var input_is_error = false; 6815 6816 const args_info: CallArgsInfo = .{ .zir_call = .{ 6817 .bound_arg = switch (callee) { 6818 .direct => .none, 6819 .method => |method| method.arg0_inst, 6820 }, 6821 .bound_arg_src = callee_src, 6822 .call_inst = inst, 6823 .call_node_offset = inst_data.src_node, 6824 .num_args = args_len, 6825 .args_body = @ptrCast(sema.code.extra[extra.end..]), 6826 .any_arg_is_error = &input_is_error, 6827 } }; 6828 6829 // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction. 6830 const call_dbg_node: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); 6831 const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call); 6832 6833 if (block.ownerModule().error_tracing and 6834 !block.isComptime() and !block.is_typeof and (input_is_error or pop_error_return_trace)) 6835 { 6836 const return_ty = sema.typeOf(call_inst); 6837 if (modifier != .always_tail and return_ty.isNoReturn(zcu)) 6838 return call_inst; // call to "fn (...) noreturn", don't pop 6839 6840 // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it 6841 // *before* the recursive call. This will be a bit tricky to do and probably requires 6842 // moving this logic into analyzeCall. But that's probably a good idea anyway. 6843 if (modifier == .always_tail) 6844 return call_inst; 6845 6846 // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only 6847 // need to clean-up our own trace if we were passed to a non-error-handling expression. 6848 if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) { 6849 const stack_trace_ty = try sema.getBuiltinType(call_src, .StackTrace); 6850 try stack_trace_ty.resolveFields(pt); 6851 const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "index", .no_embedded_nulls); 6852 const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); 6853 6854 // Insert a save instruction before the arg resolution + call instructions we just generated 6855 const save_inst = try block.insertInst(block_index, .{ 6856 .tag = .save_err_return_trace_index, 6857 .data = .{ .ty_pl = .{ 6858 .ty = Air.internedToRef(stack_trace_ty.toIntern()), 6859 .payload = @intCast(field_index), 6860 } }, 6861 }); 6862 6863 // Pop the error return trace, testing the result for non-error if necessary 6864 const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst; 6865 try sema.popErrorReturnTrace(block, call_src, operand, save_inst); 6866 } 6867 6868 return call_inst; 6869 } else { 6870 return call_inst; 6871 } 6872 } 6873 6874 fn checkCallArgumentCount( 6875 sema: *Sema, 6876 block: *Block, 6877 func: Air.Inst.Ref, 6878 func_src: LazySrcLoc, 6879 callee_ty: Type, 6880 total_args: usize, 6881 member_fn: bool, 6882 ) !Type { 6883 const pt = sema.pt; 6884 const zcu = pt.zcu; 6885 const func_ty: Type = func_ty: { 6886 switch (callee_ty.zigTypeTag(zcu)) { 6887 .@"fn" => break :func_ty callee_ty, 6888 .pointer => { 6889 const ptr_info = callee_ty.ptrInfo(zcu); 6890 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") { 6891 break :func_ty .fromInterned(ptr_info.child); 6892 } 6893 }, 6894 .optional => { 6895 const opt_child = callee_ty.optionalChild(zcu); 6896 if (opt_child.zigTypeTag(zcu) == .@"fn" or (opt_child.isSinglePointer(zcu) and 6897 opt_child.childType(zcu).zigTypeTag(zcu) == .@"fn")) 6898 { 6899 const msg = msg: { 6900 const msg = try sema.errMsg(func_src, "cannot call optional type '{f}'", .{ 6901 callee_ty.fmt(pt), 6902 }); 6903 errdefer msg.destroy(sema.gpa); 6904 try sema.errNote(func_src, msg, "consider using '.?', 'orelse' or 'if'", .{}); 6905 break :msg msg; 6906 }; 6907 return sema.failWithOwnedErrorMsg(block, msg); 6908 } 6909 }, 6910 else => {}, 6911 } 6912 return sema.fail(block, func_src, "type '{f}' not a function", .{callee_ty.fmt(pt)}); 6913 }; 6914 6915 const func_ty_info = zcu.typeToFunc(func_ty).?; 6916 const fn_params_len = func_ty_info.param_types.len; 6917 const args_len = total_args - @intFromBool(member_fn); 6918 if (func_ty_info.is_var_args) { 6919 assert(callConvSupportsVarArgs(func_ty_info.cc)); 6920 if (total_args >= fn_params_len) return func_ty; 6921 } else if (fn_params_len == total_args) { 6922 return func_ty; 6923 } 6924 6925 const maybe_func_inst = try sema.funcDeclSrcInst(func); 6926 const member_str = if (member_fn) "member function " else ""; 6927 const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; 6928 const msg = msg: { 6929 const msg = try sema.errMsg( 6930 func_src, 6931 "{s}expected {s}{d} argument(s), found {d}", 6932 .{ 6933 member_str, 6934 variadic_str, 6935 fn_params_len - @intFromBool(member_fn), 6936 args_len, 6937 }, 6938 ); 6939 errdefer msg.destroy(sema.gpa); 6940 6941 if (maybe_func_inst) |func_inst| { 6942 try sema.errNote(.{ 6943 .base_node_inst = func_inst, 6944 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 6945 }, msg, "function declared here", .{}); 6946 } 6947 break :msg msg; 6948 }; 6949 return sema.failWithOwnedErrorMsg(block, msg); 6950 } 6951 6952 fn callBuiltin( 6953 sema: *Sema, 6954 block: *Block, 6955 call_src: LazySrcLoc, 6956 builtin_fn: Air.Inst.Ref, 6957 modifier: std.builtin.CallModifier, 6958 args: []const Air.Inst.Ref, 6959 operation: CallOperation, 6960 ) !void { 6961 const pt = sema.pt; 6962 const zcu = pt.zcu; 6963 const callee_ty = sema.typeOf(builtin_fn); 6964 const func_ty: Type = func_ty: { 6965 switch (callee_ty.zigTypeTag(zcu)) { 6966 .@"fn" => break :func_ty callee_ty, 6967 .pointer => { 6968 const ptr_info = callee_ty.ptrInfo(zcu); 6969 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") { 6970 break :func_ty .fromInterned(ptr_info.child); 6971 } 6972 }, 6973 else => {}, 6974 } 6975 std.debug.panic("type '{f}' is not a function calling builtin fn", .{callee_ty.fmt(pt)}); 6976 }; 6977 6978 const func_ty_info = zcu.typeToFunc(func_ty).?; 6979 const fn_params_len = func_ty_info.param_types.len; 6980 if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) { 6981 std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len }); 6982 } 6983 6984 _ = try sema.analyzeCall( 6985 block, 6986 builtin_fn, 6987 func_ty, 6988 call_src, 6989 call_src, 6990 modifier, 6991 false, 6992 .{ .resolved = .{ .src = call_src, .args = args } }, 6993 null, 6994 operation, 6995 ); 6996 } 6997 6998 const CallOperation = enum { 6999 call, 7000 @"@call", 7001 @"@panic", 7002 @"safety check", 7003 @"error return", 7004 }; 7005 7006 const CallArgsInfo = union(enum) { 7007 /// The full list of resolved (but uncoerced) arguments is known ahead of time. 7008 resolved: struct { 7009 src: LazySrcLoc, 7010 args: []const Air.Inst.Ref, 7011 }, 7012 7013 /// The list of resolved (but uncoerced) arguments is known ahead of time, but 7014 /// originated from a usage of the @call builtin at the given node offset. 7015 call_builtin: struct { 7016 call_node_offset: std.zig.Ast.Node.Offset, 7017 args: []const Air.Inst.Ref, 7018 }, 7019 7020 /// This call corresponds to a ZIR call instruction. The arguments have not yet been 7021 /// resolved. They must be resolved by `analyzeCall` so that argument resolution and 7022 /// generic instantiation may be interleaved. This is required for RLS to work on 7023 /// generic parameters. 7024 zir_call: struct { 7025 /// This may be `none`, in which case it is ignored. Otherwise, it is the 7026 /// already-resolved value of the first argument, from method call syntax. 7027 bound_arg: Air.Inst.Ref, 7028 /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`. 7029 bound_arg_src: LazySrcLoc, 7030 /// The ZIR call instruction. The parameter type is placed at this index while 7031 /// analyzing arguments. 7032 call_inst: Zir.Inst.Index, 7033 /// The node offset of `call_inst`. 7034 call_node_offset: std.zig.Ast.Node.Offset, 7035 /// The number of arguments to this call, not including `bound_arg`. 7036 num_args: u32, 7037 /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it 7038 /// is not `none`). Format is precisely the same as trailing data of ZIR `call`. 7039 args_body: []const Zir.Inst.Index, 7040 /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type. 7041 /// This is used by the caller to restore the error return trace when necessary. 7042 any_arg_is_error: *bool, 7043 }, 7044 7045 fn count(cai: CallArgsInfo) usize { 7046 return switch (cai) { 7047 inline .resolved, .call_builtin => |resolved| resolved.args.len, 7048 .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none), 7049 }; 7050 } 7051 7052 fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc { 7053 return switch (cai) { 7054 .resolved => |resolved| resolved.src, 7055 .call_builtin => |call_builtin| block.src(.{ .call_arg = .{ 7056 .call_node_offset = call_builtin.call_node_offset, 7057 .arg_index = @intCast(arg_index), 7058 } }), 7059 .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) { 7060 return zir_call.bound_arg_src; 7061 } else block.src(.{ .call_arg = .{ 7062 .call_node_offset = zir_call.call_node_offset, 7063 .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)), 7064 } }), 7065 }; 7066 } 7067 7068 /// Analyzes the arg at `arg_index` and coerces it to `param_ty`. 7069 /// `param_ty` may be `generic_poison`. A value of `null` indicates a varargs parameter. 7070 /// `func_ty_info` may be the type before instantiation, even if a generic instantiation is in progress. 7071 /// Emits a compile error if the argument is not comptime-known despite either `block.isComptime()` or 7072 /// the parameter being marked `comptime`. 7073 fn analyzeArg( 7074 cai: CallArgsInfo, 7075 sema: *Sema, 7076 block: *Block, 7077 arg_index: usize, 7078 maybe_param_ty: ?Type, 7079 func_ty_info: InternPool.Key.FuncType, 7080 func_inst: Air.Inst.Ref, 7081 maybe_func_src_inst: ?InternPool.TrackedInst.Index, 7082 ) CompileError!Air.Inst.Ref { 7083 const pt = sema.pt; 7084 const zcu = pt.zcu; 7085 const param_count = func_ty_info.param_types.len; 7086 const uncoerced_arg: Air.Inst.Ref = switch (cai) { 7087 inline .resolved, .call_builtin => |resolved| resolved.args[arg_index], 7088 .zir_call => |zir_call| arg_val: { 7089 const has_bound_arg = zir_call.bound_arg != .none; 7090 if (arg_index == 0 and has_bound_arg) { 7091 break :arg_val zir_call.bound_arg; 7092 } 7093 const real_arg_idx = arg_index - @intFromBool(has_bound_arg); 7094 7095 const arg_body = if (real_arg_idx == 0) blk: { 7096 const start = zir_call.num_args; 7097 const end = @intFromEnum(zir_call.args_body[0]); 7098 break :blk zir_call.args_body[start..end]; 7099 } else blk: { 7100 const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]); 7101 const end = @intFromEnum(zir_call.args_body[real_arg_idx]); 7102 break :blk zir_call.args_body[start..end]; 7103 }; 7104 7105 // Generate args to comptime params in comptime block 7106 const parent_comptime = block.comptime_reason; 7107 defer block.comptime_reason = parent_comptime; 7108 // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx` 7109 if (std.math.cast(u5, arg_index)) |i| { 7110 if (i < param_count and func_ty_info.paramIsComptime(i)) { 7111 block.comptime_reason = .{ 7112 .reason = .{ 7113 .src = cai.argSrc(block, arg_index), 7114 .r = .{ 7115 .comptime_param = .{ 7116 .comptime_src = if (maybe_func_src_inst) |src_inst| .{ 7117 .base_node_inst = src_inst, 7118 .offset = .{ .func_decl_param_comptime = @intCast(arg_index) }, 7119 } else unreachable, // should be non-null because the function is generic 7120 }, 7121 }, 7122 }, 7123 }; 7124 } 7125 } 7126 // Give the arg its result type 7127 const provide_param_ty: Type = maybe_param_ty orelse .generic_poison; 7128 sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(provide_param_ty.toIntern())); 7129 // Resolve the arg! 7130 const uncoerced_arg = try sema.resolveInlineBody(block, arg_body, zir_call.call_inst); 7131 7132 if (block.isComptime() and !try sema.isComptimeKnown(uncoerced_arg)) { 7133 return sema.failWithNeededComptime(block, cai.argSrc(block, arg_index), null); 7134 } 7135 7136 if (sema.typeOf(uncoerced_arg).zigTypeTag(zcu) == .noreturn) { 7137 // This terminates resolution of arguments. The caller should 7138 // propagate this. 7139 return uncoerced_arg; 7140 } 7141 7142 if (sema.typeOf(uncoerced_arg).isError(zcu)) { 7143 zir_call.any_arg_is_error.* = true; 7144 } 7145 7146 break :arg_val uncoerced_arg; 7147 }, 7148 }; 7149 const param_ty = maybe_param_ty orelse { 7150 return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index)); 7151 }; 7152 switch (param_ty.toIntern()) { 7153 .generic_poison_type => return uncoerced_arg, 7154 else => return sema.coerceExtra( 7155 block, 7156 param_ty, 7157 uncoerced_arg, 7158 cai.argSrc(block, arg_index), 7159 .{ .param_src = .{ 7160 .func_inst = func_inst, 7161 .param_i = @intCast(arg_index), 7162 } }, 7163 ) catch |err| switch (err) { 7164 error.NotCoercible => unreachable, 7165 else => |e| return e, 7166 }, 7167 } 7168 } 7169 }; 7170 7171 fn analyzeCall( 7172 sema: *Sema, 7173 block: *Block, 7174 callee: Air.Inst.Ref, 7175 func_ty: Type, 7176 func_src: LazySrcLoc, 7177 call_src: LazySrcLoc, 7178 modifier: std.builtin.CallModifier, 7179 ensure_result_used: bool, 7180 args_info: CallArgsInfo, 7181 call_dbg_node: ?Zir.Inst.Index, 7182 operation: CallOperation, 7183 ) CompileError!Air.Inst.Ref { 7184 const pt = sema.pt; 7185 const zcu = pt.zcu; 7186 const gpa = zcu.gpa; 7187 const ip = &zcu.intern_pool; 7188 const arena = sema.arena; 7189 7190 const maybe_func_inst = try sema.funcDeclSrcInst(callee); 7191 const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{ 7192 .base_node_inst = fn_decl_inst, 7193 .offset = .{ .node_offset_fn_type_ret_ty = .zero }, 7194 } else func_src; 7195 7196 const func_ty_info = zcu.typeToFunc(func_ty).?; 7197 if (!callConvIsCallable(func_ty_info.cc)) { 7198 return sema.failWithOwnedErrorMsg(block, msg: { 7199 const msg = try sema.errMsg( 7200 func_src, 7201 "unable to call function with calling convention '{s}'", 7202 .{@tagName(func_ty_info.cc)}, 7203 ); 7204 errdefer msg.destroy(gpa); 7205 if (maybe_func_inst) |func_inst| try sema.errNote(.{ 7206 .base_node_inst = func_inst, 7207 .offset = .nodeOffset(.zero), 7208 }, msg, "function declared here", .{}); 7209 break :msg msg; 7210 }); 7211 } 7212 7213 // We need this value in a few code paths. 7214 const callee_val = try sema.resolveDefinedValue(block, call_src, callee); 7215 // If the callee is a comptime-known *non-extern* function, `func_val` is populated. 7216 // If it is a comptime-known extern function, `func_is_extern` is set instead. 7217 // If it is not comptime-known, neither is set. 7218 const func_val: ?Value, const func_is_extern: bool = if (callee_val) |c| switch (ip.indexToKey(c.toIntern())) { 7219 .func => .{ c, false }, 7220 .ptr => switch (try sema.pointerDerefExtra(block, func_src, c)) { 7221 .runtime_load, .needed_well_defined, .out_of_bounds => .{ null, false }, 7222 .val => |pointee| switch (ip.indexToKey(pointee.toIntern())) { 7223 .func => .{ pointee, false }, 7224 .@"extern" => .{ null, true }, 7225 else => unreachable, 7226 }, 7227 }, 7228 .@"extern" => .{ null, true }, 7229 else => unreachable, 7230 } else .{ null, false }; 7231 7232 if (func_ty_info.is_generic and func_val == null) { 7233 return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target }); 7234 } 7235 7236 const inline_requested = func_ty_info.cc == .@"inline" or modifier == .always_inline; 7237 7238 // If the modifier is `.compile_time`, or if the return type is non-generic and comptime-only, 7239 // then we need to enter a comptime scope *now* to make sure the args are comptime-eval'd. 7240 const old_block_comptime_reason = block.comptime_reason; 7241 defer block.comptime_reason = old_block_comptime_reason; 7242 if (!block.isComptime()) { 7243 if (modifier == .compile_time) { 7244 block.comptime_reason = .{ .reason = .{ 7245 .src = call_src, 7246 .r = .{ .simple = .comptime_call_modifier }, 7247 } }; 7248 } else if (!inline_requested and try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) { 7249 block.comptime_reason = .{ 7250 .reason = .{ 7251 .src = call_src, 7252 .r = .{ 7253 .comptime_only_ret_ty = .{ 7254 .ty = .fromInterned(func_ty_info.return_type), 7255 .is_generic_inst = false, 7256 .ret_ty_src = func_ret_ty_src, 7257 }, 7258 }, 7259 }, 7260 }; 7261 } 7262 } 7263 7264 // This is whether we already know this to be an inline call. 7265 // If so, then comptime-known arguments are propagated when evaluating generic parameter/return types. 7266 // We might still learn that this call is inline *after* evaluating the generic return type. 7267 const early_known_inline = inline_requested or block.isComptime(); 7268 7269 // These values are undefined if `func_val == null`. 7270 const fn_nav: InternPool.Nav, const fn_zir: Zir, const fn_tracked_inst: InternPool.TrackedInst.Index, const fn_zir_inst: Zir.Inst.Index, const fn_zir_info: Zir.FnInfo = if (func_val) |f| b: { 7271 const info = ip.indexToKey(f.toIntern()).func; 7272 const nav = ip.getNav(info.owner_nav); 7273 const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail; 7274 const file = zcu.fileByIndex(resolved_func_inst.file); 7275 const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst); 7276 break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info }; 7277 } else .{ undefined, undefined, undefined, undefined, undefined }; 7278 7279 // This is the `inst_map` used when evaluating generic parameters and return types. 7280 var generic_inst_map: InstMap = .{}; 7281 defer generic_inst_map.deinit(gpa); 7282 if (func_ty_info.is_generic) { 7283 try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); 7284 } 7285 7286 // This exists so that `generic_block` below can include a "called from here" note back to this 7287 // call site when analyzing generic parameter/return types. 7288 var generic_inlining: Block.Inlining = if (func_ty_info.is_generic) .{ 7289 .call_block = block, 7290 .call_src = call_src, 7291 .func = func_val.?.toIntern(), 7292 .is_generic_instantiation = true, // this allows the following fields to be `undefined` 7293 .has_comptime_args = undefined, 7294 .comptime_result = undefined, 7295 .merges = undefined, 7296 } else undefined; 7297 7298 // This is the block in which we evaluate generic function components: that is, generic parameter 7299 // types and the generic return type. This must not be used if the function is not generic. 7300 // `comptime_reason` is set as needed. 7301 var generic_block: Block = if (func_ty_info.is_generic) .{ 7302 .parent = null, 7303 .sema = sema, 7304 .namespace = fn_nav.analysis.?.namespace, 7305 .instructions = .{}, 7306 .inlining = &generic_inlining, 7307 .src_base_inst = fn_nav.analysis.?.zir_index, 7308 .type_name_ctx = fn_nav.fqn, 7309 } else undefined; 7310 defer if (func_ty_info.is_generic) generic_block.instructions.deinit(gpa); 7311 7312 if (func_ty_info.is_generic) { 7313 // We certainly depend on the generic owner's signature! 7314 try sema.declareDependency(.{ .src_hash = fn_tracked_inst }); 7315 } 7316 7317 const args = try arena.alloc(Air.Inst.Ref, args_info.count()); 7318 for (args, 0..) |*arg, arg_idx| { 7319 const param_ty: ?Type = if (arg_idx < func_ty_info.param_types.len) ty: { 7320 const raw = func_ty_info.param_types.get(ip)[arg_idx]; 7321 if (raw != .generic_poison_type) break :ty .fromInterned(raw); 7322 7323 // We must discover the generic parameter type. 7324 assert(func_ty_info.is_generic); 7325 const param_inst_idx = fn_zir_info.param_body[arg_idx]; 7326 const param_inst = fn_zir.instructions.get(@intFromEnum(param_inst_idx)); 7327 switch (param_inst.tag) { 7328 .param_anytype, .param_anytype_comptime => break :ty .generic_poison, 7329 .param, .param_comptime => {}, 7330 else => unreachable, 7331 } 7332 7333 // Evaluate the generic parameter type. We need to switch out `sema.code` and `sema.inst_map`, because 7334 // the function definition may be in a different file to the call site. 7335 const old_code = sema.code; 7336 const old_inst_map = sema.inst_map; 7337 defer { 7338 generic_inst_map = sema.inst_map; 7339 sema.code = old_code; 7340 sema.inst_map = old_inst_map; 7341 } 7342 sema.code = fn_zir; 7343 sema.inst_map = generic_inst_map; 7344 7345 const extra = sema.code.extraData(Zir.Inst.Param, param_inst.data.pl_tok.payload_index); 7346 const param_src = generic_block.tokenOffset(param_inst.data.pl_tok.src_tok); 7347 const body = sema.code.bodySlice(extra.end, extra.data.type.body_len); 7348 7349 generic_block.comptime_reason = .{ .reason = .{ 7350 .r = .{ .simple = .function_parameters }, 7351 .src = param_src, 7352 } }; 7353 7354 const ty_ref = try sema.resolveInlineBody(&generic_block, body, param_inst_idx); 7355 const param_ty = try sema.analyzeAsType(&generic_block, param_src, ty_ref); 7356 7357 if (!param_ty.isValidParamType(zcu)) { 7358 const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; 7359 return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{ 7360 opaque_str, param_ty.fmt(pt), 7361 }); 7362 } 7363 7364 break :ty param_ty; 7365 } else null; // vararg 7366 7367 arg.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, callee, maybe_func_inst); 7368 const arg_ty = sema.typeOf(arg.*); 7369 if (arg_ty.zigTypeTag(zcu) == .noreturn) { 7370 return arg.*; // terminate analysis here 7371 } 7372 7373 if (func_ty_info.is_generic) { 7374 // We need to put the argument into `generic_inst_map` so that other parameters can refer to it. 7375 const param_inst_idx = fn_zir_info.param_body[arg_idx]; 7376 const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false; 7377 const param_is_comptime = declared_comptime or try arg_ty.comptimeOnlySema(pt); 7378 // We allow comptime-known arguments to propagate to generic types not only for comptime 7379 // parameters, but if the call is known to be inline. 7380 if (param_is_comptime or early_known_inline) { 7381 if (param_is_comptime and !try sema.isComptimeKnown(arg.*)) { 7382 assert(!declared_comptime); // `analyzeArg` handles this 7383 const arg_src = args_info.argSrc(block, arg_idx); 7384 const param_ty_src: LazySrcLoc = .{ 7385 .base_node_inst = maybe_func_inst.?, // the function is generic 7386 .offset = .{ .func_decl_param_ty = @intCast(arg_idx) }, 7387 }; 7388 return sema.failWithNeededComptime( 7389 block, 7390 arg_src, 7391 .{ .comptime_only_param_ty = .{ .ty = arg_ty, .param_ty_src = param_ty_src } }, 7392 ); 7393 } 7394 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*); 7395 } else { 7396 // We need a dummy instruction with this type. It doesn't actually need to be in any block, 7397 // since it will never be referenced at runtime! 7398 const dummy: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 7399 try sema.air_instructions.append(gpa, .{ .tag = .alloc, .data = .{ .ty = arg_ty } }); 7400 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, dummy.toRef()); 7401 } 7402 } 7403 } 7404 7405 // This return type is never generic poison. 7406 // However, if it has an IES, it is always associated with the callee value. 7407 // This is not correct for inline calls (where it should be an ad-hoc IES), nor for generic 7408 // calls (where it should be the IES of the instantiation). However, it's how we print this 7409 // in error messages. 7410 const resolved_ret_ty: Type = ret_ty: { 7411 if (!func_ty_info.is_generic) break :ret_ty .fromInterned(func_ty_info.return_type); 7412 7413 const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: { 7414 break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type); 7415 } else func_ty_info.return_type; 7416 7417 if (maybe_poison_bare != .generic_poison_type) break :ret_ty .fromInterned(func_ty_info.return_type); 7418 7419 // Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`. 7420 7421 assert(func_ty_info.is_generic); 7422 7423 const old_code = sema.code; 7424 const old_inst_map = sema.inst_map; 7425 defer { 7426 generic_inst_map = sema.inst_map; 7427 sema.code = old_code; 7428 sema.inst_map = old_inst_map; 7429 } 7430 sema.code = fn_zir; 7431 sema.inst_map = generic_inst_map; 7432 7433 generic_block.comptime_reason = .{ .reason = .{ 7434 .r = .{ .simple = .function_ret_ty }, 7435 .src = func_ret_ty_src, 7436 } }; 7437 7438 const bare_ty = if (fn_zir_info.ret_ty_ref != .none) bare: { 7439 assert(fn_zir_info.ret_ty_body.len == 0); 7440 break :bare try sema.resolveType(&generic_block, func_ret_ty_src, fn_zir_info.ret_ty_ref); 7441 } else bare: { 7442 assert(fn_zir_info.ret_ty_body.len != 0); 7443 const ty_ref = try sema.resolveInlineBody(&generic_block, fn_zir_info.ret_ty_body, fn_zir_inst); 7444 break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, ty_ref); 7445 }; 7446 assert(bare_ty.toIntern() != .generic_poison_type); 7447 7448 const full_ty = if (fn_zir_info.inferred_error_set) full: { 7449 try sema.validateErrorUnionPayloadType(block, bare_ty, func_ret_ty_src); 7450 const set = ip.errorUnionSet(func_ty_info.return_type); 7451 break :full try pt.errorUnionType(.fromInterned(set), bare_ty); 7452 } else bare_ty; 7453 7454 if (!full_ty.isValidReturnType(zcu)) { 7455 const opaque_str = if (full_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; 7456 return sema.fail(block, func_ret_ty_src, "{s}return type '{f}' not allowed", .{ 7457 opaque_str, full_ty.fmt(pt), 7458 }); 7459 } 7460 7461 break :ret_ty full_ty; 7462 }; 7463 7464 // If we've discovered after evaluating arguments that a generic function instantiation is 7465 // comptime-only, then we can mark the block as comptime *now*. 7466 if (!inline_requested and !block.isComptime() and try resolved_ret_ty.comptimeOnlySema(pt)) { 7467 block.comptime_reason = .{ 7468 .reason = .{ 7469 .src = call_src, 7470 .r = .{ 7471 .comptime_only_ret_ty = .{ 7472 .ty = resolved_ret_ty, 7473 .is_generic_inst = true, 7474 .ret_ty_src = func_ret_ty_src, 7475 }, 7476 }, 7477 }, 7478 }; 7479 } 7480 7481 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); 7482 7483 const is_inline_call = block.isComptime() or inline_requested; 7484 7485 if (!is_inline_call) { 7486 if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: { 7487 const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); 7488 errdefer msg.destroy(gpa); 7489 switch (operation) { 7490 .call, .@"@call", .@"@panic", .@"error return" => {}, 7491 .@"safety check" => try sema.errNote(call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}), 7492 } 7493 break :msg msg; 7494 }); 7495 if (func_ty_info.cc == .auto) { 7496 switch (sema.owner.unwrap()) { 7497 .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, 7498 .func => |owner_func| ip.funcSetHasErrorTrace(owner_func, true), 7499 } 7500 } 7501 for (args, 0..) |arg, arg_idx| { 7502 try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg); 7503 } 7504 const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: { 7505 if (!func_ty_info.is_generic) break :func .{ callee, args }; 7506 7507 // Instantiate the generic function! 7508 7509 // This may be an overestimate, but it's definitely sufficient. 7510 const max_runtime_args = args_info.count() - @popCount(func_ty_info.comptime_bits); 7511 var runtime_args: std.ArrayListUnmanaged(Air.Inst.Ref) = try .initCapacity(arena, max_runtime_args); 7512 var runtime_param_tys: std.ArrayListUnmanaged(InternPool.Index) = try .initCapacity(arena, max_runtime_args); 7513 7514 const comptime_args = try arena.alloc(InternPool.Index, args_info.count()); 7515 7516 var noalias_bits: u32 = 0; 7517 7518 for (args, comptime_args, 0..) |arg, *comptime_arg, arg_idx| { 7519 const arg_ty = sema.typeOf(arg); 7520 7521 const is_comptime = c: { 7522 if (std.math.cast(u5, arg_idx)) |i| { 7523 if (func_ty_info.paramIsComptime(i)) { 7524 break :c true; 7525 } 7526 } 7527 break :c try arg_ty.comptimeOnlySema(pt); 7528 }; 7529 const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false; 7530 7531 if (is_comptime) { 7532 // We already emitted an error if the argument isn't comptime-known. 7533 comptime_arg.* = (try sema.resolveValue(arg)).?.toIntern(); 7534 } else { 7535 comptime_arg.* = .none; 7536 if (is_noalias) { 7537 const runtime_idx = runtime_args.items.len; 7538 noalias_bits |= @as(u32, 1) << @intCast(runtime_idx); 7539 } 7540 runtime_args.appendAssumeCapacity(arg); 7541 runtime_param_tys.appendAssumeCapacity(arg_ty.toIntern()); 7542 } 7543 } 7544 7545 const bare_ret_ty = if (fn_zir_info.inferred_error_set) t: { 7546 break :t resolved_ret_ty.errorUnionPayload(zcu); 7547 } else resolved_ret_ty; 7548 7549 // We now need to actually create the function instance. 7550 const func_instance = try ip.getFuncInstance(gpa, pt.tid, .{ 7551 .param_types = runtime_param_tys.items, 7552 .noalias_bits = noalias_bits, 7553 .bare_return_type = bare_ret_ty.toIntern(), 7554 .is_noinline = func_ty_info.is_noinline, 7555 .inferred_error_set = fn_zir_info.inferred_error_set, 7556 .generic_owner = func_val.?.toIntern(), 7557 .comptime_args = comptime_args, 7558 }); 7559 if (zcu.comp.debugIncremental()) { 7560 const nav = ip.indexToKey(func_instance).func.owner_nav; 7561 const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav); 7562 if (!gop.found_existing) gop.value_ptr.* = zcu.generation; 7563 } 7564 7565 // This call is problematic as it breaks guarantees about order-independency of semantic analysis. 7566 // These guarantees are necessary for incremental compilation and parallel semantic analysis. 7567 // See: #22410 7568 zcu.funcInfo(func_instance).maxBranchQuota(ip, sema.branch_quota); 7569 7570 break :func .{ Air.internedToRef(func_instance), runtime_args.items }; 7571 }; 7572 7573 ref_func: { 7574 const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func; 7575 if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func; 7576 const orig_fn_index = ip.unwrapCoercedFunc(runtime_func_val.toIntern()); 7577 try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = orig_fn_index })); 7578 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); 7579 } 7580 7581 const call_tag: Air.Inst.Tag = switch (modifier) { 7582 .auto, .no_suspend => .call, 7583 .never_tail => .call_never_tail, 7584 .never_inline => .call_never_inline, 7585 .always_tail => .call_always_tail, 7586 7587 .always_inline, 7588 .compile_time, 7589 => unreachable, 7590 }; 7591 7592 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len); 7593 const maybe_opv = try block.addInst(.{ 7594 .tag = call_tag, 7595 .data = .{ .pl_op = .{ 7596 .operand = runtime_func, 7597 .payload = sema.addExtraAssumeCapacity(Air.Call{ 7598 .args_len = @intCast(runtime_args.len), 7599 }), 7600 } }, 7601 }); 7602 sema.appendRefsAssumeCapacity(runtime_args); 7603 7604 if (ensure_result_used) { 7605 try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src); 7606 } 7607 7608 if (call_tag == .call_always_tail) { 7609 const func_or_ptr_ty = sema.typeOf(runtime_func); 7610 const runtime_func_ty = switch (func_or_ptr_ty.zigTypeTag(zcu)) { 7611 .@"fn" => func_or_ptr_ty, 7612 .pointer => func_or_ptr_ty.childType(zcu), 7613 else => unreachable, 7614 }; 7615 return sema.handleTailCall(block, call_src, runtime_func_ty, maybe_opv); 7616 } 7617 7618 if (ip.isNoReturn(resolved_ret_ty.toIntern())) { 7619 const want_check = c: { 7620 if (!block.wantSafety()) break :c false; 7621 if (func_val != null) break :c false; 7622 break :c true; 7623 }; 7624 if (want_check) { 7625 try sema.safetyPanic(block, call_src, .noreturn_returned); 7626 } else { 7627 _ = try block.addNoOp(.unreach); 7628 } 7629 return .unreachable_value; 7630 } 7631 7632 const result: Air.Inst.Ref = if (try sema.typeHasOnePossibleValue(sema.typeOf(maybe_opv))) |opv| 7633 .fromValue(opv) 7634 else 7635 maybe_opv; 7636 7637 return result; 7638 } 7639 7640 // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`. 7641 7642 if (zcu.comp.time_report) |*tr| { 7643 if (!block.isComptime()) { 7644 tr.stats.n_inline_calls += 1; 7645 } 7646 } 7647 7648 if (func_ty_info.is_noinline and !block.isComptime()) { 7649 return sema.fail(block, call_src, "inline call of noinline function", .{}); 7650 } 7651 7652 const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline"; 7653 if (modifier == .never_inline) { 7654 const msg, const fail_block = msg: { 7655 const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type}); 7656 errdefer msg.destroy(gpa); 7657 const fail_block = if (block.isComptime()) b: { 7658 break :b try block.explainWhyBlockIsComptime(msg); 7659 } else block; 7660 break :msg .{ msg, fail_block }; 7661 }; 7662 return sema.failWithOwnedErrorMsg(fail_block, msg); 7663 } 7664 if (func_ty_info.is_var_args) { 7665 const msg, const fail_block = msg: { 7666 const msg = try sema.errMsg(call_src, "{s} call of variadic function", .{call_type}); 7667 errdefer msg.destroy(gpa); 7668 const fail_block = if (block.isComptime()) b: { 7669 break :b try block.explainWhyBlockIsComptime(msg); 7670 } else block; 7671 break :msg .{ msg, fail_block }; 7672 }; 7673 return sema.failWithOwnedErrorMsg(fail_block, msg); 7674 } 7675 if (func_val == null) { 7676 if (func_is_extern) { 7677 const msg, const fail_block = msg: { 7678 const msg = try sema.errMsg(call_src, "{s} call of extern function", .{call_type}); 7679 errdefer msg.destroy(gpa); 7680 const fail_block = if (block.isComptime()) b: { 7681 break :b try block.explainWhyBlockIsComptime(msg); 7682 } else block; 7683 break :msg .{ msg, fail_block }; 7684 }; 7685 return sema.failWithOwnedErrorMsg(fail_block, msg); 7686 } 7687 return sema.failWithNeededComptime( 7688 block, 7689 func_src, 7690 if (block.isComptime()) null else .{ .simple = .inline_call_target }, 7691 ); 7692 } 7693 7694 if (block.isComptime()) { 7695 for (args, 0..) |arg, arg_idx| { 7696 if (!try sema.isComptimeKnown(arg)) { 7697 const arg_src = args_info.argSrc(block, arg_idx); 7698 return sema.failWithNeededComptime(block, arg_src, null); 7699 } 7700 } 7701 } 7702 7703 // For an inline call, we depend on the source code of the whole function definition. 7704 try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index }); 7705 7706 try sema.emitBackwardBranch(block, call_src); 7707 7708 const want_memoize = m: { 7709 // TODO: comptime call memoization is currently not supported under incremental compilation 7710 // since dependencies are not marked on callers. If we want to keep this around (we should 7711 // check that it's worthwhile first!), each memoized call needs an `AnalUnit`. 7712 if (zcu.comp.incremental) break :m false; 7713 if (!block.isComptime()) break :m false; 7714 for (args) |a| { 7715 const val = (try sema.resolveValue(a)).?; 7716 if (val.canMutateComptimeVarState(zcu)) break :m false; 7717 } 7718 break :m true; 7719 }; 7720 const memoized_arg_values: []const InternPool.Index = if (want_memoize) arg_vals: { 7721 const vals = try sema.arena.alloc(InternPool.Index, args.len); 7722 for (vals, args) |*v, a| v.* = (try sema.resolveValue(a)).?.toIntern(); 7723 break :arg_vals vals; 7724 } else undefined; 7725 if (want_memoize) memoize: { 7726 const memoized_call_index = ip.getIfExists(.{ 7727 .memoized_call = .{ 7728 .func = func_val.?.toIntern(), 7729 .arg_values = memoized_arg_values, 7730 .result = undefined, // ignored by hash+eql 7731 .branch_count = undefined, // ignored by hash+eql 7732 }, 7733 }) orelse break :memoize; 7734 const memoized_call = ip.indexToKey(memoized_call_index).memoized_call; 7735 if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) { 7736 // Let the call play out se we get the correct source location for the 7737 // "evaluation exceeded X backwards branches" error. 7738 break :memoize; 7739 } 7740 sema.branch_count += memoized_call.branch_count; 7741 const result = Air.internedToRef(memoized_call.result); 7742 if (ensure_result_used) { 7743 try sema.ensureResultUsed(block, sema.typeOf(result), call_src); 7744 } 7745 return result; 7746 } 7747 7748 var new_ies: InferredErrorSet = .{ .func = .none }; 7749 7750 const old_inst_map = sema.inst_map; 7751 const old_code = sema.code; 7752 const old_func_index = sema.func_index; 7753 const old_fn_ret_ty = sema.fn_ret_ty; 7754 const old_fn_ret_ty_ies = sema.fn_ret_ty_ies; 7755 const old_error_return_trace_index_on_fn_entry = sema.error_return_trace_index_on_fn_entry; 7756 defer { 7757 sema.inst_map.deinit(gpa); 7758 sema.inst_map = old_inst_map; 7759 sema.code = old_code; 7760 sema.func_index = old_func_index; 7761 sema.fn_ret_ty = old_fn_ret_ty; 7762 sema.fn_ret_ty_ies = old_fn_ret_ty_ies; 7763 sema.error_return_trace_index_on_fn_entry = old_error_return_trace_index_on_fn_entry; 7764 } 7765 sema.inst_map = .{}; 7766 sema.code = fn_zir; 7767 sema.func_index = func_val.?.toIntern(); 7768 sema.fn_ret_ty = if (fn_zir_info.inferred_error_set) try pt.errorUnionType( 7769 .fromInterned(.adhoc_inferred_error_set_type), 7770 resolved_ret_ty.errorUnionPayload(zcu), 7771 ) else resolved_ret_ty; 7772 sema.fn_ret_ty_ies = if (fn_zir_info.inferred_error_set) &new_ies else null; 7773 7774 try sema.inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); 7775 for (args, 0..) |arg, arg_idx| { 7776 sema.inst_map.putAssumeCapacityNoClobber(fn_zir_info.param_body[arg_idx], arg); 7777 } 7778 7779 const need_debug_scope = !block.isComptime() and !block.is_typeof and !block.ownerModule().strip; 7780 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 7781 try sema.air_instructions.append(gpa, .{ 7782 .tag = if (need_debug_scope) .dbg_inline_block else .block, 7783 .data = undefined, 7784 }); 7785 7786 var inlining: Block.Inlining = .{ 7787 .call_block = block, 7788 .call_src = call_src, 7789 .func = func_val.?.toIntern(), 7790 .is_generic_instantiation = false, 7791 .has_comptime_args = for (args) |a| { 7792 if (try sema.isComptimeKnown(a)) break true; 7793 } else false, 7794 .comptime_result = undefined, 7795 .merges = .{ 7796 .block_inst = block_inst, 7797 .results = .empty, 7798 .br_list = .empty, 7799 .src_locs = .empty, 7800 }, 7801 }; 7802 var child_block: Block = .{ 7803 .parent = null, 7804 .sema = sema, 7805 .namespace = fn_nav.analysis.?.namespace, 7806 .instructions = .{}, 7807 .inlining = &inlining, 7808 .is_typeof = block.is_typeof, 7809 .comptime_reason = if (block.isComptime()) .inlining_parent else null, 7810 .error_return_trace_index = block.error_return_trace_index, 7811 .runtime_cond = block.runtime_cond, 7812 .runtime_loop = block.runtime_loop, 7813 .runtime_index = block.runtime_index, 7814 .src_base_inst = fn_nav.analysis.?.zir_index, 7815 .type_name_ctx = fn_nav.fqn, 7816 }; 7817 7818 defer child_block.instructions.deinit(gpa); 7819 defer inlining.merges.deinit(gpa); 7820 7821 if (!inlining.has_comptime_args) { 7822 var block_it = block; 7823 while (block_it.inlining) |parent_inlining| { 7824 if (!parent_inlining.is_generic_instantiation and 7825 !parent_inlining.has_comptime_args and 7826 parent_inlining.func == func_val.?.toIntern()) 7827 { 7828 return sema.fail(block, call_src, "inline call is recursive", .{}); 7829 } 7830 block_it = parent_inlining.call_block; 7831 } 7832 } 7833 7834 if (!block.isComptime() and !block.is_typeof) { 7835 const zir_tags = sema.code.instructions.items(.tag); 7836 const zir_datas = sema.code.instructions.items(.data); 7837 for (fn_zir_info.param_body) |inst| switch (zir_tags[@intFromEnum(inst)]) { 7838 .param, .param_comptime => { 7839 const extra = sema.code.extraData(Zir.Inst.Param, zir_datas[@intFromEnum(inst)].pl_tok.payload_index); 7840 const param_name = sema.code.nullTerminatedString(extra.data.name); 7841 const air_inst = sema.inst_map.get(inst).?; 7842 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name); 7843 }, 7844 .param_anytype, .param_anytype_comptime => { 7845 const param_name = zir_datas[@intFromEnum(inst)].str_tok.get(sema.code); 7846 const air_inst = sema.inst_map.get(inst).?; 7847 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name); 7848 }, 7849 else => {}, 7850 }; 7851 } 7852 7853 child_block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block); 7854 // Save the error trace as our first action in the function 7855 // to match the behavior of runtime function calls. 7856 const error_return_trace_index_on_parent_fn_entry = sema.error_return_trace_index_on_fn_entry; 7857 sema.error_return_trace_index_on_fn_entry = child_block.error_return_trace_index; 7858 defer sema.error_return_trace_index_on_fn_entry = error_return_trace_index_on_parent_fn_entry; 7859 7860 // We temporarily set `allow_memoize` to `true` to track this comptime call. 7861 // It is restored after the call finishes analysis, so that a caller may 7862 // know whether an in-progress call (containing this call) may be memoized. 7863 const old_allow_memoize = sema.allow_memoize; 7864 defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize; 7865 sema.allow_memoize = true; 7866 7867 // Store the current eval branch count so we can find out how many eval branches 7868 // the comptime call caused. 7869 const old_branch_count = sema.branch_count; 7870 7871 const result_raw: Air.Inst.Ref = result: { 7872 sema.analyzeFnBody(&child_block, fn_zir_info.body) catch |err| switch (err) { 7873 error.ComptimeReturn => break :result inlining.comptime_result, 7874 else => |e| return e, 7875 }; 7876 break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope); 7877 }; 7878 7879 const maybe_opv: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: { 7880 const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern()); 7881 break :r Air.internedToRef(val_resolved); 7882 } else r: { 7883 const resolved_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result_raw).toIntern()); 7884 if (resolved_ty == .none) break :r result_raw; 7885 // TODO: mutate in place the previous instruction if possible 7886 // rather than adding a bitcast instruction. 7887 break :r try block.addBitCast(.fromInterned(resolved_ty), result_raw); 7888 }; 7889 7890 if (block.isComptime()) { 7891 const result_val = (try sema.resolveValue(maybe_opv)).?; 7892 if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) { 7893 _ = try pt.intern(.{ .memoized_call = .{ 7894 .func = func_val.?.toIntern(), 7895 .arg_values = memoized_arg_values, 7896 .result = result_val.toIntern(), 7897 .branch_count = sema.branch_count - old_branch_count, 7898 } }); 7899 } 7900 } 7901 7902 if (ensure_result_used) { 7903 try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src); 7904 } 7905 7906 return maybe_opv; 7907 } 7908 7909 fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref { 7910 const pt = sema.pt; 7911 const zcu = pt.zcu; 7912 const target = zcu.getTarget(); 7913 const backend = zcu.comp.getZigBackend(); 7914 if (!target_util.supportsTailCall(target, backend)) { 7915 return sema.fail(block, call_src, "unable to perform tail call: compiler backend '{s}' does not support tail calls on target architecture '{s}' with the selected CPU feature flags", .{ 7916 @tagName(backend), @tagName(target.cpu.arch), 7917 }); 7918 } 7919 const owner_func_ty: Type = .fromInterned(zcu.funcInfo(sema.owner.unwrap().func).ty); 7920 if (owner_func_ty.toIntern() != func_ty.toIntern()) { 7921 return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{f}' does not match type of calling function '{f}'", .{ 7922 func_ty.fmt(pt), owner_func_ty.fmt(pt), 7923 }); 7924 } 7925 _ = try block.addUnOp(.ret, result); 7926 return .unreachable_value; 7927 } 7928 7929 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7930 const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type; 7931 const ty = try sema.pt.intType(int_type.signedness, int_type.bit_count); 7932 return Air.internedToRef(ty.toIntern()); 7933 } 7934 7935 fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7936 const tracy = trace(@src()); 7937 defer tracy.end(); 7938 7939 const pt = sema.pt; 7940 const zcu = pt.zcu; 7941 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 7942 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 7943 const child_type = try sema.resolveType(block, operand_src, inst_data.operand); 7944 if (child_type.zigTypeTag(zcu) == .@"opaque") { 7945 return sema.fail(block, operand_src, "opaque type '{f}' cannot be optional", .{child_type.fmt(pt)}); 7946 } else if (child_type.zigTypeTag(zcu) == .null) { 7947 return sema.fail(block, operand_src, "type '{f}' cannot be optional", .{child_type.fmt(pt)}); 7948 } 7949 const opt_type = try pt.optionalType(child_type.toIntern()); 7950 7951 return Air.internedToRef(opt_type.toIntern()); 7952 } 7953 7954 fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7955 const pt = sema.pt; 7956 const zcu = pt.zcu; 7957 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 7958 const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type; 7959 const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu); 7960 try indexable_ty.resolveFields(pt); 7961 assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction 7962 if (indexable_ty.zigTypeTag(zcu) == .@"struct") { 7963 const elem_type = indexable_ty.fieldType(@intFromEnum(bin.rhs), zcu); 7964 return Air.internedToRef(elem_type.toIntern()); 7965 } else { 7966 const elem_type = indexable_ty.elemType2(zcu); 7967 return Air.internedToRef(elem_type.toIntern()); 7968 } 7969 } 7970 7971 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7972 const pt = sema.pt; 7973 const zcu = pt.zcu; 7974 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 7975 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type; 7976 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); 7977 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction 7978 const elem_ty = ptr_ty.childType(zcu); 7979 if (elem_ty.toIntern() == .anyopaque_type) { 7980 // The pointer's actual child type is effectively unknown, so it makes 7981 // sense to represent it with a generic poison. 7982 return .generic_poison_type; 7983 } 7984 return Air.internedToRef(ptr_ty.childType(zcu).toIntern()); 7985 } 7986 7987 fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7988 const pt = sema.pt; 7989 const zcu = pt.zcu; 7990 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 7991 const src = block.nodeOffset(un_node.src_node); 7992 const ptr_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type; 7993 try sema.checkMemOperand(block, src, ptr_ty); 7994 const elem_ty = switch (ptr_ty.ptrSize(zcu)) { 7995 .slice, .many, .c => ptr_ty.childType(zcu), 7996 .one => ptr_ty.childType(zcu).childType(zcu), 7997 }; 7998 return Air.internedToRef(elem_ty.toIntern()); 7999 } 8000 8001 fn zirSplatOpResultType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8002 const pt = sema.pt; 8003 const zcu = pt.zcu; 8004 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8005 8006 const raw_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type; 8007 const vec_ty = raw_ty.optEuBaseType(zcu); 8008 8009 switch (vec_ty.zigTypeTag(zcu)) { 8010 .array, .vector => {}, 8011 else => return sema.fail(block, block.nodeOffset(un_node.src_node), "expected array or vector type, found '{f}'", .{vec_ty.fmt(pt)}), 8012 } 8013 return Air.internedToRef(vec_ty.childType(zcu).toIntern()); 8014 } 8015 8016 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8017 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8018 const len_src = block.builtinCallArgSrc(inst_data.src_node, 0); 8019 const elem_type_src = block.builtinCallArgSrc(inst_data.src_node, 1); 8020 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8021 const len: u32 = @intCast(try sema.resolveInt(block, len_src, extra.lhs, .u32, .{ .simple = .vector_length })); 8022 const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs); 8023 try sema.checkVectorElemType(block, elem_type_src, elem_type); 8024 const vector_type = try sema.pt.vectorType(.{ 8025 .len = len, 8026 .child = elem_type.toIntern(), 8027 }); 8028 return Air.internedToRef(vector_type.toIntern()); 8029 } 8030 8031 fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8032 const tracy = trace(@src()); 8033 defer tracy.end(); 8034 8035 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8036 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8037 const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node }); 8038 const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node }); 8039 const len = try sema.resolveInt(block, len_src, extra.lhs, .usize, .{ .simple = .array_length }); 8040 const elem_type = try sema.resolveType(block, elem_src, extra.rhs); 8041 try sema.validateArrayElemType(block, elem_type, elem_src); 8042 const array_ty = try sema.pt.arrayType(.{ 8043 .len = len, 8044 .child = elem_type.toIntern(), 8045 }); 8046 8047 return Air.internedToRef(array_ty.toIntern()); 8048 } 8049 8050 fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8051 const tracy = trace(@src()); 8052 defer tracy.end(); 8053 8054 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8055 const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; 8056 const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node }); 8057 const sentinel_src = block.src(.{ .node_offset_array_type_sentinel = inst_data.src_node }); 8058 const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node }); 8059 const len = try sema.resolveInt(block, len_src, extra.len, .usize, .{ .simple = .array_length }); 8060 const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); 8061 try sema.validateArrayElemType(block, elem_type, elem_src); 8062 const uncasted_sentinel = try sema.resolveInst(extra.sentinel); 8063 const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); 8064 const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ .simple = .array_sentinel }); 8065 const array_ty = try sema.pt.arrayType(.{ 8066 .len = len, 8067 .sentinel = sentinel_val.toIntern(), 8068 .child = elem_type.toIntern(), 8069 }); 8070 try sema.checkSentinelType(block, sentinel_src, elem_type); 8071 8072 return Air.internedToRef(array_ty.toIntern()); 8073 } 8074 8075 fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void { 8076 const pt = sema.pt; 8077 const zcu = pt.zcu; 8078 if (elem_type.zigTypeTag(zcu) == .@"opaque") { 8079 return sema.fail(block, elem_src, "array of opaque type '{f}' not allowed", .{elem_type.fmt(pt)}); 8080 } else if (elem_type.zigTypeTag(zcu) == .noreturn) { 8081 return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{}); 8082 } 8083 } 8084 8085 fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8086 const tracy = trace(@src()); 8087 defer tracy.end(); 8088 8089 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8090 if (true) { 8091 return sema.failWithUseOfAsync(block, block.nodeOffset(inst_data.src_node)); 8092 } 8093 const zcu = sema.zcu; 8094 const operand_src = block.src(.{ .node_offset_anyframe_type = inst_data.src_node }); 8095 const return_type = try sema.resolveType(block, operand_src, inst_data.operand); 8096 const anyframe_type = try zcu.anyframeType(return_type); 8097 8098 return Air.internedToRef(anyframe_type.toIntern()); 8099 } 8100 8101 fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8102 const tracy = trace(@src()); 8103 defer tracy.end(); 8104 8105 const pt = sema.pt; 8106 const zcu = pt.zcu; 8107 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8108 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8109 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 8110 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 8111 const error_set = try sema.resolveType(block, lhs_src, extra.lhs); 8112 const payload = try sema.resolveType(block, rhs_src, extra.rhs); 8113 8114 if (error_set.zigTypeTag(zcu) != .error_set) { 8115 return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{ 8116 error_set.fmt(pt), 8117 }); 8118 } 8119 try sema.validateErrorUnionPayloadType(block, payload, rhs_src); 8120 const err_union_ty = try pt.errorUnionType(error_set, payload); 8121 return Air.internedToRef(err_union_ty.toIntern()); 8122 } 8123 8124 fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void { 8125 const pt = sema.pt; 8126 const zcu = pt.zcu; 8127 if (payload_ty.zigTypeTag(zcu) == .@"opaque") { 8128 return sema.fail(block, payload_src, "error union with payload of opaque type '{f}' not allowed", .{ 8129 payload_ty.fmt(pt), 8130 }); 8131 } else if (payload_ty.zigTypeTag(zcu) == .error_set) { 8132 return sema.fail(block, payload_src, "error union with payload of error set type '{f}' not allowed", .{ 8133 payload_ty.fmt(pt), 8134 }); 8135 } 8136 } 8137 8138 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8139 _ = block; 8140 const pt = sema.pt; 8141 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 8142 const name = try pt.zcu.intern_pool.getOrPutString( 8143 sema.gpa, 8144 pt.tid, 8145 inst_data.get(sema.code), 8146 .no_embedded_nulls, 8147 ); 8148 _ = try pt.getErrorValue(name); 8149 // Create an error set type with only this error value, and return the value. 8150 const error_set_type = try pt.singleErrorSetType(name); 8151 return Air.internedToRef((try pt.intern(.{ .err = .{ 8152 .ty = error_set_type.toIntern(), 8153 .name = name, 8154 } }))); 8155 } 8156 8157 fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 8158 const tracy = trace(@src()); 8159 defer tracy.end(); 8160 8161 const pt = sema.pt; 8162 const zcu = pt.zcu; 8163 const ip = &zcu.intern_pool; 8164 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 8165 const src = block.nodeOffset(extra.node); 8166 const operand_src = block.builtinCallArgSrc(extra.node, 0); 8167 const uncasted_operand = try sema.resolveInst(extra.operand); 8168 const operand = try sema.coerce(block, .anyerror, uncasted_operand, operand_src); 8169 const err_int_ty = try pt.errorIntType(); 8170 8171 if (try sema.resolveValue(operand)) |val| { 8172 if (val.isUndef(zcu)) { 8173 return pt.undefRef(err_int_ty); 8174 } 8175 const err_name = ip.indexToKey(val.toIntern()).err.name; 8176 return Air.internedToRef((try pt.intValue( 8177 err_int_ty, 8178 try pt.getErrorValue(err_name), 8179 )).toIntern()); 8180 } 8181 8182 const op_ty = sema.typeOf(uncasted_operand); 8183 switch (try sema.resolveInferredErrorSetTy(block, src, op_ty.toIntern())) { 8184 .anyerror_type => {}, 8185 else => |err_set_ty_index| { 8186 const names = ip.indexToKey(err_set_ty_index).error_set_type.names; 8187 switch (names.len) { 8188 0 => return Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()), 8189 1 => return pt.intRef(err_int_ty, ip.getErrorValueIfExists(names.get(ip)[0]).?), 8190 else => {}, 8191 } 8192 }, 8193 } 8194 8195 try sema.requireRuntimeBlock(block, src, operand_src); 8196 return block.addBitCast(err_int_ty, operand); 8197 } 8198 8199 fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 8200 const tracy = trace(@src()); 8201 defer tracy.end(); 8202 8203 const pt = sema.pt; 8204 const zcu = pt.zcu; 8205 const ip = &zcu.intern_pool; 8206 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 8207 const src = block.nodeOffset(extra.node); 8208 const operand_src = block.builtinCallArgSrc(extra.node, 0); 8209 const uncasted_operand = try sema.resolveInst(extra.operand); 8210 const err_int_ty = try pt.errorIntType(); 8211 const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src); 8212 8213 if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { 8214 const int = try sema.usizeCast(block, operand_src, try value.toUnsignedIntSema(pt)); 8215 if (int > len: { 8216 const mutate = &ip.global_error_set.mutate; 8217 mutate.map.mutex.lock(); 8218 defer mutate.map.mutex.unlock(); 8219 break :len mutate.names.len; 8220 } or int == 0) 8221 return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int}); 8222 return Air.internedToRef((try pt.intern(.{ .err = .{ 8223 .ty = .anyerror_type, 8224 .name = ip.global_error_set.shared.names.acquire().view().items(.@"0")[int - 1], 8225 } }))); 8226 } 8227 try sema.requireRuntimeBlock(block, src, operand_src); 8228 if (block.wantSafety()) { 8229 const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); 8230 const zero_val = Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()); 8231 const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val); 8232 const ok = try block.addBinOp(.bool_and, is_lt_len, is_non_zero); 8233 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 8234 } 8235 return block.addInst(.{ 8236 .tag = .bitcast, 8237 .data = .{ .ty_op = .{ 8238 .ty = .anyerror_type, 8239 .operand = operand, 8240 } }, 8241 }); 8242 } 8243 8244 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8245 const tracy = trace(@src()); 8246 defer tracy.end(); 8247 8248 const pt = sema.pt; 8249 const zcu = pt.zcu; 8250 const ip = &zcu.intern_pool; 8251 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8252 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8253 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 8254 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 8255 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 8256 const lhs = try sema.resolveInst(extra.lhs); 8257 const rhs = try sema.resolveInst(extra.rhs); 8258 if (sema.typeOf(lhs).zigTypeTag(zcu) == .bool and sema.typeOf(rhs).zigTypeTag(zcu) == .bool) { 8259 const msg = msg: { 8260 const msg = try sema.errMsg(lhs_src, "expected error set type, found 'bool'", .{}); 8261 errdefer msg.destroy(sema.gpa); 8262 try sema.errNote(src, msg, "'||' merges error sets; 'or' performs boolean OR", .{}); 8263 break :msg msg; 8264 }; 8265 return sema.failWithOwnedErrorMsg(block, msg); 8266 } 8267 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); 8268 const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); 8269 if (lhs_ty.zigTypeTag(zcu) != .error_set) 8270 return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{lhs_ty.fmt(pt)}); 8271 if (rhs_ty.zigTypeTag(zcu) != .error_set) 8272 return sema.fail(block, rhs_src, "expected error set type, found '{f}'", .{rhs_ty.fmt(pt)}); 8273 8274 // Anything merged with anyerror is anyerror. 8275 if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) { 8276 return .anyerror_type; 8277 } 8278 8279 if (ip.isInferredErrorSetType(lhs_ty.toIntern())) { 8280 switch (try sema.resolveInferredErrorSet(block, src, lhs_ty.toIntern())) { 8281 // isAnyError might have changed from a false negative to a true 8282 // positive after resolution. 8283 .anyerror_type => return .anyerror_type, 8284 else => {}, 8285 } 8286 } 8287 if (ip.isInferredErrorSetType(rhs_ty.toIntern())) { 8288 switch (try sema.resolveInferredErrorSet(block, src, rhs_ty.toIntern())) { 8289 // isAnyError might have changed from a false negative to a true 8290 // positive after resolution. 8291 .anyerror_type => return .anyerror_type, 8292 else => {}, 8293 } 8294 } 8295 8296 const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty); 8297 return Air.internedToRef(err_set_ty.toIntern()); 8298 } 8299 8300 fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8301 _ = block; 8302 const tracy = trace(@src()); 8303 defer tracy.end(); 8304 8305 const pt = sema.pt; 8306 const zcu = pt.zcu; 8307 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 8308 const name = inst_data.get(sema.code); 8309 return Air.internedToRef((try pt.intern(.{ 8310 .enum_literal = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls), 8311 }))); 8312 } 8313 8314 fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref { 8315 const tracy = trace(@src()); 8316 defer tracy.end(); 8317 8318 const pt = sema.pt; 8319 const zcu = pt.zcu; 8320 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8321 const src = block.nodeOffset(inst_data.src_node); 8322 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 8323 const name = try zcu.intern_pool.getOrPutString( 8324 sema.gpa, 8325 pt.tid, 8326 sema.code.nullTerminatedString(extra.field_name_start), 8327 .no_embedded_nulls, 8328 ); 8329 8330 const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison; 8331 8332 const uncoerced_result = res: { 8333 if (orig_ty.toIntern() == .generic_poison_type) { 8334 // Treat this as a normal enum literal. 8335 break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name })); 8336 } 8337 8338 var ty = orig_ty; 8339 while (true) switch (ty.zigTypeTag(zcu)) { 8340 .error_union => ty = ty.errorUnionPayload(zcu), 8341 .optional => ty = ty.optionalChild(zcu), 8342 .pointer => ty = if (ty.isSinglePointer(zcu)) ty.childType(zcu) else break, 8343 .enum_literal, .error_set => { 8344 // Treat this as a normal enum literal. 8345 break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name })); 8346 }, 8347 else => break, 8348 }; 8349 8350 break :res try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src); 8351 }; 8352 8353 // Decl literals cannot lookup runtime `var`s. 8354 if (!try sema.isComptimeKnown(uncoerced_result)) { 8355 return sema.fail(block, src, "decl literal must be comptime-known", .{}); 8356 } 8357 8358 if (do_coerce) { 8359 return sema.coerce(block, orig_ty, uncoerced_result, src); 8360 } else { 8361 return uncoerced_result; 8362 } 8363 } 8364 8365 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8366 const pt = sema.pt; 8367 const zcu = pt.zcu; 8368 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8369 const src = block.nodeOffset(inst_data.src_node); 8370 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 8371 const operand = try sema.resolveInst(inst_data.operand); 8372 const operand_ty = sema.typeOf(operand); 8373 8374 const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(zcu)) { 8375 .@"enum" => operand, 8376 .@"union" => blk: { 8377 try operand_ty.resolveFields(pt); 8378 const tag_ty = operand_ty.unionTagType(zcu) orelse { 8379 return sema.fail( 8380 block, 8381 operand_src, 8382 "untagged union '{f}' cannot be converted to integer", 8383 .{operand_ty.fmt(pt)}, 8384 ); 8385 }; 8386 8387 break :blk try sema.unionToTag(block, tag_ty, operand, operand_src); 8388 }, 8389 else => { 8390 return sema.fail(block, operand_src, "expected enum or tagged union, found '{f}'", .{ 8391 operand_ty.fmt(pt), 8392 }); 8393 }, 8394 }; 8395 const enum_tag_ty = sema.typeOf(enum_tag); 8396 const int_tag_ty = enum_tag_ty.intTagType(zcu); 8397 8398 // TODO: use correct solution 8399 // https://github.com/ziglang/zig/issues/15909 8400 if (enum_tag_ty.enumFieldCount(zcu) == 0 and !enum_tag_ty.isNonexhaustiveEnum(zcu)) { 8401 return sema.fail(block, operand_src, "cannot use @intFromEnum on empty enum '{f}'", .{ 8402 enum_tag_ty.fmt(pt), 8403 }); 8404 } 8405 8406 if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| { 8407 return Air.internedToRef((try pt.getCoerced(opv, int_tag_ty)).toIntern()); 8408 } 8409 8410 if (try sema.resolveValue(enum_tag)) |enum_tag_val| { 8411 if (enum_tag_val.isUndef(zcu)) { 8412 return pt.undefRef(int_tag_ty); 8413 } 8414 8415 const val = try enum_tag_val.intFromEnum(enum_tag_ty, pt); 8416 return Air.internedToRef(val.toIntern()); 8417 } 8418 8419 try sema.requireRuntimeBlock(block, src, operand_src); 8420 return block.addBitCast(int_tag_ty, enum_tag); 8421 } 8422 8423 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8424 const pt = sema.pt; 8425 const zcu = pt.zcu; 8426 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8427 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8428 const src = block.nodeOffset(inst_data.src_node); 8429 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 8430 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt"); 8431 const operand = try sema.resolveInst(extra.rhs); 8432 const operand_ty = sema.typeOf(operand); 8433 8434 if (dest_ty.zigTypeTag(zcu) != .@"enum") { 8435 return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)}); 8436 } 8437 _ = try sema.checkIntType(block, operand_src, operand_ty); 8438 8439 if (try sema.resolveValue(operand)) |int_val| { 8440 if (dest_ty.isNonexhaustiveEnum(zcu)) { 8441 const int_tag_ty = dest_ty.intTagType(zcu); 8442 if (try sema.intFitsInType(int_val, int_tag_ty, null)) { 8443 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); 8444 } 8445 return sema.fail(block, src, "int value '{f}' out of range of non-exhaustive enum '{f}'", .{ 8446 int_val.fmtValueSema(pt, sema), dest_ty.fmt(pt), 8447 }); 8448 } 8449 if (int_val.isUndef(zcu)) { 8450 return sema.failWithUseOfUndef(block, operand_src, null); 8451 } 8452 if (!(try sema.enumHasInt(dest_ty, int_val))) { 8453 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{ 8454 dest_ty.fmt(pt), int_val.fmtValueSema(pt, sema), 8455 }); 8456 } 8457 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); 8458 } 8459 8460 if (dest_ty.intTagType(zcu).zigTypeTag(zcu) == .comptime_int) { 8461 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum }); 8462 } 8463 8464 if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| { 8465 if (block.wantSafety()) { 8466 // The operand is runtime-known but the result is comptime-known. In 8467 // this case we still need a safety check. 8468 const expect_int_val = switch (zcu.intern_pool.indexToKey(opv.toIntern())) { 8469 .enum_tag => |enum_tag| enum_tag.int, 8470 else => unreachable, 8471 }; 8472 const expect_int_coerced = try pt.getCoerced(.fromInterned(expect_int_val), operand_ty); 8473 const ok = try block.addBinOp(.cmp_eq, operand, Air.internedToRef(expect_int_coerced.toIntern())); 8474 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); 8475 } 8476 return Air.internedToRef(opv.toIntern()); 8477 } 8478 8479 try sema.requireRuntimeBlock(block, src, operand_src); 8480 if (block.wantSafety()) { 8481 try sema.preparePanicId(src, .invalid_enum_value); 8482 return block.addTyOp(.intcast_safe, dest_ty, operand); 8483 } 8484 return block.addTyOp(.intcast, dest_ty, operand); 8485 } 8486 8487 /// Pointer in, pointer out. 8488 fn zirOptionalPayloadPtr( 8489 sema: *Sema, 8490 block: *Block, 8491 inst: Zir.Inst.Index, 8492 safety_check: bool, 8493 ) CompileError!Air.Inst.Ref { 8494 const tracy = trace(@src()); 8495 defer tracy.end(); 8496 8497 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8498 const optional_ptr = try sema.resolveInst(inst_data.operand); 8499 const src = block.nodeOffset(inst_data.src_node); 8500 8501 return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); 8502 } 8503 8504 fn analyzeOptionalPayloadPtr( 8505 sema: *Sema, 8506 block: *Block, 8507 src: LazySrcLoc, 8508 optional_ptr: Air.Inst.Ref, 8509 safety_check: bool, 8510 initializing: bool, 8511 ) CompileError!Air.Inst.Ref { 8512 const pt = sema.pt; 8513 const zcu = pt.zcu; 8514 const optional_ptr_ty = sema.typeOf(optional_ptr); 8515 assert(optional_ptr_ty.zigTypeTag(zcu) == .pointer); 8516 8517 const opt_type = optional_ptr_ty.childType(zcu); 8518 if (opt_type.zigTypeTag(zcu) != .optional) { 8519 return sema.failWithExpectedOptionalType(block, src, opt_type); 8520 } 8521 8522 const child_type = opt_type.optionalChild(zcu); 8523 const child_pointer = try pt.ptrTypeSema(.{ 8524 .child = child_type.toIntern(), 8525 .flags = .{ 8526 .is_const = optional_ptr_ty.isConstPtr(zcu), 8527 .address_space = optional_ptr_ty.ptrAddressSpace(zcu), 8528 }, 8529 }); 8530 8531 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| { 8532 if (initializing) { 8533 if (sema.isComptimeMutablePtr(ptr_val)) { 8534 // Set the optional to non-null at comptime. 8535 // If the payload is OPV, we must use that value instead of undef. 8536 const payload_val = try sema.typeHasOnePossibleValue(child_type) orelse try pt.undefValue(child_type); 8537 const opt_val = try pt.intern(.{ .opt = .{ 8538 .ty = opt_type.toIntern(), 8539 .val = payload_val.toIntern(), 8540 } }); 8541 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(opt_val), opt_type); 8542 } else { 8543 // Emit runtime instructions to set the optional non-null bit. 8544 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); 8545 try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr); 8546 } 8547 return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern()); 8548 } 8549 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| { 8550 if (val.isNull(zcu)) { 8551 return sema.fail(block, src, "unable to unwrap null", .{}); 8552 } 8553 return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern()); 8554 } 8555 } 8556 8557 try sema.requireRuntimeBlock(block, src, null); 8558 if (safety_check and block.wantSafety()) { 8559 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); 8560 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 8561 } 8562 8563 if (initializing) { 8564 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); 8565 try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr); 8566 return opt_payload_ptr; 8567 } else { 8568 return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr); 8569 } 8570 } 8571 8572 /// Value in, value out. 8573 fn zirOptionalPayload( 8574 sema: *Sema, 8575 block: *Block, 8576 inst: Zir.Inst.Index, 8577 safety_check: bool, 8578 ) CompileError!Air.Inst.Ref { 8579 const tracy = trace(@src()); 8580 defer tracy.end(); 8581 8582 const pt = sema.pt; 8583 const zcu = pt.zcu; 8584 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8585 const src = block.nodeOffset(inst_data.src_node); 8586 const operand = try sema.resolveInst(inst_data.operand); 8587 const operand_ty = sema.typeOf(operand); 8588 const result_ty = switch (operand_ty.zigTypeTag(zcu)) { 8589 .optional => operand_ty.optionalChild(zcu), 8590 .pointer => t: { 8591 if (operand_ty.ptrSize(zcu) != .c) { 8592 return sema.failWithExpectedOptionalType(block, src, operand_ty); 8593 } 8594 // TODO https://github.com/ziglang/zig/issues/6597 8595 if (true) break :t operand_ty; 8596 const ptr_info = operand_ty.ptrInfo(zcu); 8597 break :t try pt.ptrTypeSema(.{ 8598 .child = ptr_info.child, 8599 .flags = .{ 8600 .alignment = ptr_info.flags.alignment, 8601 .is_const = ptr_info.flags.is_const, 8602 .is_volatile = ptr_info.flags.is_volatile, 8603 .is_allowzero = ptr_info.flags.is_allowzero, 8604 .address_space = ptr_info.flags.address_space, 8605 }, 8606 }); 8607 }, 8608 else => return sema.failWithExpectedOptionalType(block, src, operand_ty), 8609 }; 8610 8611 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 8612 if (val.optionalValue(zcu)) |payload| return Air.internedToRef(payload.toIntern()); 8613 if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{}); 8614 if (safety_check and block.wantSafety()) { 8615 try sema.safetyPanic(block, src, .unwrap_null); 8616 } else { 8617 _ = try block.addNoOp(.unreach); 8618 } 8619 return .unreachable_value; 8620 } 8621 8622 try sema.requireRuntimeBlock(block, src, null); 8623 if (safety_check and block.wantSafety()) { 8624 const is_non_null = try block.addUnOp(.is_non_null, operand); 8625 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 8626 } 8627 return block.addTyOp(.optional_payload, result_ty, operand); 8628 } 8629 8630 /// Value in, value out 8631 fn zirErrUnionPayload( 8632 sema: *Sema, 8633 block: *Block, 8634 inst: Zir.Inst.Index, 8635 ) CompileError!Air.Inst.Ref { 8636 const tracy = trace(@src()); 8637 defer tracy.end(); 8638 8639 const pt = sema.pt; 8640 const zcu = pt.zcu; 8641 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8642 const src = block.nodeOffset(inst_data.src_node); 8643 const operand = try sema.resolveInst(inst_data.operand); 8644 const operand_src = src; 8645 const err_union_ty = sema.typeOf(operand); 8646 if (err_union_ty.zigTypeTag(zcu) != .error_union) { 8647 return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{ 8648 err_union_ty.fmt(pt), 8649 }); 8650 } 8651 return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); 8652 } 8653 8654 fn analyzeErrUnionPayload( 8655 sema: *Sema, 8656 block: *Block, 8657 src: LazySrcLoc, 8658 err_union_ty: Type, 8659 operand: Air.Inst.Ref, 8660 operand_src: LazySrcLoc, 8661 safety_check: bool, 8662 ) CompileError!Air.Inst.Ref { 8663 const pt = sema.pt; 8664 const zcu = pt.zcu; 8665 const payload_ty = err_union_ty.errorUnionPayload(zcu); 8666 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 8667 if (val.getErrorName(zcu).unwrap()) |name| { 8668 return sema.failWithComptimeErrorRetTrace(block, src, name); 8669 } 8670 return Air.internedToRef(zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.payload); 8671 } 8672 8673 try sema.requireRuntimeBlock(block, src, null); 8674 8675 // If the error set has no fields then no safety check is needed. 8676 if (safety_check and block.wantSafety() and 8677 !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) 8678 { 8679 try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); 8680 } 8681 8682 if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_only_value| { 8683 return Air.internedToRef(payload_only_value.toIntern()); 8684 } 8685 8686 return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); 8687 } 8688 8689 /// Pointer in, pointer out. 8690 fn zirErrUnionPayloadPtr( 8691 sema: *Sema, 8692 block: *Block, 8693 inst: Zir.Inst.Index, 8694 ) CompileError!Air.Inst.Ref { 8695 const tracy = trace(@src()); 8696 defer tracy.end(); 8697 8698 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8699 const operand = try sema.resolveInst(inst_data.operand); 8700 const src = block.nodeOffset(inst_data.src_node); 8701 8702 return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); 8703 } 8704 8705 fn analyzeErrUnionPayloadPtr( 8706 sema: *Sema, 8707 block: *Block, 8708 src: LazySrcLoc, 8709 operand: Air.Inst.Ref, 8710 safety_check: bool, 8711 initializing: bool, 8712 ) CompileError!Air.Inst.Ref { 8713 const pt = sema.pt; 8714 const zcu = pt.zcu; 8715 const operand_ty = sema.typeOf(operand); 8716 assert(operand_ty.zigTypeTag(zcu) == .pointer); 8717 8718 if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) { 8719 return sema.fail(block, src, "expected error union type, found '{f}'", .{ 8720 operand_ty.childType(zcu).fmt(pt), 8721 }); 8722 } 8723 8724 const err_union_ty = operand_ty.childType(zcu); 8725 const payload_ty = err_union_ty.errorUnionPayload(zcu); 8726 const operand_pointer_ty = try pt.ptrTypeSema(.{ 8727 .child = payload_ty.toIntern(), 8728 .flags = .{ 8729 .is_const = operand_ty.isConstPtr(zcu), 8730 .address_space = operand_ty.ptrAddressSpace(zcu), 8731 }, 8732 }); 8733 8734 if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| { 8735 if (initializing) { 8736 if (sema.isComptimeMutablePtr(ptr_val)) { 8737 // Set the error union to non-error at comptime. 8738 // If the payload is OPV, we must use that value instead of undef. 8739 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); 8740 const eu_val = try pt.intern(.{ .error_union = .{ 8741 .ty = err_union_ty.toIntern(), 8742 .val = .{ .payload = payload_val.toIntern() }, 8743 } }); 8744 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(eu_val), err_union_ty); 8745 } else { 8746 // Emit runtime instructions to set the error union error code. 8747 try sema.requireRuntimeBlock(block, src, null); 8748 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); 8749 try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr); 8750 } 8751 return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern()); 8752 } 8753 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| { 8754 if (val.getErrorName(zcu).unwrap()) |name| { 8755 return sema.failWithComptimeErrorRetTrace(block, src, name); 8756 } 8757 return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern()); 8758 } 8759 } 8760 8761 try sema.requireRuntimeBlock(block, src, null); 8762 8763 // If the error set has no fields then no safety check is needed. 8764 if (safety_check and block.wantSafety() and 8765 !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) 8766 { 8767 try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); 8768 } 8769 8770 if (initializing) { 8771 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); 8772 try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr); 8773 return eu_payload_ptr; 8774 } else { 8775 return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand); 8776 } 8777 } 8778 8779 /// Value in, value out 8780 fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8781 const tracy = trace(@src()); 8782 defer tracy.end(); 8783 8784 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8785 const src = block.nodeOffset(inst_data.src_node); 8786 const operand = try sema.resolveInst(inst_data.operand); 8787 return sema.analyzeErrUnionCode(block, src, operand); 8788 } 8789 8790 /// If `operand` is comptime-known, asserts that it is an error value rather than a payload value. 8791 fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { 8792 const pt = sema.pt; 8793 const zcu = pt.zcu; 8794 const operand_ty = sema.typeOf(operand); 8795 if (operand_ty.zigTypeTag(zcu) != .error_union) { 8796 return sema.fail(block, src, "expected error union type, found '{f}'", .{ 8797 operand_ty.fmt(pt), 8798 }); 8799 } 8800 8801 const result_ty = operand_ty.errorUnionSet(zcu); 8802 8803 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 8804 return Air.internedToRef((try pt.intern(.{ .err = .{ 8805 .ty = result_ty.toIntern(), 8806 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, 8807 } }))); 8808 } 8809 8810 try sema.requireRuntimeBlock(block, src, null); 8811 return block.addTyOp(.unwrap_errunion_err, result_ty, operand); 8812 } 8813 8814 /// Pointer in, value out 8815 fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8816 const tracy = trace(@src()); 8817 defer tracy.end(); 8818 8819 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8820 const src = block.nodeOffset(inst_data.src_node); 8821 const operand = try sema.resolveInst(inst_data.operand); 8822 return sema.analyzeErrUnionCodePtr(block, src, operand); 8823 } 8824 8825 fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { 8826 const pt = sema.pt; 8827 const zcu = pt.zcu; 8828 const operand_ty = sema.typeOf(operand); 8829 assert(operand_ty.zigTypeTag(zcu) == .pointer); 8830 8831 if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) { 8832 return sema.fail(block, src, "expected error union type, found '{f}'", .{ 8833 operand_ty.childType(zcu).fmt(pt), 8834 }); 8835 } 8836 8837 const result_ty = operand_ty.childType(zcu).errorUnionSet(zcu); 8838 8839 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { 8840 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { 8841 assert(val.getErrorName(zcu) != .none); 8842 return Air.internedToRef((try pt.intern(.{ .err = .{ 8843 .ty = result_ty.toIntern(), 8844 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, 8845 } }))); 8846 } 8847 } 8848 8849 try sema.requireRuntimeBlock(block, src, null); 8850 return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); 8851 } 8852 8853 fn zirFunc( 8854 sema: *Sema, 8855 block: *Block, 8856 inst: Zir.Inst.Index, 8857 inferred_error_set: bool, 8858 ) CompileError!Air.Inst.Ref { 8859 const pt = sema.pt; 8860 const zcu = pt.zcu; 8861 const ip = &zcu.intern_pool; 8862 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8863 const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); 8864 const target = zcu.getTarget(); 8865 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node }); 8866 const src = block.nodeOffset(inst_data.src_node); 8867 8868 var extra_index = extra.end; 8869 8870 const ret_ty: Type = if (extra.data.ret_ty.is_generic) 8871 .generic_poison 8872 else switch (extra.data.ret_ty.body_len) { 8873 0 => .void, 8874 1 => blk: { 8875 const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 8876 extra_index += 1; 8877 break :blk try sema.resolveType(block, ret_ty_src, ret_ty_ref); 8878 }, 8879 else => blk: { 8880 const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_ty.body_len); 8881 extra_index += ret_ty_body.len; 8882 8883 const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, .type, .{ .simple = .function_ret_ty }); 8884 break :blk ret_ty_val.toType(); 8885 }, 8886 }; 8887 8888 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 8889 const has_body = extra.data.body_len != 0; 8890 if (has_body) { 8891 extra_index += extra.data.body_len; 8892 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 8893 } 8894 8895 // If this instruction has a body, then it's a function declaration, and we decide 8896 // the callconv based on whether it is exported. Otherwise, the callconv defaults 8897 // to `.auto`. 8898 const cc: std.builtin.CallingConvention = if (has_body) cc: { 8899 const func_decl_nav = sema.owner.unwrap().nav_val; 8900 const fn_is_exported = exported: { 8901 const decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(ip) orelse return error.AnalysisFail; 8902 const zir_decl = sema.code.getDeclaration(decl_inst); 8903 break :exported zir_decl.linkage == .@"export"; 8904 }; 8905 if (fn_is_exported) { 8906 break :cc target.cCallingConvention() orelse { 8907 // This target has no default C calling convention. We sometimes trigger a similar 8908 // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency, 8909 // let's eval that now and just get the transitive error. (It's guaranteed to error 8910 // because it does the exact `cCallingConvention` call we just did.) 8911 const cc_type = try sema.getBuiltinType(src, .CallingConvention); 8912 _ = try sema.namespaceLookupVal( 8913 block, 8914 LazySrcLoc.unneeded, 8915 cc_type.getNamespaceIndex(zcu), 8916 try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls), 8917 ); 8918 // The above should have errored. 8919 @panic("std.builtin is corrupt"); 8920 }; 8921 } else { 8922 break :cc .auto; 8923 } 8924 } else .auto; 8925 8926 return sema.funcCommon( 8927 block, 8928 inst_data.src_node, 8929 inst, 8930 cc, 8931 ret_ty, 8932 false, 8933 inferred_error_set, 8934 has_body, 8935 src_locs, 8936 0, 8937 false, 8938 ); 8939 } 8940 8941 fn resolveGenericBody( 8942 sema: *Sema, 8943 block: *Block, 8944 src: LazySrcLoc, 8945 body: []const Zir.Inst.Index, 8946 func_inst: Zir.Inst.Index, 8947 dest_ty: Type, 8948 reason: ComptimeReason, 8949 ) !Value { 8950 assert(body.len != 0); 8951 8952 // Make sure any nested param instructions don't clobber our work. 8953 const prev_params = block.params; 8954 block.params = .{}; 8955 defer { 8956 block.params = prev_params; 8957 } 8958 8959 const uncasted = try sema.resolveInlineBody(block, body, func_inst); 8960 const result = try sema.coerce(block, dest_ty, uncasted, src); 8961 return sema.resolveConstDefinedValue(block, src, result, reason); 8962 } 8963 8964 /// Given a library name, examines if the library name should end up in 8965 /// `link.File.Options.windows_libs` table (for example, libc is always 8966 /// specified via dedicated flag `link_libc` instead), 8967 /// and puts it there if it doesn't exist. 8968 /// It also dupes the library name which can then be saved as part of the 8969 /// respective `Decl` (either `ExternFn` or `Var`). 8970 /// The liveness of the duped library name is tied to liveness of `Zcu`. 8971 /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). 8972 pub fn handleExternLibName( 8973 sema: *Sema, 8974 block: *Block, 8975 src_loc: LazySrcLoc, 8976 lib_name: []const u8, 8977 ) CompileError!void { 8978 blk: { 8979 const pt = sema.pt; 8980 const zcu = pt.zcu; 8981 const comp = zcu.comp; 8982 const target = zcu.getTarget(); 8983 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); 8984 if (std.zig.target.isLibCLibName(target, lib_name)) { 8985 if (!comp.config.link_libc) { 8986 return sema.fail( 8987 block, 8988 src_loc, 8989 "dependency on libc must be explicitly specified in the build command", 8990 .{}, 8991 ); 8992 } 8993 break :blk; 8994 } 8995 if (std.zig.target.isLibCxxLibName(target, lib_name)) { 8996 if (!comp.config.link_libcpp) return sema.fail( 8997 block, 8998 src_loc, 8999 "dependency on libc++ must be explicitly specified in the build command", 9000 .{}, 9001 ); 9002 break :blk; 9003 } 9004 if (mem.eql(u8, lib_name, "unwind")) { 9005 if (!comp.config.link_libunwind) return sema.fail( 9006 block, 9007 src_loc, 9008 "dependency on libunwind must be explicitly specified in the build command", 9009 .{}, 9010 ); 9011 break :blk; 9012 } 9013 if (!target.cpu.arch.isWasm() and !block.ownerModule().pic) { 9014 return sema.fail( 9015 block, 9016 src_loc, 9017 "dependency on dynamic library '{s}' requires enabling Position Independent Code; fixed by '-l{s}' or '-fPIC'", 9018 .{ lib_name, lib_name }, 9019 ); 9020 } 9021 comp.addLinkLib(lib_name) catch |err| { 9022 return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ 9023 lib_name, @errorName(err), 9024 }); 9025 }; 9026 } 9027 } 9028 9029 /// These are calling conventions that are confirmed to work with variadic functions. 9030 /// Any calling conventions not included here are either not yet verified to work with variadic 9031 /// functions or there are no more other calling conventions that support variadic functions. 9032 const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention.Tag{ 9033 .x86_64_sysv, 9034 .x86_64_win, 9035 .x86_sysv, 9036 .x86_win, 9037 .aarch64_aapcs, 9038 .aarch64_aapcs_darwin, 9039 .aarch64_aapcs_win, 9040 .aarch64_vfabi, 9041 .aarch64_vfabi_sve, 9042 .arm_aapcs, 9043 .arm_aapcs_vfp, 9044 .mips64_n64, 9045 .mips64_n32, 9046 .mips_o32, 9047 .riscv64_lp64, 9048 .riscv64_lp64_v, 9049 .riscv32_ilp32, 9050 .riscv32_ilp32_v, 9051 .sparc64_sysv, 9052 .sparc_sysv, 9053 .powerpc64_elf, 9054 .powerpc64_elf_altivec, 9055 .powerpc64_elf_v2, 9056 .powerpc_sysv, 9057 .powerpc_sysv_altivec, 9058 .powerpc_aix, 9059 .powerpc_aix_altivec, 9060 .wasm_mvp, 9061 .arc_sysv, 9062 .avr_gnu, 9063 .bpf_std, 9064 .csky_sysv, 9065 .hexagon_sysv, 9066 .hexagon_sysv_hvx, 9067 .lanai_sysv, 9068 .loongarch64_lp64, 9069 .loongarch32_ilp32, 9070 .m68k_sysv, 9071 .m68k_gnu, 9072 .m68k_rtd, 9073 .msp430_eabi, 9074 .s390x_sysv, 9075 .s390x_sysv_vx, 9076 .ve_sysv, 9077 .xcore_xs1, 9078 .xcore_xs2, 9079 .xtensa_call0, 9080 .xtensa_windowed, 9081 }; 9082 fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool { 9083 return for (calling_conventions_supporting_var_args) |supported_cc| { 9084 if (cc == supported_cc) return true; 9085 } else false; 9086 } 9087 fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void { 9088 const CallingConventionsSupportingVarArgsList = struct { 9089 arch: std.Target.Cpu.Arch, 9090 pub fn format(ctx: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { 9091 var first = true; 9092 for (calling_conventions_supporting_var_args) |cc_inner| { 9093 for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| { 9094 if (supported_arch == ctx.arch) break; 9095 } else continue; // callconv not supported by this arch 9096 if (!first) { 9097 try w.writeAll(", "); 9098 } 9099 first = false; 9100 try w.print("'{s}'", .{@tagName(cc_inner)}); 9101 } 9102 } 9103 }; 9104 9105 if (!callConvSupportsVarArgs(cc)) { 9106 return sema.failWithOwnedErrorMsg(block, msg: { 9107 const msg = try sema.errMsg(src, "variadic function does not support '{s}' calling convention", .{@tagName(cc)}); 9108 errdefer msg.destroy(sema.gpa); 9109 const target = sema.pt.zcu.getTarget(); 9110 try sema.errNote(src, msg, "supported calling conventions: {f}", .{CallingConventionsSupportingVarArgsList{ .arch = target.cpu.arch }}); 9111 break :msg msg; 9112 }); 9113 } 9114 } 9115 9116 fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool { 9117 return switch (cc) { 9118 .naked, 9119 9120 .arm_interrupt, 9121 .avr_interrupt, 9122 .avr_signal, 9123 .csky_interrupt, 9124 .m68k_interrupt, 9125 .mips_interrupt, 9126 .mips64_interrupt, 9127 .riscv32_interrupt, 9128 .riscv64_interrupt, 9129 .x86_interrupt, 9130 .x86_64_interrupt, 9131 9132 .amdgcn_kernel, 9133 .nvptx_kernel, 9134 .spirv_kernel, 9135 .spirv_fragment, 9136 .spirv_vertex, 9137 => false, 9138 9139 else => true, 9140 }; 9141 } 9142 9143 fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void { 9144 const pt = sema.pt; 9145 const zcu = pt.zcu; 9146 const target = zcu.getTarget(); 9147 9148 if (!peer_ty.isPtrAtRuntime(zcu)) { 9149 return; 9150 } 9151 9152 const as = peer_ty.ptrAddressSpace(zcu); 9153 if (!target_util.arePointersLogical(target, as)) { 9154 return; 9155 } 9156 9157 return sema.failWithOwnedErrorMsg(block, msg: { 9158 const msg = try sema.errMsg(src, "value with non-mergable pointer type '{f}' depends on runtime control flow", .{peer_ty.fmt(pt)}); 9159 errdefer msg.destroy(sema.gpa); 9160 9161 const runtime_src = block.runtime_cond orelse block.runtime_loop.?; 9162 try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); 9163 9164 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm); 9165 try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target {s}-{s} by compiler backend {s}", .{ 9166 @tagName(as), 9167 @tagName(target.cpu.arch.family()), 9168 @tagName(target.os.tag), 9169 @tagName(backend), 9170 }); 9171 9172 break :msg msg; 9173 }); 9174 } 9175 9176 const Section = union(enum) { 9177 generic, 9178 default, 9179 explicit: InternPool.NullTerminatedString, 9180 }; 9181 9182 fn funcCommon( 9183 sema: *Sema, 9184 block: *Block, 9185 src_node_offset: std.zig.Ast.Node.Offset, 9186 func_inst: Zir.Inst.Index, 9187 cc: std.builtin.CallingConvention, 9188 /// this might be Type.generic_poison 9189 bare_return_type: Type, 9190 var_args: bool, 9191 inferred_error_set: bool, 9192 has_body: bool, 9193 src_locs: Zir.Inst.Func.SrcLocs, 9194 noalias_bits: u32, 9195 is_noinline: bool, 9196 ) CompileError!Air.Inst.Ref { 9197 const pt = sema.pt; 9198 const zcu = pt.zcu; 9199 const gpa = sema.gpa; 9200 const target = zcu.getTarget(); 9201 const ip = &zcu.intern_pool; 9202 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset }); 9203 const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset }); 9204 const func_src = block.nodeOffset(src_node_offset); 9205 9206 const ret_ty_requires_comptime = try bare_return_type.comptimeOnlySema(pt); 9207 var is_generic = bare_return_type.isGenericPoison() or ret_ty_requires_comptime; 9208 9209 var comptime_bits: u32 = 0; 9210 for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| { 9211 const param_ty: Type = .fromInterned(param_ty_ip); 9212 const is_noalias = blk: { 9213 const index = std.math.cast(u5, i) orelse break :blk false; 9214 break :blk @as(u1, @truncate(noalias_bits >> index)) != 0; 9215 }; 9216 const param_src = block.src(.{ .fn_proto_param = .{ 9217 .fn_proto_node_offset = src_node_offset, 9218 .param_index = @intCast(i), 9219 } }); 9220 const param_ty_comptime = try param_ty.comptimeOnlySema(pt); 9221 const param_ty_generic = param_ty.isGenericPoison(); 9222 if (param_is_comptime or param_ty_comptime or param_ty_generic) { 9223 is_generic = true; 9224 } 9225 if (param_is_comptime) { 9226 comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error 9227 } 9228 if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(cc)) { 9229 return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); 9230 } 9231 if (param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc)) { 9232 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); 9233 } 9234 if (!param_ty.isValidParamType(zcu)) { 9235 const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; 9236 return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{ 9237 opaque_str, param_ty.fmt(pt), 9238 }); 9239 } 9240 if (!param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc) and !try sema.validateExternType(param_ty, .param_ty)) { 9241 const msg = msg: { 9242 const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{s}'", .{ 9243 param_ty.fmt(pt), @tagName(cc), 9244 }); 9245 errdefer msg.destroy(sema.gpa); 9246 9247 try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty); 9248 9249 try sema.addDeclaredHereNote(msg, param_ty); 9250 break :msg msg; 9251 }; 9252 return sema.failWithOwnedErrorMsg(block, msg); 9253 } 9254 if (param_ty_comptime and !param_is_comptime and has_body and !block.isComptime()) { 9255 const msg = msg: { 9256 const msg = try sema.errMsg(param_src, "parameter of type '{f}' must be declared comptime", .{ 9257 param_ty.fmt(pt), 9258 }); 9259 errdefer msg.destroy(sema.gpa); 9260 9261 try sema.explainWhyTypeIsComptime(msg, param_src, param_ty); 9262 9263 try sema.addDeclaredHereNote(msg, param_ty); 9264 break :msg msg; 9265 }; 9266 return sema.failWithOwnedErrorMsg(block, msg); 9267 } 9268 if (!param_ty_generic and is_noalias and 9269 !(param_ty.zigTypeTag(zcu) == .pointer or param_ty.isPtrLikeOptional(zcu))) 9270 { 9271 return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); 9272 } 9273 switch (cc) { 9274 .x86_64_interrupt, .x86_interrupt => { 9275 const err_code_size = target.ptrBitWidth(); 9276 switch (i) { 9277 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{s}' calling convention must be a pointer type", .{@tagName(cc)}), 9278 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{s}' calling convention must be a {d}-bit integer", .{ @tagName(cc), err_code_size }), 9279 else => return sema.fail(block, param_src, "'{s}' calling convention supports up to 2 parameters, found {d}", .{ @tagName(cc), i + 1 }), 9280 } 9281 }, 9282 .arm_interrupt, 9283 .mips64_interrupt, 9284 .mips_interrupt, 9285 .riscv64_interrupt, 9286 .riscv32_interrupt, 9287 .avr_interrupt, 9288 .csky_interrupt, 9289 .m68k_interrupt, 9290 .avr_signal, 9291 => return sema.fail(block, param_src, "parameters are not allowed with '{s}' calling convention", .{@tagName(cc)}), 9292 else => {}, 9293 } 9294 } 9295 9296 if (var_args) { 9297 if (is_generic) { 9298 return sema.fail(block, func_src, "generic function cannot be variadic", .{}); 9299 } 9300 const va_args_src = block.src(.{ 9301 .fn_proto_param = .{ 9302 .fn_proto_node_offset = src_node_offset, 9303 .param_index = @intCast(block.params.len), // va_arg must be the last parameter 9304 }, 9305 }); 9306 try sema.checkCallConvSupportsVarArgs(block, va_args_src, cc); 9307 } 9308 9309 const ret_poison = bare_return_type.isGenericPoison(); 9310 9311 const param_types = block.params.items(.ty); 9312 9313 if (inferred_error_set) { 9314 assert(has_body); 9315 if (!ret_poison) 9316 try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); 9317 const func_index = try ip.getFuncDeclIes(gpa, pt.tid, .{ 9318 .owner_nav = sema.owner.unwrap().nav_val, 9319 9320 .param_types = param_types, 9321 .noalias_bits = noalias_bits, 9322 .comptime_bits = comptime_bits, 9323 .bare_return_type = bare_return_type.toIntern(), 9324 .cc = cc, 9325 .is_var_args = var_args, 9326 .is_generic = is_generic, 9327 .is_noinline = is_noinline, 9328 9329 .zir_body_inst = try block.trackZir(func_inst), 9330 .lbrace_line = src_locs.lbrace_line, 9331 .rbrace_line = src_locs.rbrace_line, 9332 .lbrace_column = @as(u16, @truncate(src_locs.columns)), 9333 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), 9334 }); 9335 return finishFunc( 9336 sema, 9337 block, 9338 func_index, 9339 .none, 9340 ret_poison, 9341 bare_return_type, 9342 ret_ty_src, 9343 cc, 9344 ret_ty_requires_comptime, 9345 func_inst, 9346 cc_src, 9347 is_noinline, 9348 ); 9349 } 9350 9351 const func_ty = try ip.getFuncType(gpa, pt.tid, .{ 9352 .param_types = param_types, 9353 .noalias_bits = noalias_bits, 9354 .comptime_bits = comptime_bits, 9355 .return_type = bare_return_type.toIntern(), 9356 .cc = cc, 9357 .is_var_args = var_args, 9358 .is_generic = is_generic, 9359 .is_noinline = is_noinline, 9360 }); 9361 9362 if (has_body) { 9363 const func_index = try ip.getFuncDecl(gpa, pt.tid, .{ 9364 .owner_nav = sema.owner.unwrap().nav_val, 9365 .ty = func_ty, 9366 .cc = cc, 9367 .is_noinline = is_noinline, 9368 .zir_body_inst = try block.trackZir(func_inst), 9369 .lbrace_line = src_locs.lbrace_line, 9370 .rbrace_line = src_locs.rbrace_line, 9371 .lbrace_column = @as(u16, @truncate(src_locs.columns)), 9372 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), 9373 }); 9374 return finishFunc( 9375 sema, 9376 block, 9377 func_index, 9378 func_ty, 9379 ret_poison, 9380 bare_return_type, 9381 ret_ty_src, 9382 cc, 9383 ret_ty_requires_comptime, 9384 func_inst, 9385 cc_src, 9386 is_noinline, 9387 ); 9388 } 9389 9390 return finishFunc( 9391 sema, 9392 block, 9393 .none, 9394 func_ty, 9395 ret_poison, 9396 bare_return_type, 9397 ret_ty_src, 9398 cc, 9399 ret_ty_requires_comptime, 9400 func_inst, 9401 cc_src, 9402 is_noinline, 9403 ); 9404 } 9405 9406 fn finishFunc( 9407 sema: *Sema, 9408 block: *Block, 9409 opt_func_index: InternPool.Index, 9410 func_ty: InternPool.Index, 9411 ret_poison: bool, 9412 bare_return_type: Type, 9413 ret_ty_src: LazySrcLoc, 9414 cc_resolved: std.builtin.CallingConvention, 9415 ret_ty_requires_comptime: bool, 9416 func_inst: Zir.Inst.Index, 9417 cc_src: LazySrcLoc, 9418 is_noinline: bool, 9419 ) CompileError!Air.Inst.Ref { 9420 const pt = sema.pt; 9421 const zcu = pt.zcu; 9422 const ip = &zcu.intern_pool; 9423 const gpa = sema.gpa; 9424 9425 const return_type: Type = if (opt_func_index == .none or ret_poison) 9426 bare_return_type 9427 else 9428 .fromInterned(ip.funcTypeReturnType(ip.typeOf(opt_func_index))); 9429 9430 if (!return_type.isValidReturnType(zcu)) { 9431 const opaque_str = if (return_type.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; 9432 return sema.fail(block, ret_ty_src, "{s}return type '{f}' not allowed", .{ 9433 opaque_str, return_type.fmt(pt), 9434 }); 9435 } 9436 if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(cc_resolved) and 9437 !try sema.validateExternType(return_type, .ret_ty)) 9438 { 9439 const msg = msg: { 9440 const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{s}'", .{ 9441 return_type.fmt(pt), @tagName(cc_resolved), 9442 }); 9443 errdefer msg.destroy(gpa); 9444 9445 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, return_type, .ret_ty); 9446 9447 try sema.addDeclaredHereNote(msg, return_type); 9448 break :msg msg; 9449 }; 9450 return sema.failWithOwnedErrorMsg(block, msg); 9451 } 9452 9453 // If the return type is comptime-only but not dependent on parameters then 9454 // all parameter types also need to be comptime. 9455 if (opt_func_index != .none and ret_ty_requires_comptime and !block.isComptime()) comptime_check: { 9456 for (block.params.items(.is_comptime)) |is_comptime| { 9457 if (!is_comptime) break; 9458 } else break :comptime_check; 9459 9460 const msg = try sema.errMsg( 9461 ret_ty_src, 9462 "function with comptime-only return type '{f}' requires all parameters to be comptime", 9463 .{return_type.fmt(pt)}, 9464 ); 9465 errdefer msg.destroy(sema.gpa); 9466 try sema.explainWhyTypeIsComptime(msg, ret_ty_src, return_type); 9467 9468 const tags = sema.code.instructions.items(.tag); 9469 const data = sema.code.instructions.items(.data); 9470 const param_body = sema.code.getParamBody(func_inst); 9471 for ( 9472 block.params.items(.is_comptime), 9473 block.params.items(.name), 9474 param_body[0..block.params.len], 9475 ) |is_comptime, name_nts, param_index| { 9476 if (!is_comptime) { 9477 const param_src = block.tokenOffset(switch (tags[@intFromEnum(param_index)]) { 9478 .param => data[@intFromEnum(param_index)].pl_tok.src_tok, 9479 .param_anytype => data[@intFromEnum(param_index)].str_tok.src_tok, 9480 else => unreachable, 9481 }); 9482 const name = sema.code.nullTerminatedString(name_nts); 9483 if (name.len != 0) { 9484 try sema.errNote(param_src, msg, "param '{s}' is required to be comptime", .{name}); 9485 } else { 9486 try sema.errNote(param_src, msg, "param is required to be comptime", .{}); 9487 } 9488 } 9489 } 9490 return sema.failWithOwnedErrorMsg(block, msg); 9491 } 9492 9493 validate_incoming_stack_align: { 9494 const a: u64 = switch (cc_resolved) { 9495 inline else => |payload| if (@TypeOf(payload) != void and @hasField(@TypeOf(payload), "incoming_stack_alignment")) 9496 payload.incoming_stack_alignment orelse break :validate_incoming_stack_align 9497 else 9498 break :validate_incoming_stack_align, 9499 }; 9500 if (!std.math.isPowerOfTwo(a)) { 9501 return sema.fail(block, cc_src, "calling convention incoming stack alignment '{d}' is not a power of two", .{a}); 9502 } 9503 } 9504 9505 switch (cc_resolved) { 9506 .x86_64_interrupt, 9507 .x86_interrupt, 9508 .arm_interrupt, 9509 .mips64_interrupt, 9510 .mips_interrupt, 9511 .riscv64_interrupt, 9512 .riscv32_interrupt, 9513 .avr_interrupt, 9514 .csky_interrupt, 9515 .m68k_interrupt, 9516 .avr_signal, 9517 => if (return_type.zigTypeTag(zcu) != .void and return_type.zigTypeTag(zcu) != .noreturn) { 9518 return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(cc_resolved)}); 9519 }, 9520 .@"inline" => if (is_noinline) { 9521 return sema.fail(block, cc_src, "'noinline' function cannot have calling convention 'inline'", .{}); 9522 }, 9523 else => {}, 9524 } 9525 9526 switch (zcu.callconvSupported(cc_resolved)) { 9527 .ok => {}, 9528 .bad_arch => |allowed_archs| { 9529 const ArchListFormatter = struct { 9530 archs: []const std.Target.Cpu.Arch, 9531 pub fn format(formatter: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { 9532 for (formatter.archs, 0..) |arch, i| { 9533 if (i != 0) 9534 try w.writeAll(", "); 9535 try w.print("'{s}'", .{@tagName(arch)}); 9536 } 9537 } 9538 }; 9539 return sema.fail(block, cc_src, "calling convention '{s}' only available on architectures {f}", .{ 9540 @tagName(cc_resolved), 9541 ArchListFormatter{ .archs = allowed_archs }, 9542 }); 9543 }, 9544 .bad_backend => |bad_backend| return sema.fail(block, cc_src, "calling convention '{s}' not supported by compiler backend '{s}'", .{ 9545 @tagName(cc_resolved), 9546 @tagName(bad_backend), 9547 }), 9548 } 9549 9550 return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty); 9551 } 9552 9553 fn zirParam( 9554 sema: *Sema, 9555 block: *Block, 9556 inst: Zir.Inst.Index, 9557 comptime_syntax: bool, 9558 ) CompileError!void { 9559 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; 9560 const src = block.tokenOffset(inst_data.src_tok); 9561 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); 9562 const param_name: Zir.NullTerminatedString = extra.data.name; 9563 const body = sema.code.bodySlice(extra.end, extra.data.type.body_len); 9564 9565 const param_ty: Type = if (extra.data.type.is_generic) .generic_poison else ty: { 9566 // Make sure any nested param instructions don't clobber our work. 9567 const prev_params = block.params; 9568 block.params = .{}; 9569 defer { 9570 block.params = prev_params; 9571 } 9572 9573 const param_ty_inst = try sema.resolveInlineBody(block, body, inst); 9574 break :ty try sema.analyzeAsType(block, src, param_ty_inst); 9575 }; 9576 9577 try block.params.append(sema.arena, .{ 9578 .ty = param_ty.toIntern(), 9579 .is_comptime = comptime_syntax, 9580 .name = param_name, 9581 }); 9582 } 9583 9584 fn zirParamAnytype( 9585 sema: *Sema, 9586 block: *Block, 9587 inst: Zir.Inst.Index, 9588 comptime_syntax: bool, 9589 ) CompileError!void { 9590 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 9591 const param_name: Zir.NullTerminatedString = inst_data.start; 9592 9593 try block.params.append(sema.arena, .{ 9594 .ty = .generic_poison_type, 9595 .is_comptime = comptime_syntax, 9596 .name = param_name, 9597 }); 9598 } 9599 9600 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9601 const tracy = trace(@src()); 9602 defer tracy.end(); 9603 9604 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9605 const src = block.nodeOffset(inst_data.src_node); 9606 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; 9607 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false); 9608 } 9609 9610 fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9611 const tracy = trace(@src()); 9612 defer tracy.end(); 9613 9614 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9615 const src = block.nodeOffset(inst_data.src_node); 9616 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; 9617 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true); 9618 } 9619 9620 fn analyzeAs( 9621 sema: *Sema, 9622 block: *Block, 9623 src: LazySrcLoc, 9624 zir_dest_type: Zir.Inst.Ref, 9625 zir_operand: Zir.Inst.Ref, 9626 no_cast_to_comptime_int: bool, 9627 ) CompileError!Air.Inst.Ref { 9628 const pt = sema.pt; 9629 const zcu = pt.zcu; 9630 const operand = try sema.resolveInst(zir_operand); 9631 const dest_ty = try sema.resolveTypeOrPoison(block, src, zir_dest_type) orelse return operand; 9632 switch (dest_ty.zigTypeTag(zcu)) { 9633 .@"opaque" => return sema.fail(block, src, "cannot cast to opaque type '{f}'", .{dest_ty.fmt(pt)}), 9634 .noreturn => return sema.fail(block, src, "cannot cast to noreturn", .{}), 9635 else => {}, 9636 } 9637 9638 const is_ret = if (zir_dest_type.toIndex()) |ptr_index| 9639 sema.code.instructions.items(.tag)[@intFromEnum(ptr_index)] == .ret_type 9640 else 9641 false; 9642 return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) { 9643 error.NotCoercible => unreachable, 9644 else => |e| return e, 9645 }; 9646 } 9647 9648 fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9649 const tracy = trace(@src()); 9650 defer tracy.end(); 9651 9652 const pt = sema.pt; 9653 const zcu = pt.zcu; 9654 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 9655 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0); 9656 const operand = try sema.resolveInst(inst_data.operand); 9657 const operand_ty = sema.typeOf(operand); 9658 const ptr_ty = operand_ty.scalarType(zcu); 9659 const is_vector = operand_ty.zigTypeTag(zcu) == .vector; 9660 if (!ptr_ty.isPtrAtRuntime(zcu)) { 9661 return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}); 9662 } 9663 const pointee_ty = ptr_ty.childType(zcu); 9664 if (try ptr_ty.comptimeOnlySema(pt)) { 9665 const msg = msg: { 9666 const msg = try sema.errMsg(ptr_src, "comptime-only type '{f}' has no pointer address", .{pointee_ty.fmt(pt)}); 9667 errdefer msg.destroy(sema.gpa); 9668 try sema.explainWhyTypeIsComptime(msg, ptr_src, pointee_ty); 9669 break :msg msg; 9670 }; 9671 return sema.failWithOwnedErrorMsg(block, msg); 9672 } 9673 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; 9674 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .usize_type, .len = len }) else .usize; 9675 9676 if (try sema.resolveValue(operand)) |operand_val| ct: { 9677 if (!is_vector) { 9678 if (operand_val.isUndef(zcu)) { 9679 return .undef_usize; 9680 } 9681 const addr = try operand_val.getUnsignedIntSema(pt) orelse { 9682 // Wasn't an integer pointer. This is a runtime operation. 9683 break :ct; 9684 }; 9685 return Air.internedToRef((try pt.intValue( 9686 .usize, 9687 addr, 9688 )).toIntern()); 9689 } 9690 const new_elems = try sema.arena.alloc(InternPool.Index, len); 9691 for (new_elems, 0..) |*new_elem, i| { 9692 const ptr_val = try operand_val.elemValue(pt, i); 9693 if (ptr_val.isUndef(zcu)) { 9694 new_elem.* = .undef_usize; 9695 continue; 9696 } 9697 const addr = try ptr_val.getUnsignedIntSema(pt) orelse { 9698 // A vector element wasn't an integer pointer. This is a runtime operation. 9699 break :ct; 9700 }; 9701 new_elem.* = (try pt.intValue( 9702 .usize, 9703 addr, 9704 )).toIntern(); 9705 } 9706 return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); 9707 } 9708 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src); 9709 try sema.validateRuntimeValue(block, ptr_src, operand); 9710 try sema.checkLogicalPtrOperation(block, ptr_src, ptr_ty); 9711 return block.addBitCast(dest_ty, operand); 9712 } 9713 9714 fn zirFieldPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9715 const tracy = trace(@src()); 9716 defer tracy.end(); 9717 9718 const pt = sema.pt; 9719 const zcu = pt.zcu; 9720 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9721 const src = block.nodeOffset(inst_data.src_node); 9722 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); 9723 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9724 const field_name = try zcu.intern_pool.getOrPutString( 9725 sema.gpa, 9726 pt.tid, 9727 sema.code.nullTerminatedString(extra.field_name_start), 9728 .no_embedded_nulls, 9729 ); 9730 const object_ptr = try sema.resolveInst(extra.lhs); 9731 return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src); 9732 } 9733 9734 fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9735 const tracy = trace(@src()); 9736 defer tracy.end(); 9737 9738 const pt = sema.pt; 9739 const zcu = pt.zcu; 9740 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9741 const src = block.nodeOffset(inst_data.src_node); 9742 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); 9743 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9744 const field_name = try zcu.intern_pool.getOrPutString( 9745 sema.gpa, 9746 pt.tid, 9747 sema.code.nullTerminatedString(extra.field_name_start), 9748 .no_embedded_nulls, 9749 ); 9750 const object_ptr = try sema.resolveInst(extra.lhs); 9751 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); 9752 } 9753 9754 fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9755 const tracy = trace(@src()); 9756 defer tracy.end(); 9757 9758 const pt = sema.pt; 9759 const zcu = pt.zcu; 9760 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9761 const src = block.nodeOffset(inst_data.src_node); 9762 const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node }); 9763 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9764 const field_name = try zcu.intern_pool.getOrPutString( 9765 sema.gpa, 9766 pt.tid, 9767 sema.code.nullTerminatedString(extra.field_name_start), 9768 .no_embedded_nulls, 9769 ); 9770 const object_ptr = try sema.resolveInst(extra.lhs); 9771 const struct_ty = sema.typeOf(object_ptr).childType(zcu); 9772 switch (struct_ty.zigTypeTag(zcu)) { 9773 .@"struct", .@"union" => { 9774 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true); 9775 }, 9776 else => { 9777 return sema.failWithStructInitNotSupported(block, src, struct_ty); 9778 }, 9779 } 9780 } 9781 9782 fn zirFieldPtrNamedLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9783 const tracy = trace(@src()); 9784 defer tracy.end(); 9785 9786 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9787 const src = block.nodeOffset(inst_data.src_node); 9788 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); 9789 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; 9790 const object_ptr = try sema.resolveInst(extra.lhs); 9791 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); 9792 return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src); 9793 } 9794 9795 fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9796 const tracy = trace(@src()); 9797 defer tracy.end(); 9798 9799 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9800 const src = block.nodeOffset(inst_data.src_node); 9801 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); 9802 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; 9803 const object_ptr = try sema.resolveInst(extra.lhs); 9804 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); 9805 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); 9806 } 9807 9808 fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9809 const tracy = trace(@src()); 9810 defer tracy.end(); 9811 9812 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9813 const src = block.nodeOffset(inst_data.src_node); 9814 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 9815 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9816 9817 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast"); 9818 const operand = try sema.resolveInst(extra.rhs); 9819 9820 return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src); 9821 } 9822 9823 fn intCast( 9824 sema: *Sema, 9825 block: *Block, 9826 src: LazySrcLoc, 9827 dest_ty: Type, 9828 dest_ty_src: LazySrcLoc, 9829 operand: Air.Inst.Ref, 9830 operand_src: LazySrcLoc, 9831 ) CompileError!Air.Inst.Ref { 9832 const pt = sema.pt; 9833 const zcu = pt.zcu; 9834 const operand_ty = sema.typeOf(operand); 9835 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src); 9836 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 9837 9838 if (try sema.isComptimeKnown(operand)) { 9839 return sema.coerce(block, dest_ty, operand, operand_src); 9840 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { 9841 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); 9842 } 9843 9844 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); 9845 const is_vector = dest_ty.zigTypeTag(zcu) == .vector; 9846 9847 if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| { 9848 // requirement: intCast(u0, input) iff input == 0 9849 if (block.wantSafety()) { 9850 try sema.requireRuntimeBlock(block, src, operand_src); 9851 const wanted_info = dest_scalar_ty.intInfo(zcu); 9852 const wanted_bits = wanted_info.bits; 9853 9854 if (wanted_bits == 0) { 9855 const ok = if (is_vector) ok: { 9856 const zeros = try sema.splat(operand_ty, try pt.intValue(operand_scalar_ty, 0)); 9857 const zero_inst = Air.internedToRef(zeros.toIntern()); 9858 const is_in_range = try block.addCmpVector(operand, zero_inst, .eq); 9859 const all_in_range = try block.addReduce(is_in_range, .And); 9860 break :ok all_in_range; 9861 } else ok: { 9862 const zero_inst = Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern()); 9863 const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst); 9864 break :ok is_in_range; 9865 }; 9866 try sema.addSafetyCheck(block, src, ok, .integer_out_of_bounds); 9867 } 9868 } 9869 9870 return Air.internedToRef(opv.toIntern()); 9871 } 9872 9873 try sema.requireRuntimeBlock(block, src, operand_src); 9874 if (block.wantSafety()) { 9875 try sema.preparePanicId(src, .integer_out_of_bounds); 9876 return block.addTyOp(.intcast_safe, dest_ty, operand); 9877 } 9878 return block.addTyOp(.intcast, dest_ty, operand); 9879 } 9880 9881 fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9882 const tracy = trace(@src()); 9883 defer tracy.end(); 9884 9885 const pt = sema.pt; 9886 const zcu = pt.zcu; 9887 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9888 const src = block.nodeOffset(inst_data.src_node); 9889 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 9890 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9891 9892 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast"); 9893 const operand = try sema.resolveInst(extra.rhs); 9894 const operand_ty = sema.typeOf(operand); 9895 switch (dest_ty.zigTypeTag(zcu)) { 9896 .@"anyframe", 9897 .comptime_float, 9898 .comptime_int, 9899 .enum_literal, 9900 .error_set, 9901 .error_union, 9902 .@"fn", 9903 .frame, 9904 .noreturn, 9905 .null, 9906 .@"opaque", 9907 .optional, 9908 .type, 9909 .undefined, 9910 .void, 9911 => return sema.fail(block, src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}), 9912 9913 .@"enum" => { 9914 const msg = msg: { 9915 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}); 9916 errdefer msg.destroy(sema.gpa); 9917 switch (operand_ty.zigTypeTag(zcu)) { 9918 .int, .comptime_int => try sema.errNote(src, msg, "use @enumFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}), 9919 else => {}, 9920 } 9921 9922 break :msg msg; 9923 }; 9924 return sema.failWithOwnedErrorMsg(block, msg); 9925 }, 9926 9927 .pointer => { 9928 const msg = msg: { 9929 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}); 9930 errdefer msg.destroy(sema.gpa); 9931 switch (operand_ty.zigTypeTag(zcu)) { 9932 .int, .comptime_int => try sema.errNote(src, msg, "use @ptrFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}), 9933 .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}), 9934 else => {}, 9935 } 9936 9937 break :msg msg; 9938 }; 9939 return sema.failWithOwnedErrorMsg(block, msg); 9940 }, 9941 .@"struct", .@"union" => if (dest_ty.containerLayout(zcu) == .auto) { 9942 const container = switch (dest_ty.zigTypeTag(zcu)) { 9943 .@"struct" => "struct", 9944 .@"union" => "union", 9945 else => unreachable, 9946 }; 9947 return sema.fail(block, src, "cannot @bitCast to '{f}'; {s} does not have a guaranteed in-memory layout", .{ 9948 dest_ty.fmt(pt), container, 9949 }); 9950 }, 9951 9952 .array, 9953 .bool, 9954 .float, 9955 .int, 9956 .vector, 9957 => {}, 9958 } 9959 switch (operand_ty.zigTypeTag(zcu)) { 9960 .@"anyframe", 9961 .comptime_float, 9962 .comptime_int, 9963 .enum_literal, 9964 .error_set, 9965 .error_union, 9966 .@"fn", 9967 .frame, 9968 .noreturn, 9969 .null, 9970 .@"opaque", 9971 .optional, 9972 .type, 9973 .undefined, 9974 .void, 9975 => return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}), 9976 9977 .@"enum" => { 9978 const msg = msg: { 9979 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}); 9980 errdefer msg.destroy(sema.gpa); 9981 switch (dest_ty.zigTypeTag(zcu)) { 9982 .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromEnum to cast to '{f}'", .{dest_ty.fmt(pt)}), 9983 else => {}, 9984 } 9985 9986 break :msg msg; 9987 }; 9988 return sema.failWithOwnedErrorMsg(block, msg); 9989 }, 9990 .pointer => { 9991 const msg = msg: { 9992 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}); 9993 errdefer msg.destroy(sema.gpa); 9994 switch (dest_ty.zigTypeTag(zcu)) { 9995 .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromPtr to cast to '{f}'", .{dest_ty.fmt(pt)}), 9996 .pointer => try sema.errNote(operand_src, msg, "use @ptrCast to cast to '{f}'", .{dest_ty.fmt(pt)}), 9997 else => {}, 9998 } 9999 10000 break :msg msg; 10001 }; 10002 return sema.failWithOwnedErrorMsg(block, msg); 10003 }, 10004 .@"struct", .@"union" => if (operand_ty.containerLayout(zcu) == .auto) { 10005 const container = switch (operand_ty.zigTypeTag(zcu)) { 10006 .@"struct" => "struct", 10007 .@"union" => "union", 10008 else => unreachable, 10009 }; 10010 return sema.fail(block, operand_src, "cannot @bitCast from '{f}'; {s} does not have a guaranteed in-memory layout", .{ 10011 operand_ty.fmt(pt), container, 10012 }); 10013 }, 10014 10015 .array, 10016 .bool, 10017 .float, 10018 .int, 10019 .vector, 10020 => {}, 10021 } 10022 return sema.bitCast(block, dest_ty, operand, block.nodeOffset(inst_data.src_node), operand_src); 10023 } 10024 10025 fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10026 const tracy = trace(@src()); 10027 defer tracy.end(); 10028 10029 const pt = sema.pt; 10030 const zcu = pt.zcu; 10031 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10032 const src = block.nodeOffset(inst_data.src_node); 10033 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 10034 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10035 10036 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast"); 10037 const dest_scalar_ty = dest_ty.scalarType(zcu); 10038 10039 const operand = try sema.resolveInst(extra.rhs); 10040 const operand_ty = sema.typeOf(operand); 10041 const operand_scalar_ty = operand_ty.scalarType(zcu); 10042 10043 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 10044 const is_vector = dest_ty.zigTypeTag(zcu) == .vector; 10045 10046 const target = zcu.getTarget(); 10047 const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(zcu)) { 10048 .comptime_float => true, 10049 .float => false, 10050 else => return sema.fail( 10051 block, 10052 src, 10053 "expected float or vector type, found '{f}'", 10054 .{dest_ty.fmt(pt)}, 10055 ), 10056 }; 10057 10058 switch (operand_scalar_ty.zigTypeTag(zcu)) { 10059 .comptime_float, .float, .comptime_int => {}, 10060 else => return sema.fail( 10061 block, 10062 operand_src, 10063 "expected float or vector type, found '{f}'", 10064 .{operand_ty.fmt(pt)}, 10065 ), 10066 } 10067 10068 if (try sema.resolveValue(operand)) |operand_val| { 10069 if (!is_vector) { 10070 return Air.internedToRef((try operand_val.floatCast(dest_ty, pt)).toIntern()); 10071 } 10072 const vec_len = operand_ty.vectorLen(zcu); 10073 const new_elems = try sema.arena.alloc(InternPool.Index, vec_len); 10074 for (new_elems, 0..) |*new_elem, i| { 10075 const old_elem = try operand_val.elemValue(pt, i); 10076 new_elem.* = (try old_elem.floatCast(dest_scalar_ty, pt)).toIntern(); 10077 } 10078 return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); 10079 } 10080 if (dest_is_comptime_float) { 10081 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{}); 10082 } 10083 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src); 10084 10085 const src_bits = operand_scalar_ty.floatBits(target); 10086 const dst_bits = dest_scalar_ty.floatBits(target); 10087 if (dst_bits >= src_bits) { 10088 return sema.coerce(block, dest_ty, operand, operand_src); 10089 } 10090 return block.addTyOp(.fptrunc, dest_ty, operand); 10091 } 10092 10093 fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10094 const tracy = trace(@src()); 10095 defer tracy.end(); 10096 10097 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10098 const src = block.nodeOffset(inst_data.src_node); 10099 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10100 const array = try sema.resolveInst(extra.lhs); 10101 const elem_index = try sema.resolveInst(extra.rhs); 10102 return sema.elemVal(block, src, array, elem_index, src, false); 10103 } 10104 10105 fn zirElemPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10106 const tracy = trace(@src()); 10107 defer tracy.end(); 10108 10109 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10110 const src = block.nodeOffset(inst_data.src_node); 10111 const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node }); 10112 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10113 const array_ptr = try sema.resolveInst(extra.lhs); 10114 const uncoerced_elem_index = try sema.resolveInst(extra.rhs); 10115 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { 10116 const array_ptr_ty = sema.typeOf(array_ptr); 10117 if (try sema.pointerDeref(block, src, array_ptr_val, array_ptr_ty)) |array_val| { 10118 const array: Air.Inst.Ref = .fromValue(array_val); 10119 return elemVal(sema, block, src, array, uncoerced_elem_index, elem_index_src, true); 10120 } 10121 } 10122 const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src); 10123 const elem_ptr = try elemPtr(sema, block, src, array_ptr, elem_index, elem_index_src, false, true); 10124 return analyzeLoad(sema, block, src, elem_ptr, elem_index_src); 10125 } 10126 10127 fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10128 const tracy = trace(@src()); 10129 defer tracy.end(); 10130 10131 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm; 10132 const array = try sema.resolveInst(inst_data.operand); 10133 const elem_index = try sema.pt.intRef(.usize, inst_data.idx); 10134 return sema.elemVal(block, LazySrcLoc.unneeded, array, elem_index, LazySrcLoc.unneeded, false); 10135 } 10136 10137 fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10138 const tracy = trace(@src()); 10139 defer tracy.end(); 10140 10141 const pt = sema.pt; 10142 const zcu = pt.zcu; 10143 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10144 const src = block.nodeOffset(inst_data.src_node); 10145 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10146 const array_ptr = try sema.resolveInst(extra.lhs); 10147 const elem_index = try sema.resolveInst(extra.rhs); 10148 const indexable_ty = sema.typeOf(array_ptr); 10149 if (indexable_ty.zigTypeTag(zcu) != .pointer) { 10150 const capture_src = block.src(.{ .for_capture_from_input = inst_data.src_node }); 10151 const msg = msg: { 10152 const msg = try sema.errMsg(capture_src, "pointer capture of non pointer type '{f}'", .{ 10153 indexable_ty.fmt(pt), 10154 }); 10155 errdefer msg.destroy(sema.gpa); 10156 if (indexable_ty.isIndexable(zcu)) { 10157 try sema.errNote(src, msg, "consider using '&' here", .{}); 10158 } 10159 break :msg msg; 10160 }; 10161 return sema.failWithOwnedErrorMsg(block, msg); 10162 } 10163 return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false); 10164 } 10165 10166 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10167 const tracy = trace(@src()); 10168 defer tracy.end(); 10169 10170 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10171 const src = block.nodeOffset(inst_data.src_node); 10172 const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node }); 10173 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10174 const array_ptr = try sema.resolveInst(extra.lhs); 10175 const uncoerced_elem_index = try sema.resolveInst(extra.rhs); 10176 const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src); 10177 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true); 10178 } 10179 10180 fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10181 const tracy = trace(@src()); 10182 defer tracy.end(); 10183 10184 const pt = sema.pt; 10185 const zcu = pt.zcu; 10186 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10187 const src = block.nodeOffset(inst_data.src_node); 10188 const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; 10189 const array_ptr = try sema.resolveInst(extra.ptr); 10190 const elem_index = try pt.intRef(.usize, extra.index); 10191 const array_ty = sema.typeOf(array_ptr).childType(zcu); 10192 switch (array_ty.zigTypeTag(zcu)) { 10193 .array, .vector => {}, 10194 else => if (!array_ty.isTuple(zcu)) { 10195 return sema.failWithArrayInitNotSupported(block, src, array_ty); 10196 }, 10197 } 10198 return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true); 10199 } 10200 10201 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10202 const tracy = trace(@src()); 10203 defer tracy.end(); 10204 10205 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10206 const src = block.nodeOffset(inst_data.src_node); 10207 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; 10208 const array_ptr = try sema.resolveInst(extra.lhs); 10209 const start = try sema.resolveInst(extra.start); 10210 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10211 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); 10212 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); 10213 10214 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false); 10215 } 10216 10217 fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10218 const tracy = trace(@src()); 10219 defer tracy.end(); 10220 10221 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10222 const src = block.nodeOffset(inst_data.src_node); 10223 const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data; 10224 const array_ptr = try sema.resolveInst(extra.lhs); 10225 const start = try sema.resolveInst(extra.start); 10226 const end = try sema.resolveInst(extra.end); 10227 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10228 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); 10229 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); 10230 10231 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false); 10232 } 10233 10234 fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10235 const tracy = trace(@src()); 10236 defer tracy.end(); 10237 10238 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10239 const src = block.nodeOffset(inst_data.src_node); 10240 const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); 10241 const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; 10242 const array_ptr = try sema.resolveInst(extra.lhs); 10243 const start = try sema.resolveInst(extra.start); 10244 const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end); 10245 const sentinel = try sema.resolveInst(extra.sentinel); 10246 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10247 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); 10248 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); 10249 10250 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false); 10251 } 10252 10253 fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10254 const tracy = trace(@src()); 10255 defer tracy.end(); 10256 10257 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10258 const src = block.nodeOffset(inst_data.src_node); 10259 const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; 10260 const array_ptr = try sema.resolveInst(extra.lhs); 10261 const start = try sema.resolveInst(extra.start); 10262 const len = try sema.resolveInst(extra.len); 10263 const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel); 10264 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10265 const start_src = block.src(.{ .node_offset_slice_start = extra.start_src_node_offset }); 10266 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); 10267 const sentinel_src: LazySrcLoc = if (sentinel == .none) 10268 LazySrcLoc.unneeded 10269 else 10270 block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); 10271 10272 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); 10273 } 10274 10275 fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10276 const tracy = trace(@src()); 10277 defer tracy.end(); 10278 10279 const pt = sema.pt; 10280 const zcu = pt.zcu; 10281 10282 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 10283 10284 const src = block.nodeOffset(inst_data.src_node); 10285 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10286 const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); 10287 10288 // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will 10289 // have a double pointer if it was already a pointer. 10290 10291 const lhs_ptr_ty = sema.typeOf(try sema.resolveInst(inst_data.operand)); 10292 const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) { 10293 .pointer => lhs_ptr_ty.childType(zcu), 10294 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{lhs_ptr_ty.fmt(pt)}), 10295 }; 10296 10297 const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) { 10298 .array => lhs_ty.childType(zcu), 10299 .pointer => switch (lhs_ty.ptrSize(zcu)) { 10300 .many, .c, .slice => lhs_ty.childType(zcu), 10301 .one => s: { 10302 const lhs_elem_ty = lhs_ty.childType(zcu); 10303 break :s switch (lhs_elem_ty.zigTypeTag(zcu)) { 10304 .array => lhs_elem_ty.childType(zcu), // array element type 10305 else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}), 10306 }; 10307 }, 10308 }, 10309 else => return sema.fail(block, src, "slice of non-array type '{f}'", .{lhs_ty.fmt(pt)}), 10310 }; 10311 10312 return Air.internedToRef(sentinel_ty.toIntern()); 10313 } 10314 10315 /// Holds common data used when analyzing or resolving switch prong bodies, 10316 /// including setting up captures. 10317 const SwitchProngAnalysis = struct { 10318 sema: *Sema, 10319 /// The block containing the `switch_block` itself. 10320 parent_block: *Block, 10321 operand: Operand, 10322 /// If this switch is on an error set, this is the type to assign to the 10323 /// `else` prong. If `null`, the prong should be unreachable. 10324 else_error_ty: ?Type, 10325 /// The index of the `switch_block` instruction itself. 10326 switch_block_inst: Zir.Inst.Index, 10327 /// The dummy index into which inline tag captures should be placed. May be 10328 /// undefined if no prong has a tag capture. 10329 tag_capture_inst: Zir.Inst.Index, 10330 10331 const Operand = union(enum) { 10332 /// This switch will be dispatched only once, with the given operand. 10333 simple: struct { 10334 /// The raw switch operand value. Always defined. 10335 by_val: Air.Inst.Ref, 10336 /// The switch operand *pointer*. Defined only if there is a prong 10337 /// with a by-ref capture. 10338 by_ref: Air.Inst.Ref, 10339 /// The switch condition value. For unions, `operand` is the union 10340 /// and `cond` is its enum tag value. 10341 cond: Air.Inst.Ref, 10342 }, 10343 /// This switch may be dispatched multiple times with `continue` syntax. 10344 /// As such, the operand is stored in an alloc if needed. 10345 loop: struct { 10346 /// The `alloc` containing the `switch` operand for the active dispatch. 10347 /// Each prong must load from this `alloc` to get captures. 10348 /// If there are no captures, this may be undefined. 10349 operand_alloc: Air.Inst.Ref, 10350 /// Whether `operand_alloc` contains a by-val operand or a by-ref 10351 /// operand. 10352 operand_is_ref: bool, 10353 /// The switch condition value for the *initial* dispatch. For 10354 /// unions, this is the enum tag value. 10355 init_cond: Air.Inst.Ref, 10356 }, 10357 }; 10358 10359 /// Resolve a switch prong which is determined at comptime to have no peers. 10360 /// Uses `resolveBlockBody`. Sets up captures as needed. 10361 fn resolveProngComptime( 10362 spa: SwitchProngAnalysis, 10363 child_block: *Block, 10364 prong_type: enum { normal, special }, 10365 prong_body: []const Zir.Inst.Index, 10366 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10367 /// Must use the `switch_capture` field in `offset`. 10368 capture_src: LazySrcLoc, 10369 /// The set of all values which can reach this prong. May be undefined 10370 /// if the prong is special or contains ranges. 10371 case_vals: []const Air.Inst.Ref, 10372 /// The inline capture of this prong. If this is not an inline prong, 10373 /// this is `.none`. 10374 inline_case_capture: Air.Inst.Ref, 10375 /// Whether this prong has an inline tag capture. If `true`, then 10376 /// `inline_case_capture` cannot be `.none`. 10377 has_tag_capture: bool, 10378 merges: *Block.Merges, 10379 ) CompileError!Air.Inst.Ref { 10380 const sema = spa.sema; 10381 const src = spa.parent_block.nodeOffset( 10382 sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src_node, 10383 ); 10384 10385 // We can propagate `.cold` hints from this branch since it's comptime-known 10386 // to be taken from the parent branch. 10387 const parent_hint = sema.branch_hint; 10388 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; 10389 10390 if (has_tag_capture) { 10391 const tag_ref = try spa.analyzeTagCapture(child_block, capture_src, inline_case_capture); 10392 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); 10393 } 10394 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); 10395 10396 switch (capture) { 10397 .none => { 10398 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); 10399 }, 10400 10401 .by_val, .by_ref => { 10402 const capture_ref = try spa.analyzeCapture( 10403 child_block, 10404 capture == .by_ref, 10405 prong_type == .special, 10406 capture_src, 10407 case_vals, 10408 inline_case_capture, 10409 ); 10410 10411 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) { 10412 // This prong should be unreachable! 10413 return .unreachable_value; 10414 } 10415 10416 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); 10417 defer assert(sema.inst_map.remove(spa.switch_block_inst)); 10418 10419 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); 10420 }, 10421 } 10422 } 10423 10424 /// Analyze a switch prong which may have peers at runtime. 10425 /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed. 10426 /// Returns the `BranchHint` for the prong. 10427 fn analyzeProngRuntime( 10428 spa: SwitchProngAnalysis, 10429 case_block: *Block, 10430 prong_type: enum { normal, special }, 10431 prong_body: []const Zir.Inst.Index, 10432 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10433 /// Must use the `switch_capture` field in `offset`. 10434 capture_src: LazySrcLoc, 10435 /// The set of all values which can reach this prong. May be undefined 10436 /// if the prong is special or contains ranges. 10437 case_vals: []const Air.Inst.Ref, 10438 /// The inline capture of this prong. If this is not an inline prong, 10439 /// this is `.none`. 10440 inline_case_capture: Air.Inst.Ref, 10441 /// Whether this prong has an inline tag capture. If `true`, then 10442 /// `inline_case_capture` cannot be `.none`. 10443 has_tag_capture: bool, 10444 ) CompileError!std.builtin.BranchHint { 10445 const sema = spa.sema; 10446 10447 if (has_tag_capture) { 10448 const tag_ref = try spa.analyzeTagCapture(case_block, capture_src, inline_case_capture); 10449 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); 10450 } 10451 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); 10452 10453 switch (capture) { 10454 .none => { 10455 return sema.analyzeBodyRuntimeBreak(case_block, prong_body); 10456 }, 10457 10458 .by_val, .by_ref => { 10459 const capture_ref = try spa.analyzeCapture( 10460 case_block, 10461 capture == .by_ref, 10462 prong_type == .special, 10463 capture_src, 10464 case_vals, 10465 inline_case_capture, 10466 ); 10467 10468 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) { 10469 // No need to analyze any further, the prong is unreachable 10470 return .none; 10471 } 10472 10473 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); 10474 defer assert(sema.inst_map.remove(spa.switch_block_inst)); 10475 10476 return sema.analyzeBodyRuntimeBreak(case_block, prong_body); 10477 }, 10478 } 10479 } 10480 10481 fn analyzeTagCapture( 10482 spa: SwitchProngAnalysis, 10483 block: *Block, 10484 capture_src: LazySrcLoc, 10485 inline_case_capture: Air.Inst.Ref, 10486 ) CompileError!Air.Inst.Ref { 10487 const sema = spa.sema; 10488 const pt = sema.pt; 10489 const zcu = pt.zcu; 10490 const operand_ty = switch (spa.operand) { 10491 .simple => |s| sema.typeOf(s.by_val), 10492 .loop => |l| ty: { 10493 const alloc_ty = sema.typeOf(l.operand_alloc); 10494 const alloc_child = alloc_ty.childType(zcu); 10495 if (l.operand_is_ref) break :ty alloc_child.childType(zcu); 10496 break :ty alloc_child; 10497 }, 10498 }; 10499 if (operand_ty.zigTypeTag(zcu) != .@"union") { 10500 const tag_capture_src: LazySrcLoc = .{ 10501 .base_node_inst = capture_src.base_node_inst, 10502 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, 10503 }; 10504 return sema.fail(block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ 10505 operand_ty.fmt(pt), 10506 }); 10507 } 10508 assert(inline_case_capture != .none); 10509 return inline_case_capture; 10510 } 10511 10512 fn analyzeCapture( 10513 spa: SwitchProngAnalysis, 10514 block: *Block, 10515 capture_byref: bool, 10516 is_special_prong: bool, 10517 capture_src: LazySrcLoc, 10518 case_vals: []const Air.Inst.Ref, 10519 inline_case_capture: Air.Inst.Ref, 10520 ) CompileError!Air.Inst.Ref { 10521 const sema = spa.sema; 10522 const pt = sema.pt; 10523 const zcu = pt.zcu; 10524 const ip = &zcu.intern_pool; 10525 10526 const zir_datas = sema.code.instructions.items(.data); 10527 const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node; 10528 10529 const operand_src = block.src(.{ .node_offset_switch_operand = switch_node_offset }); 10530 10531 const operand_val, const operand_ptr = switch (spa.operand) { 10532 .simple => |s| .{ s.by_val, s.by_ref }, 10533 .loop => |l| op: { 10534 const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src); 10535 if (l.operand_is_ref) { 10536 const by_val = try sema.analyzeLoad(block, operand_src, loaded, operand_src); 10537 break :op .{ by_val, loaded }; 10538 } else { 10539 break :op .{ loaded, undefined }; 10540 } 10541 }, 10542 }; 10543 10544 const operand_ty = sema.typeOf(operand_val); 10545 const operand_ptr_ty = if (capture_byref) sema.typeOf(operand_ptr) else undefined; 10546 10547 if (inline_case_capture != .none) { 10548 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inline_case_capture, undefined) catch unreachable; 10549 if (operand_ty.zigTypeTag(zcu) == .@"union") { 10550 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); 10551 const union_obj = zcu.typeToUnion(operand_ty).?; 10552 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 10553 if (capture_byref) { 10554 const ptr_field_ty = try pt.ptrTypeSema(.{ 10555 .child = field_ty.toIntern(), 10556 .flags = .{ 10557 .is_const = !operand_ptr_ty.ptrIsMutable(zcu), 10558 .is_volatile = operand_ptr_ty.isVolatilePtr(zcu), 10559 .address_space = operand_ptr_ty.ptrAddressSpace(zcu), 10560 }, 10561 }); 10562 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |union_ptr| { 10563 return Air.internedToRef((try union_ptr.ptrField(field_index, pt)).toIntern()); 10564 } 10565 return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty); 10566 } else { 10567 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |union_val| { 10568 const tag_and_val = ip.indexToKey(union_val.toIntern()).un; 10569 return Air.internedToRef(tag_and_val.val); 10570 } 10571 return block.addStructFieldVal(operand_val, field_index, field_ty); 10572 } 10573 } else if (capture_byref) { 10574 return sema.uavRef(item_val.toIntern()); 10575 } else { 10576 return inline_case_capture; 10577 } 10578 } 10579 10580 if (is_special_prong) { 10581 if (capture_byref) { 10582 return operand_ptr; 10583 } 10584 10585 switch (operand_ty.zigTypeTag(zcu)) { 10586 .error_set => if (spa.else_error_ty) |ty| { 10587 return sema.bitCast(block, ty, operand_val, operand_src, null); 10588 } else { 10589 try sema.analyzeUnreachable(block, operand_src, false); 10590 return .unreachable_value; 10591 }, 10592 else => return operand_val, 10593 } 10594 } 10595 10596 switch (operand_ty.zigTypeTag(zcu)) { 10597 .@"union" => { 10598 const union_obj = zcu.typeToUnion(operand_ty).?; 10599 const first_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable; 10600 10601 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; 10602 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); 10603 10604 const field_indices = try sema.arena.alloc(u32, case_vals.len); 10605 for (case_vals, field_indices) |item, *field_idx| { 10606 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 10607 field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; 10608 } 10609 10610 // Fast path: if all the operands are the same type already, we don't need to hit 10611 // PTR! This will also allow us to emit simpler code. 10612 const same_types = for (field_indices[1..]) |field_idx| { 10613 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10614 if (!field_ty.eql(first_field_ty, zcu)) break false; 10615 } else true; 10616 10617 const capture_ty = if (same_types) first_field_ty else capture_ty: { 10618 // We need values to run PTR on, so make a bunch of undef constants. 10619 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); 10620 for (dummy_captures, field_indices) |*dummy, field_idx| { 10621 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10622 dummy.* = try pt.undefRef(field_ty); 10623 } 10624 10625 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); 10626 for (case_srcs, 0..) |*case_src, i| { 10627 case_src.* = .{ 10628 .base_node_inst = capture_src.base_node_inst, 10629 .offset = .{ .switch_case_item = .{ 10630 .switch_node_offset = switch_node_offset, 10631 .case_idx = capture_src.offset.switch_capture.case_idx, 10632 .item_idx = .{ .kind = .single, .index = @intCast(i) }, 10633 } }, 10634 }; 10635 } 10636 10637 break :capture_ty sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { 10638 error.AnalysisFail => { 10639 const msg = sema.err orelse return error.AnalysisFail; 10640 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); 10641 return error.AnalysisFail; 10642 }, 10643 else => |e| return e, 10644 }; 10645 }; 10646 10647 // By-reference captures have some further restrictions which make them easier to emit 10648 if (capture_byref) { 10649 const operand_ptr_info = operand_ptr_ty.ptrInfo(zcu); 10650 const capture_ptr_ty = resolve: { 10651 // By-ref captures of hetereogeneous types are only allowed if all field 10652 // pointer types are peer resolvable to each other. 10653 // We need values to run PTR on, so make a bunch of undef constants. 10654 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); 10655 for (field_indices, dummy_captures) |field_idx, *dummy| { 10656 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10657 const field_ptr_ty = try pt.ptrTypeSema(.{ 10658 .child = field_ty.toIntern(), 10659 .flags = .{ 10660 .is_const = operand_ptr_info.flags.is_const, 10661 .is_volatile = operand_ptr_info.flags.is_volatile, 10662 .address_space = operand_ptr_info.flags.address_space, 10663 .alignment = union_obj.fieldAlign(ip, field_idx), 10664 }, 10665 }); 10666 dummy.* = try pt.undefRef(field_ptr_ty); 10667 } 10668 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); 10669 for (case_srcs, 0..) |*case_src, i| { 10670 case_src.* = .{ 10671 .base_node_inst = capture_src.base_node_inst, 10672 .offset = .{ .switch_case_item = .{ 10673 .switch_node_offset = switch_node_offset, 10674 .case_idx = capture_src.offset.switch_capture.case_idx, 10675 .item_idx = .{ .kind = .single, .index = @intCast(i) }, 10676 } }, 10677 }; 10678 } 10679 10680 break :resolve sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { 10681 error.AnalysisFail => { 10682 const msg = sema.err orelse return error.AnalysisFail; 10683 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); 10684 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); 10685 return error.AnalysisFail; 10686 }, 10687 else => |e| return e, 10688 }; 10689 }; 10690 10691 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| { 10692 if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); 10693 const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); 10694 return Air.internedToRef((try pt.getCoerced(field_ptr_val, capture_ptr_ty)).toIntern()); 10695 } 10696 10697 try sema.requireRuntimeBlock(block, operand_src, null); 10698 return block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); 10699 } 10700 10701 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |operand_val_val| { 10702 if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); 10703 const union_val = ip.indexToKey(operand_val_val.toIntern()).un; 10704 if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); 10705 const uncoerced = Air.internedToRef(union_val.val); 10706 return sema.coerce(block, capture_ty, uncoerced, operand_src); 10707 } 10708 10709 try sema.requireRuntimeBlock(block, operand_src, null); 10710 10711 if (same_types) { 10712 return block.addStructFieldVal(operand_val, first_field_index, capture_ty); 10713 } 10714 10715 // We may have to emit a switch block which coerces the operand to the capture type. 10716 // If we can, try to avoid that using in-memory coercions. 10717 const first_non_imc = in_mem: { 10718 for (field_indices, 0..) |field_idx, i| { 10719 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10720 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) { 10721 break :in_mem i; 10722 } 10723 } 10724 // All fields are in-memory coercible to the resolved type! 10725 // Just take the first field and bitcast the result. 10726 const uncoerced = try block.addStructFieldVal(operand_val, first_field_index, first_field_ty); 10727 return block.addBitCast(capture_ty, uncoerced); 10728 }; 10729 10730 // By-val capture with heterogeneous types which are not all in-memory coercible to 10731 // the resolved capture type. We finally have to fall back to the ugly method. 10732 10733 // However, let's first track which operands are in-memory coercible. There may well 10734 // be several, and we can squash all of these cases into the same switch prong using 10735 // a simple bitcast. We'll make this the 'else' prong. 10736 10737 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len); 10738 in_mem_coercible.unset(first_non_imc); 10739 { 10740 const next = first_non_imc + 1; 10741 for (field_indices[next..], next..) |field_idx, i| { 10742 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10743 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) { 10744 in_mem_coercible.unset(i); 10745 } 10746 } 10747 } 10748 10749 const capture_block_inst = try block.addInstAsIndex(.{ 10750 .tag = .block, 10751 .data = .{ 10752 .ty_pl = .{ 10753 .ty = Air.internedToRef(capture_ty.toIntern()), 10754 .payload = undefined, // updated below 10755 }, 10756 }, 10757 }); 10758 10759 const prong_count = field_indices.len - in_mem_coercible.count(); 10760 10761 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints 10762 var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra); 10763 defer cases_extra.deinit(); 10764 10765 { 10766 // All branch hints are `.none`, so just add zero elems. 10767 comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0); 10768 const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; 10769 try cases_extra.appendNTimes(0, need_elems); 10770 } 10771 10772 { 10773 // Non-bitcast cases 10774 var it = in_mem_coercible.iterator(.{ .kind = .unset }); 10775 while (it.next()) |idx| { 10776 var coerce_block = block.makeSubBlock(); 10777 defer coerce_block.instructions.deinit(sema.gpa); 10778 10779 const case_src: LazySrcLoc = .{ 10780 .base_node_inst = capture_src.base_node_inst, 10781 .offset = .{ .switch_case_item = .{ 10782 .switch_node_offset = switch_node_offset, 10783 .case_idx = capture_src.offset.switch_capture.case_idx, 10784 .item_idx = .{ .kind = .single, .index = @intCast(idx) }, 10785 } }, 10786 }; 10787 10788 const field_idx = field_indices[idx]; 10789 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10790 const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); 10791 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); 10792 _ = try coerce_block.addBr(capture_block_inst, coerced); 10793 10794 try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 10795 1 + // `item`, no ranges 10796 coerce_block.instructions.items.len); 10797 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 10798 .items_len = 1, 10799 .ranges_len = 0, 10800 .body_len = @intCast(coerce_block.instructions.items.len), 10801 })); 10802 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item 10803 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body 10804 } 10805 } 10806 const else_body_len = len: { 10807 // 'else' prong uses a bitcast 10808 var coerce_block = block.makeSubBlock(); 10809 defer coerce_block.instructions.deinit(sema.gpa); 10810 10811 const first_imc_item_idx = in_mem_coercible.findFirstSet().?; 10812 const first_imc_field_idx = field_indices[first_imc_item_idx]; 10813 const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); 10814 const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); 10815 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); 10816 _ = try coerce_block.addBr(capture_block_inst, coerced); 10817 10818 try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); 10819 break :len coerce_block.instructions.items.len; 10820 }; 10821 10822 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + 10823 cases_extra.items.len + 10824 @typeInfo(Air.Block).@"struct".fields.len + 10825 1); 10826 10827 const switch_br_inst: u32 = @intCast(sema.air_instructions.len); 10828 try sema.air_instructions.append(sema.gpa, .{ 10829 .tag = .switch_br, 10830 .data = .{ 10831 .pl_op = .{ 10832 .operand = undefined, // set by switch below 10833 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ 10834 .cases_len = @intCast(prong_count), 10835 .else_body_len = @intCast(else_body_len), 10836 }), 10837 }, 10838 }, 10839 }); 10840 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); 10841 10842 // Set up block body 10843 switch (spa.operand) { 10844 .simple => |s| { 10845 const air_datas = sema.air_instructions.items(.data); 10846 air_datas[switch_br_inst].pl_op.operand = s.cond; 10847 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ 10848 .body_len = 1, 10849 }); 10850 sema.air_extra.appendAssumeCapacity(switch_br_inst); 10851 }, 10852 .loop => { 10853 // The block must first extract the tag from the loaded union. 10854 const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 10855 try sema.air_instructions.append(sema.gpa, .{ 10856 .tag = .get_union_tag, 10857 .data = .{ .ty_op = .{ 10858 .ty = Air.internedToRef(union_obj.enum_tag_ty), 10859 .operand = operand_val, 10860 } }, 10861 }); 10862 const air_datas = sema.air_instructions.items(.data); 10863 air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); 10864 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ 10865 .body_len = 2, 10866 }); 10867 sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst)); 10868 sema.air_extra.appendAssumeCapacity(switch_br_inst); 10869 }, 10870 } 10871 10872 return capture_block_inst.toRef(); 10873 }, 10874 .error_set => { 10875 if (capture_byref) { 10876 return sema.fail( 10877 block, 10878 capture_src, 10879 "error set cannot be captured by reference", 10880 .{}, 10881 ); 10882 } 10883 10884 if (case_vals.len == 1) { 10885 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable; 10886 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); 10887 return sema.bitCast(block, item_ty, operand_val, operand_src, null); 10888 } 10889 10890 var names: InferredErrorSet.NameMap = .{}; 10891 try names.ensureUnusedCapacity(sema.arena, case_vals.len); 10892 for (case_vals) |err| { 10893 const err_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, err, undefined) catch unreachable; 10894 names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); 10895 } 10896 const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); 10897 return sema.bitCast(block, error_ty, operand_val, operand_src, null); 10898 }, 10899 else => { 10900 // In this case the capture value is just the passed-through value 10901 // of the switch condition. 10902 if (capture_byref) { 10903 return operand_ptr; 10904 } else { 10905 return operand_val; 10906 } 10907 }, 10908 } 10909 } 10910 }; 10911 10912 fn switchCond( 10913 sema: *Sema, 10914 block: *Block, 10915 src: LazySrcLoc, 10916 operand: Air.Inst.Ref, 10917 ) CompileError!Air.Inst.Ref { 10918 const pt = sema.pt; 10919 const zcu = pt.zcu; 10920 const operand_ty = sema.typeOf(operand); 10921 switch (operand_ty.zigTypeTag(zcu)) { 10922 .type, 10923 .void, 10924 .bool, 10925 .int, 10926 .float, 10927 .comptime_float, 10928 .comptime_int, 10929 .enum_literal, 10930 .pointer, 10931 .@"fn", 10932 .error_set, 10933 .@"enum", 10934 => { 10935 if (operand_ty.isSlice(zcu)) { 10936 return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)}); 10937 } 10938 if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| { 10939 return Air.internedToRef(opv.toIntern()); 10940 } 10941 return operand; 10942 }, 10943 10944 .@"union" => { 10945 try operand_ty.resolveFields(pt); 10946 const enum_ty = operand_ty.unionTagType(zcu) orelse { 10947 const msg = msg: { 10948 const msg = try sema.errMsg(src, "switch on union with no attached enum", .{}); 10949 errdefer msg.destroy(sema.gpa); 10950 if (operand_ty.srcLocOrNull(zcu)) |union_src| { 10951 try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{}); 10952 } 10953 break :msg msg; 10954 }; 10955 return sema.failWithOwnedErrorMsg(block, msg); 10956 }; 10957 return sema.unionToTag(block, enum_ty, operand, src); 10958 }, 10959 10960 .error_union, 10961 .noreturn, 10962 .array, 10963 .@"struct", 10964 .undefined, 10965 .null, 10966 .optional, 10967 .@"opaque", 10968 .vector, 10969 .frame, 10970 .@"anyframe", 10971 => return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)}), 10972 } 10973 } 10974 10975 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, LazySrcLoc); 10976 10977 fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10978 const tracy = trace(@src()); 10979 defer tracy.end(); 10980 10981 const pt = sema.pt; 10982 const zcu = pt.zcu; 10983 const gpa = sema.gpa; 10984 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10985 const switch_src = block.nodeOffset(inst_data.src_node); 10986 const switch_src_node_offset = inst_data.src_node; 10987 const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset }); 10988 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = switch_src_node_offset }); 10989 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index); 10990 const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset }); 10991 const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset }); 10992 10993 const raw_operand_val = try sema.resolveInst(extra.data.operand); 10994 10995 // AstGen guarantees that the instruction immediately preceding 10996 // switch_block_err_union is a dbg_stmt 10997 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); 10998 10999 var header_extra_index: usize = extra.end; 11000 11001 const scalar_cases_len = extra.data.bits.scalar_cases_len; 11002 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: { 11003 const multi_cases_len = sema.code.extra[header_extra_index]; 11004 header_extra_index += 1; 11005 break :blk multi_cases_len; 11006 } else 0; 11007 11008 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: { 11009 const err_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]); 11010 header_extra_index += 1; 11011 // SwitchProngAnalysis wants inst_map to have space for the tag capture. 11012 // Note that the normal capture is referred to via the switch block 11013 // index, which there is already necessarily space for. 11014 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{err_capture_inst}); 11015 break :blk err_capture_inst; 11016 } else undefined; 11017 11018 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); 11019 defer case_vals.deinit(gpa); 11020 11021 const NonError = struct { 11022 body: []const Zir.Inst.Index, 11023 end: usize, 11024 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 11025 }; 11026 11027 const non_error_case: NonError = non_error: { 11028 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]); 11029 const extra_body_start = header_extra_index + 1; 11030 break :non_error .{ 11031 .body = sema.code.bodySlice(extra_body_start, info.body_len), 11032 .end = extra_body_start + info.body_len, 11033 .capture = info.capture, 11034 }; 11035 }; 11036 11037 const Else = struct { 11038 body: []const Zir.Inst.Index, 11039 end: usize, 11040 is_inline: bool, 11041 has_capture: bool, 11042 }; 11043 11044 const else_case: Else = if (!extra.data.bits.has_else) .{ 11045 .body = &.{}, 11046 .end = non_error_case.end, 11047 .is_inline = false, 11048 .has_capture = false, 11049 } else special: { 11050 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[non_error_case.end]); 11051 const extra_body_start = non_error_case.end + 1; 11052 assert(info.capture != .by_ref); 11053 assert(!info.has_tag_capture); 11054 break :special .{ 11055 .body = sema.code.bodySlice(extra_body_start, info.body_len), 11056 .end = extra_body_start + info.body_len, 11057 .is_inline = info.is_inline, 11058 .has_capture = info.capture != .none, 11059 }; 11060 }; 11061 11062 var seen_errors = SwitchErrorSet.init(gpa); 11063 defer seen_errors.deinit(); 11064 11065 const operand_ty = sema.typeOf(raw_operand_val); 11066 const operand_err_set = if (extra.data.bits.payload_is_ref) 11067 operand_ty.childType(zcu) 11068 else 11069 operand_ty; 11070 11071 if (operand_err_set.zigTypeTag(zcu) != .error_union) { 11072 return sema.fail(block, switch_src, "expected error union type, found '{f}'", .{ 11073 operand_ty.fmt(pt), 11074 }); 11075 } 11076 11077 const operand_err_set_ty = operand_err_set.errorUnionSet(zcu); 11078 11079 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 11080 try sema.air_instructions.append(gpa, .{ 11081 .tag = .block, 11082 .data = undefined, 11083 }); 11084 var label: Block.Label = .{ 11085 .zir_block = inst, 11086 .merges = .{ 11087 .src_locs = .{}, 11088 .results = .{}, 11089 .br_list = .{}, 11090 .block_inst = block_inst, 11091 }, 11092 }; 11093 11094 var child_block: Block = .{ 11095 .parent = block, 11096 .sema = sema, 11097 .namespace = block.namespace, 11098 .instructions = .{}, 11099 .label = &label, 11100 .inlining = block.inlining, 11101 .comptime_reason = block.comptime_reason, 11102 .is_typeof = block.is_typeof, 11103 .c_import_buf = block.c_import_buf, 11104 .runtime_cond = block.runtime_cond, 11105 .runtime_loop = block.runtime_loop, 11106 .runtime_index = block.runtime_index, 11107 .error_return_trace_index = block.error_return_trace_index, 11108 .want_safety = block.want_safety, 11109 .src_base_inst = block.src_base_inst, 11110 .type_name_ctx = block.type_name_ctx, 11111 }; 11112 const merges = &child_block.label.?.merges; 11113 defer child_block.instructions.deinit(gpa); 11114 defer merges.deinit(gpa); 11115 11116 const resolved_err_set = try sema.resolveInferredErrorSetTy(block, main_src, operand_err_set_ty.toIntern()); 11117 if (Type.fromInterned(resolved_err_set).errorSetIsEmpty(zcu)) { 11118 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges); 11119 } 11120 11121 const else_error_ty: ?Type = try validateErrSetSwitch( 11122 sema, 11123 block, 11124 &seen_errors, 11125 &case_vals, 11126 operand_err_set_ty, 11127 inst_data, 11128 scalar_cases_len, 11129 multi_cases_len, 11130 .{ .body = else_case.body, .end = else_case.end, .src = else_prong_src }, 11131 extra.data.bits.has_else, 11132 ); 11133 11134 var spa: SwitchProngAnalysis = .{ 11135 .sema = sema, 11136 .parent_block = block, 11137 .operand = .{ 11138 .simple = .{ 11139 .by_val = undefined, // must be set to the unwrapped error code before use 11140 .by_ref = undefined, 11141 .cond = raw_operand_val, 11142 }, 11143 }, 11144 .else_error_ty = else_error_ty, 11145 .switch_block_inst = inst, 11146 .tag_capture_inst = undefined, 11147 }; 11148 11149 if (try sema.resolveDefinedValue(&child_block, main_src, raw_operand_val)) |ov| { 11150 const operand_val = if (extra.data.bits.payload_is_ref) 11151 (try sema.pointerDeref(&child_block, main_src, ov, operand_ty)).? 11152 else 11153 ov; 11154 11155 if (operand_val.errorUnionIsPayload(zcu)) { 11156 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges); 11157 } else { 11158 const err_val = Value.fromInterned(try pt.intern(.{ 11159 .err = .{ 11160 .ty = operand_err_set_ty.toIntern(), 11161 .name = operand_val.getErrorName(zcu).unwrap().?, 11162 }, 11163 })); 11164 spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref) 11165 try sema.analyzeErrUnionCodePtr(block, switch_operand_src, raw_operand_val) 11166 else 11167 try sema.analyzeErrUnionCode(block, switch_operand_src, raw_operand_val); 11168 11169 if (extra.data.bits.any_uses_err_capture) { 11170 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val); 11171 } 11172 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst)); 11173 11174 return resolveSwitchComptime( 11175 sema, 11176 spa, 11177 &child_block, 11178 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val), 11179 err_val, 11180 operand_err_set_ty, 11181 switch_src_node_offset, 11182 null, 11183 .{ 11184 .body = else_case.body, 11185 .end = else_case.end, 11186 .capture = if (else_case.has_capture) .by_val else .none, 11187 .is_inline = else_case.is_inline, 11188 .has_tag_capture = false, 11189 }, 11190 false, 11191 case_vals, 11192 scalar_cases_len, 11193 multi_cases_len, 11194 true, 11195 false, 11196 ); 11197 } 11198 } 11199 11200 if (scalar_cases_len + multi_cases_len == 0) { 11201 if (else_error_ty) |ty| if (ty.errorSetIsEmpty(zcu)) { 11202 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges); 11203 }; 11204 } 11205 11206 if (child_block.isComptime()) { 11207 _ = try sema.resolveConstDefinedValue(&child_block, main_operand_src, raw_operand_val, null); 11208 unreachable; 11209 } 11210 11211 const cond = if (extra.data.bits.payload_is_ref) blk: { 11212 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val).elemType2(zcu)); 11213 const loaded = try sema.analyzeLoad(block, main_src, raw_operand_val, main_src); 11214 break :blk try sema.analyzeIsNonErr(block, main_src, loaded); 11215 } else blk: { 11216 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val)); 11217 break :blk try sema.analyzeIsNonErr(block, main_src, raw_operand_val); 11218 }; 11219 11220 var sub_block = child_block.makeSubBlock(); 11221 sub_block.runtime_loop = null; 11222 sub_block.runtime_cond = main_operand_src; 11223 sub_block.runtime_index.increment(); 11224 sub_block.need_debug_scope = null; // this body is emitted regardless 11225 defer sub_block.instructions.deinit(gpa); 11226 11227 const non_error_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body); 11228 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); 11229 defer gpa.free(true_instructions); 11230 11231 spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref) 11232 try sema.analyzeErrUnionCodePtr(&sub_block, switch_operand_src, raw_operand_val) 11233 else 11234 try sema.analyzeErrUnionCode(&sub_block, switch_operand_src, raw_operand_val); 11235 11236 if (extra.data.bits.any_uses_err_capture) { 11237 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val); 11238 } 11239 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst)); 11240 _ = try sema.analyzeSwitchRuntimeBlock( 11241 spa, 11242 &sub_block, 11243 switch_src, 11244 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val), 11245 operand_err_set_ty, 11246 switch_operand_src, 11247 case_vals, 11248 .{ 11249 .body = else_case.body, 11250 .end = else_case.end, 11251 .capture = if (else_case.has_capture) .by_val else .none, 11252 .is_inline = else_case.is_inline, 11253 .has_tag_capture = false, 11254 }, 11255 scalar_cases_len, 11256 multi_cases_len, 11257 false, 11258 undefined, 11259 true, 11260 switch_src_node_offset, 11261 else_prong_src, 11262 false, 11263 undefined, 11264 seen_errors, 11265 undefined, 11266 undefined, 11267 undefined, 11268 cond_dbg_node_index, 11269 true, 11270 null, 11271 undefined, 11272 &.{}, 11273 &.{}, 11274 ); 11275 11276 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 11277 true_instructions.len + sub_block.instructions.items.len); 11278 11279 _ = try child_block.addInst(.{ 11280 .tag = .cond_br, 11281 .data = .{ 11282 .pl_op = .{ 11283 .operand = cond, 11284 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 11285 .then_body_len = @intCast(true_instructions.len), 11286 .else_body_len = @intCast(sub_block.instructions.items.len), 11287 .branch_hints = .{ 11288 .true = non_error_hint, 11289 .false = .none, 11290 // Code coverage is desired for error handling. 11291 .then_cov = .poi, 11292 .else_cov = .poi, 11293 }, 11294 }), 11295 }, 11296 }, 11297 }); 11298 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); 11299 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 11300 11301 return sema.resolveAnalyzedBlock(block, main_src, &child_block, merges, false); 11302 } 11303 11304 fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { 11305 const tracy = trace(@src()); 11306 defer tracy.end(); 11307 11308 const pt = sema.pt; 11309 const zcu = pt.zcu; 11310 const ip = &zcu.intern_pool; 11311 const gpa = sema.gpa; 11312 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 11313 const src = block.nodeOffset(inst_data.src_node); 11314 const src_node_offset = inst_data.src_node; 11315 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); 11316 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset }); 11317 const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset }); 11318 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); 11319 11320 const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: { 11321 const maybe_ptr = try sema.resolveInst(extra.data.operand); 11322 const val, const ref = if (operand_is_ref) 11323 .{ try sema.analyzeLoad(block, src, maybe_ptr, operand_src), maybe_ptr } 11324 else 11325 .{ maybe_ptr, undefined }; 11326 11327 const init_cond = try sema.switchCond(block, operand_src, val); 11328 11329 const operand_ty = sema.typeOf(val); 11330 11331 if (extra.data.bits.has_continue and !block.isComptime()) { 11332 // Even if the operand is comptime-known, this `switch` is runtime. 11333 if (try operand_ty.comptimeOnlySema(pt)) { 11334 return sema.failWithOwnedErrorMsg(block, msg: { 11335 const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)}); 11336 errdefer msg.destroy(gpa); 11337 try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{}); 11338 break :msg msg; 11339 }); 11340 } 11341 try sema.validateRuntimeValue(block, operand_src, maybe_ptr); 11342 const operand_alloc = if (extra.data.bits.any_non_inline_capture) a: { 11343 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(maybe_ptr)); 11344 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty); 11345 _ = try block.addBinOp(.store, operand_alloc, maybe_ptr); 11346 break :a operand_alloc; 11347 } else undefined; 11348 break :op .{ 11349 .{ .loop = .{ 11350 .operand_alloc = operand_alloc, 11351 .operand_is_ref = operand_is_ref, 11352 .init_cond = init_cond, 11353 } }, 11354 operand_ty, 11355 }; 11356 } 11357 11358 // We always use `simple` in the comptime case, because as far as the dispatching logic 11359 // is concerned, it really is dispatching a single prong. `resolveSwitchComptime` will 11360 // be resposible for recursively resolving different prongs as needed. 11361 break :op .{ 11362 .{ .simple = .{ 11363 .by_val = val, 11364 .by_ref = ref, 11365 .cond = init_cond, 11366 } }, 11367 operand_ty, 11368 }; 11369 }; 11370 11371 const union_originally = raw_operand_ty.zigTypeTag(zcu) == .@"union"; 11372 const err_set = raw_operand_ty.zigTypeTag(zcu) == .error_set; 11373 const cond_ty = switch (raw_operand_ty.zigTypeTag(zcu)) { 11374 .@"union" => raw_operand_ty.unionTagType(zcu).?, // validated by `switchCond` above 11375 else => raw_operand_ty, 11376 }; 11377 11378 // AstGen guarantees that the instruction immediately preceding 11379 // switch_block(_ref) is a dbg_stmt 11380 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); 11381 11382 var header_extra_index: usize = extra.end; 11383 11384 const scalar_cases_len = extra.data.bits.scalar_cases_len; 11385 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: { 11386 const multi_cases_len = sema.code.extra[header_extra_index]; 11387 header_extra_index += 1; 11388 break :blk multi_cases_len; 11389 } else 0; 11390 11391 const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: { 11392 const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]); 11393 header_extra_index += 1; 11394 // SwitchProngAnalysis wants inst_map to have space for the tag capture. 11395 // Note that the normal capture is referred to via the switch block 11396 // index, which there is already necessarily space for. 11397 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst}); 11398 break :blk tag_capture_inst; 11399 } else undefined; 11400 11401 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); 11402 defer case_vals.deinit(gpa); 11403 11404 var single_absorbed_item: Zir.Inst.Ref = .none; 11405 var absorbed_items: []const Zir.Inst.Ref = &.{}; 11406 var absorbed_ranges: []const Zir.Inst.Ref = &.{}; 11407 11408 const special_prongs = extra.data.bits.special_prongs; 11409 const has_else = special_prongs.hasElse(); 11410 const has_under = special_prongs.hasUnder(); 11411 const special_else: SpecialProng = if (has_else) blk: { 11412 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]); 11413 const extra_body_start = header_extra_index + 1; 11414 break :blk .{ 11415 .body = sema.code.bodySlice(extra_body_start, info.body_len), 11416 .end = extra_body_start + info.body_len, 11417 .capture = info.capture, 11418 .is_inline = info.is_inline, 11419 .has_tag_capture = info.has_tag_capture, 11420 }; 11421 } else .{ 11422 .body = &.{}, 11423 .end = header_extra_index, 11424 .capture = .none, 11425 .is_inline = false, 11426 .has_tag_capture = false, 11427 }; 11428 const special_under: SpecialProng = if (has_under) blk: { 11429 var extra_index = special_else.end; 11430 var trailing_items_len: usize = 0; 11431 if (special_prongs.hasOneAdditionalItem()) { 11432 single_absorbed_item = @enumFromInt(sema.code.extra[extra_index]); 11433 extra_index += 1; 11434 absorbed_items = @ptrCast(&single_absorbed_item); 11435 } else if (special_prongs.hasManyAdditionalItems()) { 11436 const items_len = sema.code.extra[extra_index]; 11437 extra_index += 1; 11438 const ranges_len = sema.code.extra[extra_index]; 11439 extra_index += 1; 11440 absorbed_items = sema.code.refSlice(extra_index + 1, items_len); 11441 absorbed_ranges = sema.code.refSlice(extra_index + 1 + items_len, ranges_len * 2); 11442 trailing_items_len = items_len + ranges_len * 2; 11443 } 11444 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11445 extra_index += 1 + trailing_items_len; 11446 break :blk .{ 11447 .body = sema.code.bodySlice(extra_index, info.body_len), 11448 .end = extra_index + info.body_len, 11449 .capture = info.capture, 11450 .is_inline = info.is_inline, 11451 .has_tag_capture = info.has_tag_capture, 11452 }; 11453 } else .{ 11454 .body = &.{}, 11455 .end = special_else.end, 11456 .capture = .none, 11457 .is_inline = false, 11458 .has_tag_capture = false, 11459 }; 11460 const special_end = special_under.end; 11461 11462 // Duplicate checking variables later also used for `inline else`. 11463 var seen_enum_fields: []?LazySrcLoc = &.{}; 11464 var seen_errors = SwitchErrorSet.init(gpa); 11465 var range_set = RangeSet.init(gpa, zcu); 11466 var true_count: u8 = 0; 11467 var false_count: u8 = 0; 11468 11469 defer { 11470 range_set.deinit(); 11471 gpa.free(seen_enum_fields); 11472 seen_errors.deinit(); 11473 } 11474 11475 var empty_enum = false; 11476 11477 var else_error_ty: ?Type = null; 11478 11479 // Validate usage of '_' prongs. 11480 if (has_under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) { 11481 const msg = msg: { 11482 const msg = try sema.errMsg( 11483 src, 11484 "'_' prong only allowed when switching on non-exhaustive enums", 11485 .{}, 11486 ); 11487 errdefer msg.destroy(gpa); 11488 try sema.errNote( 11489 under_prong_src, 11490 msg, 11491 "'_' prong here", 11492 .{}, 11493 ); 11494 try sema.errNote( 11495 src, 11496 msg, 11497 "consider using 'else'", 11498 .{}, 11499 ); 11500 break :msg msg; 11501 }; 11502 return sema.failWithOwnedErrorMsg(block, msg); 11503 } 11504 11505 // Validate for duplicate items, missing else prong, and invalid range. 11506 switch (cond_ty.zigTypeTag(zcu)) { 11507 .@"union" => unreachable, // handled in `switchCond` 11508 .@"enum" => { 11509 seen_enum_fields = try gpa.alloc(?LazySrcLoc, cond_ty.enumFieldCount(zcu)); 11510 empty_enum = seen_enum_fields.len == 0 and !cond_ty.isNonexhaustiveEnum(zcu); 11511 @memset(seen_enum_fields, null); 11512 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. 11513 11514 for (absorbed_items, 0..) |item_ref, item_i| { 11515 _ = try sema.validateSwitchItemEnum( 11516 block, 11517 seen_enum_fields, 11518 &range_set, 11519 item_ref, 11520 cond_ty, 11521 block.src(.{ .switch_case_item = .{ 11522 .switch_node_offset = src_node_offset, 11523 .case_idx = .special_under, 11524 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11525 } }), 11526 ); 11527 } 11528 try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset); 11529 11530 var extra_index: usize = special_end; 11531 { 11532 var scalar_i: u32 = 0; 11533 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11534 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11535 extra_index += 1; 11536 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11537 extra_index += 1 + info.body_len; 11538 11539 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( 11540 block, 11541 seen_enum_fields, 11542 &range_set, 11543 item_ref, 11544 cond_ty, 11545 block.src(.{ .switch_case_item = .{ 11546 .switch_node_offset = src_node_offset, 11547 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 11548 .item_idx = .{ .kind = .single, .index = 0 }, 11549 } }), 11550 )); 11551 } 11552 } 11553 { 11554 var multi_i: u32 = 0; 11555 while (multi_i < multi_cases_len) : (multi_i += 1) { 11556 const items_len = sema.code.extra[extra_index]; 11557 extra_index += 1; 11558 const ranges_len = sema.code.extra[extra_index]; 11559 extra_index += 1; 11560 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11561 extra_index += 1; 11562 const items = sema.code.refSlice(extra_index, items_len); 11563 extra_index += items_len + info.body_len; 11564 11565 try case_vals.ensureUnusedCapacity(gpa, items.len); 11566 for (items, 0..) |item_ref, item_i| { 11567 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( 11568 block, 11569 seen_enum_fields, 11570 &range_set, 11571 item_ref, 11572 cond_ty, 11573 block.src(.{ .switch_case_item = .{ 11574 .switch_node_offset = src_node_offset, 11575 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11576 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11577 } }), 11578 )); 11579 } 11580 11581 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset); 11582 } 11583 } 11584 const all_tags_handled = for (seen_enum_fields) |seen_src| { 11585 if (seen_src == null) break false; 11586 } else true; 11587 11588 if (has_else) { 11589 if (all_tags_handled) { 11590 if (cond_ty.isNonexhaustiveEnum(zcu)) { 11591 if (has_under) return sema.fail( 11592 block, 11593 else_prong_src, 11594 "unreachable else prong; all explicit cases already handled", 11595 .{}, 11596 ); 11597 } else return sema.fail( 11598 block, 11599 else_prong_src, 11600 "unreachable else prong; all cases already handled", 11601 .{}, 11602 ); 11603 } 11604 } else if (!all_tags_handled) { 11605 const msg = msg: { 11606 const msg = try sema.errMsg( 11607 src, 11608 "switch must handle all possibilities", 11609 .{}, 11610 ); 11611 errdefer msg.destroy(sema.gpa); 11612 for (seen_enum_fields, 0..) |seen_src, i| { 11613 if (seen_src != null) continue; 11614 11615 const field_name = cond_ty.enumFieldName(i, zcu); 11616 try sema.addFieldErrNote( 11617 cond_ty, 11618 i, 11619 msg, 11620 "unhandled enumeration value: '{f}'", 11621 .{field_name.fmt(ip)}, 11622 ); 11623 } 11624 try sema.errNote( 11625 cond_ty.srcLoc(zcu), 11626 msg, 11627 "enum '{f}' declared here", 11628 .{cond_ty.fmt(pt)}, 11629 ); 11630 break :msg msg; 11631 }; 11632 return sema.failWithOwnedErrorMsg(block, msg); 11633 } else if (special_prongs == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) { 11634 return sema.fail( 11635 block, 11636 src, 11637 "switch on non-exhaustive enum must include 'else' or '_' prong or both", 11638 .{}, 11639 ); 11640 } 11641 }, 11642 .error_set => else_error_ty = try validateErrSetSwitch( 11643 sema, 11644 block, 11645 &seen_errors, 11646 &case_vals, 11647 cond_ty, 11648 inst_data, 11649 scalar_cases_len, 11650 multi_cases_len, 11651 .{ .body = special_else.body, .end = special_else.end, .src = else_prong_src }, 11652 has_else, 11653 ), 11654 .int, .comptime_int => { 11655 var extra_index: usize = special_end; 11656 { 11657 var scalar_i: u32 = 0; 11658 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11659 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11660 extra_index += 1; 11661 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11662 extra_index += 1 + info.body_len; 11663 11664 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( 11665 block, 11666 &range_set, 11667 item_ref, 11668 cond_ty, 11669 block.src(.{ .switch_case_item = .{ 11670 .switch_node_offset = src_node_offset, 11671 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 11672 .item_idx = .{ .kind = .single, .index = 0 }, 11673 } }), 11674 )); 11675 } 11676 } 11677 { 11678 var multi_i: u32 = 0; 11679 while (multi_i < multi_cases_len) : (multi_i += 1) { 11680 const items_len = sema.code.extra[extra_index]; 11681 extra_index += 1; 11682 const ranges_len = sema.code.extra[extra_index]; 11683 extra_index += 1; 11684 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11685 extra_index += 1; 11686 const items = sema.code.refSlice(extra_index, items_len); 11687 extra_index += items_len; 11688 11689 try case_vals.ensureUnusedCapacity(gpa, items.len); 11690 for (items, 0..) |item_ref, item_i| { 11691 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( 11692 block, 11693 &range_set, 11694 item_ref, 11695 cond_ty, 11696 block.src(.{ .switch_case_item = .{ 11697 .switch_node_offset = src_node_offset, 11698 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11699 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11700 } }), 11701 )); 11702 } 11703 11704 try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len); 11705 var range_i: u32 = 0; 11706 while (range_i < ranges_len) : (range_i += 1) { 11707 const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11708 extra_index += 1; 11709 const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11710 extra_index += 1; 11711 11712 const vals = try sema.validateSwitchRange( 11713 block, 11714 &range_set, 11715 item_first, 11716 item_last, 11717 cond_ty, 11718 block.src(.{ .switch_case_item = .{ 11719 .switch_node_offset = src_node_offset, 11720 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11721 .item_idx = .{ .kind = .range, .index = @intCast(range_i) }, 11722 } }), 11723 ); 11724 case_vals.appendAssumeCapacity(vals[0]); 11725 case_vals.appendAssumeCapacity(vals[1]); 11726 } 11727 11728 extra_index += info.body_len; 11729 } 11730 } 11731 11732 check_range: { 11733 if (cond_ty.zigTypeTag(zcu) == .int) { 11734 const min_int = try cond_ty.minInt(pt, cond_ty); 11735 const max_int = try cond_ty.maxInt(pt, cond_ty); 11736 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) { 11737 if (has_else) { 11738 return sema.fail( 11739 block, 11740 else_prong_src, 11741 "unreachable else prong; all cases already handled", 11742 .{}, 11743 ); 11744 } 11745 break :check_range; 11746 } 11747 } 11748 if (special_prongs == .none) { 11749 return sema.fail( 11750 block, 11751 src, 11752 "switch must handle all possibilities", 11753 .{}, 11754 ); 11755 } 11756 } 11757 }, 11758 .bool => { 11759 var extra_index: usize = special_end; 11760 { 11761 var scalar_i: u32 = 0; 11762 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11763 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11764 extra_index += 1; 11765 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11766 extra_index += 1 + info.body_len; 11767 11768 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( 11769 block, 11770 &true_count, 11771 &false_count, 11772 item_ref, 11773 block.src(.{ .switch_case_item = .{ 11774 .switch_node_offset = src_node_offset, 11775 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 11776 .item_idx = .{ .kind = .single, .index = 0 }, 11777 } }), 11778 )); 11779 } 11780 } 11781 { 11782 var multi_i: u32 = 0; 11783 while (multi_i < multi_cases_len) : (multi_i += 1) { 11784 const items_len = sema.code.extra[extra_index]; 11785 extra_index += 1; 11786 const ranges_len = sema.code.extra[extra_index]; 11787 extra_index += 1; 11788 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11789 extra_index += 1; 11790 const items = sema.code.refSlice(extra_index, items_len); 11791 extra_index += items_len + info.body_len; 11792 11793 try case_vals.ensureUnusedCapacity(gpa, items.len); 11794 for (items, 0..) |item_ref, item_i| { 11795 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( 11796 block, 11797 &true_count, 11798 &false_count, 11799 item_ref, 11800 block.src(.{ .switch_case_item = .{ 11801 .switch_node_offset = src_node_offset, 11802 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11803 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11804 } }), 11805 )); 11806 } 11807 11808 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset); 11809 } 11810 } 11811 if (has_else) { 11812 if (true_count + false_count == 2) { 11813 return sema.fail( 11814 block, 11815 else_prong_src, 11816 "unreachable else prong; all cases already handled", 11817 .{}, 11818 ); 11819 } 11820 } else { 11821 if (true_count + false_count < 2) { 11822 return sema.fail( 11823 block, 11824 src, 11825 "switch must handle all possibilities", 11826 .{}, 11827 ); 11828 } 11829 } 11830 }, 11831 .enum_literal, .void, .@"fn", .pointer, .type => { 11832 if (!has_else) { 11833 return sema.fail( 11834 block, 11835 src, 11836 "else prong required when switching on type '{f}'", 11837 .{cond_ty.fmt(pt)}, 11838 ); 11839 } 11840 11841 var seen_values = ValueSrcMap{}; 11842 defer seen_values.deinit(gpa); 11843 11844 var extra_index: usize = special_end; 11845 { 11846 var scalar_i: u32 = 0; 11847 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11848 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11849 extra_index += 1; 11850 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11851 extra_index += 1; 11852 extra_index += info.body_len; 11853 11854 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( 11855 block, 11856 &seen_values, 11857 item_ref, 11858 cond_ty, 11859 block.src(.{ .switch_case_item = .{ 11860 .switch_node_offset = src_node_offset, 11861 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 11862 .item_idx = .{ .kind = .single, .index = 0 }, 11863 } }), 11864 )); 11865 } 11866 } 11867 { 11868 var multi_i: u32 = 0; 11869 while (multi_i < multi_cases_len) : (multi_i += 1) { 11870 const items_len = sema.code.extra[extra_index]; 11871 extra_index += 1; 11872 const ranges_len = sema.code.extra[extra_index]; 11873 extra_index += 1; 11874 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11875 extra_index += 1; 11876 const items = sema.code.refSlice(extra_index, items_len); 11877 extra_index += items_len + info.body_len; 11878 11879 try case_vals.ensureUnusedCapacity(gpa, items.len); 11880 for (items, 0..) |item_ref, item_i| { 11881 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( 11882 block, 11883 &seen_values, 11884 item_ref, 11885 cond_ty, 11886 block.src(.{ .switch_case_item = .{ 11887 .switch_node_offset = src_node_offset, 11888 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11889 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11890 } }), 11891 )); 11892 } 11893 11894 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset); 11895 } 11896 } 11897 }, 11898 11899 .error_union, 11900 .noreturn, 11901 .array, 11902 .@"struct", 11903 .undefined, 11904 .null, 11905 .optional, 11906 .@"opaque", 11907 .vector, 11908 .frame, 11909 .@"anyframe", 11910 .comptime_float, 11911 .float, 11912 => return sema.fail(block, operand_src, "invalid switch operand type '{f}'", .{ 11913 raw_operand_ty.fmt(pt), 11914 }), 11915 } 11916 11917 var special_members_only: ?SpecialProng = null; 11918 var special_members_only_src: LazySrcLoc = undefined; 11919 const special_generic, const special_generic_src = if (has_under) b: { 11920 if (has_else) { 11921 special_members_only = special_else; 11922 special_members_only_src = else_prong_src; 11923 } 11924 break :b .{ special_under, under_prong_src }; 11925 } else .{ special_else, else_prong_src }; 11926 11927 const spa: SwitchProngAnalysis = .{ 11928 .sema = sema, 11929 .parent_block = block, 11930 .operand = operand, 11931 .else_error_ty = else_error_ty, 11932 .switch_block_inst = inst, 11933 .tag_capture_inst = tag_capture_inst, 11934 }; 11935 11936 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 11937 try sema.air_instructions.append(gpa, .{ 11938 .tag = .block, 11939 .data = undefined, 11940 }); 11941 var label: Block.Label = .{ 11942 .zir_block = inst, 11943 .merges = .{ 11944 .src_locs = .{}, 11945 .results = .{}, 11946 .br_list = .{}, 11947 .block_inst = block_inst, 11948 }, 11949 }; 11950 11951 var child_block: Block = .{ 11952 .parent = block, 11953 .sema = sema, 11954 .namespace = block.namespace, 11955 .instructions = .{}, 11956 .label = &label, 11957 .inlining = block.inlining, 11958 .comptime_reason = block.comptime_reason, 11959 .is_typeof = block.is_typeof, 11960 .c_import_buf = block.c_import_buf, 11961 .runtime_cond = block.runtime_cond, 11962 .runtime_loop = block.runtime_loop, 11963 .runtime_index = block.runtime_index, 11964 .want_safety = block.want_safety, 11965 .error_return_trace_index = block.error_return_trace_index, 11966 .src_base_inst = block.src_base_inst, 11967 .type_name_ctx = block.type_name_ctx, 11968 }; 11969 const merges = &child_block.label.?.merges; 11970 defer child_block.instructions.deinit(gpa); 11971 defer merges.deinit(gpa); 11972 11973 if (scalar_cases_len + multi_cases_len == 0 and 11974 special_members_only == null and 11975 !special_generic.is_inline) 11976 { 11977 if (empty_enum) { 11978 return .void_value; 11979 } 11980 if (special_prongs == .none) { 11981 return sema.fail(block, src, "switch must handle all possibilities", .{}); 11982 } 11983 const init_cond = switch (operand) { 11984 .simple => |s| s.cond, 11985 .loop => |l| l.init_cond, 11986 }; 11987 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and 11988 raw_operand_ty.zigTypeTag(zcu) == .@"enum" and !raw_operand_ty.isNonexhaustiveEnum(zcu)) 11989 { 11990 try sema.zirDbgStmt(block, cond_dbg_node_index); 11991 const ok = try block.addUnOp(.is_named_enum_value, init_cond); 11992 try sema.addSafetyCheck(block, src, ok, .corrupt_switch); 11993 } 11994 if (err_set and try sema.maybeErrorUnwrap(block, special_generic.body, init_cond, operand_src, false)) { 11995 return .unreachable_value; 11996 } 11997 } 11998 11999 switch (operand) { 12000 .loop => {}, // always runtime; evaluation in comptime scope uses `simple` 12001 .simple => |s| { 12002 if (try sema.resolveDefinedValue(&child_block, src, s.cond)) |cond_val| { 12003 return resolveSwitchComptimeLoop( 12004 sema, 12005 spa, 12006 &child_block, 12007 if (operand_is_ref) 12008 sema.typeOf(s.by_ref) 12009 else 12010 raw_operand_ty, 12011 cond_ty, 12012 cond_val, 12013 src_node_offset, 12014 special_members_only, 12015 special_generic, 12016 has_under, 12017 case_vals, 12018 scalar_cases_len, 12019 multi_cases_len, 12020 err_set, 12021 empty_enum, 12022 operand_is_ref, 12023 ); 12024 } 12025 12026 if (scalar_cases_len + multi_cases_len == 0 and 12027 special_members_only == null and 12028 !special_generic.is_inline and 12029 !extra.data.bits.has_continue) 12030 { 12031 return spa.resolveProngComptime( 12032 &child_block, 12033 .special, 12034 special_generic.body, 12035 special_generic.capture, 12036 block.src(.{ .switch_capture = .{ 12037 .switch_node_offset = src_node_offset, 12038 .case_idx = if (has_under) .special_under else .special_else, 12039 } }), 12040 undefined, // case_vals may be undefined for special prongs 12041 .none, 12042 false, 12043 merges, 12044 ); 12045 } 12046 }, 12047 } 12048 12049 if (child_block.isComptime()) { 12050 _ = try sema.resolveConstDefinedValue(&child_block, operand_src, operand.simple.cond, null); 12051 unreachable; 12052 } 12053 12054 var extra_case_vals: struct { 12055 items: std.ArrayListUnmanaged(Air.Inst.Ref), 12056 ranges: std.ArrayListUnmanaged([2]Air.Inst.Ref), 12057 } = .{ .items = .empty, .ranges = .empty }; 12058 defer { 12059 extra_case_vals.items.deinit(gpa); 12060 extra_case_vals.ranges.deinit(gpa); 12061 } 12062 12063 // Runtime switch, if we have a special_members_only prong we need to unroll 12064 // it to a prong with explicit items. 12065 // Although this is potentially the same as `inline else` it does not count 12066 // towards the backward branch quota because it's an implementation detail. 12067 if (special_members_only != null) gen: { 12068 assert(cond_ty.isNonexhaustiveEnum(zcu)); 12069 12070 var min_i: usize = math.maxInt(usize); 12071 var max_i: usize = 0; 12072 var seen_field_count: usize = 0; 12073 for (seen_enum_fields, 0..) |seen, enum_i| { 12074 if (seen != null) { 12075 seen_field_count += 1; 12076 } else { 12077 min_i = @min(min_i, enum_i); 12078 max_i = @max(max_i, enum_i); 12079 } 12080 } 12081 if (min_i == max_i) { 12082 seen_enum_fields[min_i] = special_members_only_src; 12083 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i)); 12084 const item_ref = Air.internedToRef(item_val.toIntern()); 12085 try extra_case_vals.items.append(gpa, item_ref); 12086 break :gen; 12087 } 12088 const missing_field_count = seen_enum_fields.len - seen_field_count; 12089 12090 extra_case_vals.items = try .initCapacity(gpa, missing_field_count / 2); 12091 extra_case_vals.ranges = try .initCapacity(gpa, missing_field_count / 4); 12092 const int_ty = cond_ty.intTagType(zcu); 12093 12094 var last_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i)); 12095 var first_ref = Air.internedToRef(last_val.toIntern()); 12096 seen_enum_fields[min_i] = special_members_only_src; 12097 for (seen_enum_fields[(min_i + 1)..(max_i + 1)], (min_i + 1)..) |seen, enum_i| { 12098 if (seen != null) continue; 12099 seen_enum_fields[enum_i] = special_members_only_src; 12100 12101 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(enum_i)); 12102 const item_ref = Air.internedToRef(item_val.toIntern()); 12103 12104 const is_next = is_next: { 12105 const prev_int = ip.indexToKey(last_val.toIntern()).enum_tag.int; 12106 12107 const result = try arith.incrementDefinedInt(sema, int_ty, .fromInterned(prev_int)); 12108 if (result.overflow) break :is_next false; 12109 12110 const item_int = ip.indexToKey(item_val.toIntern()).enum_tag.int; 12111 break :is_next try sema.valuesEqual(.fromInterned(item_int), result.val, int_ty); 12112 }; 12113 12114 if (is_next) { 12115 last_val = item_val; 12116 } else { 12117 const last_ref = Air.internedToRef(last_val.toIntern()); 12118 if (first_ref == last_ref) { 12119 try extra_case_vals.items.append(gpa, first_ref); 12120 } else { 12121 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref }); 12122 } 12123 first_ref = item_ref; 12124 last_val = item_val; 12125 } 12126 } 12127 const last_ref = Air.internedToRef(last_val.toIntern()); 12128 if (first_ref == last_ref) { 12129 try extra_case_vals.items.append(gpa, first_ref); 12130 } else { 12131 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref }); 12132 } 12133 } 12134 12135 const air_switch_ref = try sema.analyzeSwitchRuntimeBlock( 12136 spa, 12137 &child_block, 12138 src, 12139 switch (operand) { 12140 .simple => |s| s.cond, 12141 .loop => |l| l.init_cond, 12142 }, 12143 cond_ty, 12144 operand_src, 12145 case_vals, 12146 special_generic, 12147 scalar_cases_len, 12148 multi_cases_len, 12149 union_originally, 12150 raw_operand_ty, 12151 err_set, 12152 src_node_offset, 12153 special_generic_src, 12154 has_under, 12155 seen_enum_fields, 12156 seen_errors, 12157 range_set, 12158 true_count, 12159 false_count, 12160 cond_dbg_node_index, 12161 false, 12162 special_members_only, 12163 special_members_only_src, 12164 extra_case_vals.items.items, 12165 extra_case_vals.ranges.items, 12166 ); 12167 12168 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| { 12169 var replacement_block = block.makeSubBlock(); 12170 defer replacement_block.instructions.deinit(gpa); 12171 12172 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br); 12173 const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand; 12174 12175 if (extra.data.bits.any_non_inline_capture) { 12176 _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref); 12177 } 12178 12179 const new_operand_val = if (operand_is_ref) 12180 try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src) 12181 else 12182 new_operand_maybe_ref; 12183 12184 const new_cond = try sema.switchCond(&replacement_block, dispatch_src, new_operand_val); 12185 12186 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and 12187 cond_ty.zigTypeTag(zcu) == .@"enum" and !cond_ty.isNonexhaustiveEnum(zcu) and 12188 !try sema.isComptimeKnown(new_cond)) 12189 { 12190 const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond); 12191 try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch); 12192 } 12193 12194 _ = try replacement_block.addInst(.{ 12195 .tag = .switch_dispatch, 12196 .data = .{ .br = .{ 12197 .block_inst = air_switch_ref.toIndex().?, 12198 .operand = new_cond, 12199 } }, 12200 }); 12201 12202 if (replacement_block.instructions.items.len == 1) { 12203 // Optimization: we don't need a block! 12204 sema.air_instructions.set( 12205 @intFromEnum(placeholder_inst), 12206 sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])), 12207 ); 12208 continue; 12209 } 12210 12211 // Replace placeholder with a block. 12212 // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`. 12213 try sema.air_extra.ensureUnusedCapacity( 12214 gpa, 12215 @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len, 12216 ); 12217 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{ 12218 .tag = .block, 12219 .data = .{ .ty_pl = .{ 12220 .ty = .noreturn_type, 12221 .payload = sema.addExtraAssumeCapacity(Air.Block{ 12222 .body_len = @intCast(replacement_block.instructions.items.len), 12223 }), 12224 } }, 12225 }); 12226 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); 12227 } 12228 12229 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false); 12230 } 12231 12232 const SpecialProng = struct { 12233 body: []const Zir.Inst.Index, 12234 end: usize, 12235 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 12236 is_inline: bool, 12237 has_tag_capture: bool, 12238 }; 12239 12240 fn analyzeSwitchRuntimeBlock( 12241 sema: *Sema, 12242 spa: SwitchProngAnalysis, 12243 child_block: *Block, 12244 src: LazySrcLoc, 12245 operand: Air.Inst.Ref, 12246 operand_ty: Type, 12247 operand_src: LazySrcLoc, 12248 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref), 12249 else_prong: SpecialProng, 12250 scalar_cases_len: usize, 12251 multi_cases_len: usize, 12252 union_originally: bool, 12253 maybe_union_ty: Type, 12254 err_set: bool, 12255 switch_node_offset: std.zig.Ast.Node.Offset, 12256 else_prong_src: LazySrcLoc, 12257 else_prong_is_underscore: bool, 12258 seen_enum_fields: []?LazySrcLoc, 12259 seen_errors: SwitchErrorSet, 12260 range_set: RangeSet, 12261 true_count: u8, 12262 false_count: u8, 12263 cond_dbg_node_index: Zir.Inst.Index, 12264 allow_err_code_unwrap: bool, 12265 extra_prong: ?SpecialProng, 12266 /// May be `undefined` if `extra_prong` is `null` 12267 extra_prong_src: LazySrcLoc, 12268 extra_prong_items: []const Air.Inst.Ref, 12269 extra_prong_ranges: []const [2]Air.Inst.Ref, 12270 ) CompileError!Air.Inst.Ref { 12271 const pt = sema.pt; 12272 const zcu = pt.zcu; 12273 const gpa = sema.gpa; 12274 const ip = &zcu.intern_pool; 12275 12276 const block = child_block.parent.?; 12277 12278 const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * 12279 @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 2; 12280 var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra); 12281 defer cases_extra.deinit(gpa); 12282 12283 var branch_hints = try std.ArrayListUnmanaged(std.builtin.BranchHint).initCapacity(gpa, scalar_cases_len); 12284 defer branch_hints.deinit(gpa); 12285 12286 var case_block = child_block.makeSubBlock(); 12287 case_block.runtime_loop = null; 12288 case_block.runtime_cond = operand_src; 12289 case_block.runtime_index.increment(); 12290 case_block.need_debug_scope = null; // this body is emitted regardless 12291 defer case_block.instructions.deinit(gpa); 12292 12293 var extra_index: usize = else_prong.end; 12294 12295 var scalar_i: usize = 0; 12296 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 12297 extra_index += 1; 12298 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12299 extra_index += 1; 12300 const body = sema.code.bodySlice(extra_index, info.body_len); 12301 extra_index += info.body_len; 12302 12303 case_block.instructions.shrinkRetainingCapacity(0); 12304 case_block.error_return_trace_index = child_block.error_return_trace_index; 12305 12306 const item = case_vals.items[scalar_i]; 12307 // `item` is already guaranteed to be constant known. 12308 12309 const analyze_body = if (union_originally) blk: { 12310 const unresolved_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 12311 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable; 12312 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; 12313 break :blk field_ty.zigTypeTag(zcu) != .noreturn; 12314 } else true; 12315 12316 const prong_hint: std.builtin.BranchHint = if (err_set and 12317 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) 12318 h: { 12319 // nothing to do here. weight against error branch 12320 break :h .unlikely; 12321 } else if (analyze_body) h: { 12322 break :h try spa.analyzeProngRuntime( 12323 &case_block, 12324 .normal, 12325 body, 12326 info.capture, 12327 child_block.src(.{ .switch_capture = .{ 12328 .switch_node_offset = switch_node_offset, 12329 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 12330 } }), 12331 &.{item}, 12332 if (info.is_inline) item else .none, 12333 info.has_tag_capture, 12334 ); 12335 } else h: { 12336 _ = try case_block.addNoOp(.unreach); 12337 break :h .none; 12338 }; 12339 12340 try branch_hints.append(gpa, prong_hint); 12341 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12342 1 + // `item`, no ranges 12343 case_block.instructions.items.len); 12344 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12345 .items_len = 1, 12346 .ranges_len = 0, 12347 .body_len = @intCast(case_block.instructions.items.len), 12348 })); 12349 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12350 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12351 } 12352 12353 var cases_len = scalar_cases_len; 12354 var case_val_idx: usize = scalar_cases_len; 12355 const multi_cases_len_with_extra_prong = multi_cases_len + @intFromBool(extra_prong != null); 12356 var multi_i: u32 = 0; 12357 while (multi_i < multi_cases_len_with_extra_prong) : (multi_i += 1) { 12358 const is_extra_prong = multi_i == multi_cases_len; 12359 var items: []const Air.Inst.Ref = undefined; 12360 var info: Zir.Inst.SwitchBlock.ProngInfo = undefined; 12361 var ranges: []const [2]Air.Inst.Ref = undefined; 12362 var body: []const Zir.Inst.Index = undefined; 12363 if (is_extra_prong) { 12364 const prong = extra_prong.?; 12365 items = extra_prong_items; 12366 ranges = extra_prong_ranges; 12367 body = prong.body; 12368 info = .{ 12369 .body_len = undefined, 12370 .capture = prong.capture, 12371 .is_inline = prong.is_inline, 12372 .has_tag_capture = prong.has_tag_capture, 12373 }; 12374 } else { 12375 @branchHint(.likely); 12376 const items_len = sema.code.extra[extra_index]; 12377 extra_index += 1; 12378 const ranges_len = sema.code.extra[extra_index]; 12379 extra_index += 1; 12380 info = @bitCast(sema.code.extra[extra_index]); 12381 extra_index += 1 + items_len + ranges_len * 2; 12382 12383 items = case_vals.items[case_val_idx..][0..items_len]; 12384 case_val_idx += items_len; 12385 ranges = @ptrCast(case_vals.items[case_val_idx..][0 .. ranges_len * 2]); 12386 case_val_idx += ranges_len * 2; 12387 12388 body = sema.code.bodySlice(extra_index, info.body_len); 12389 extra_index += info.body_len; 12390 } 12391 12392 case_block.instructions.shrinkRetainingCapacity(0); 12393 case_block.error_return_trace_index = child_block.error_return_trace_index; 12394 12395 // Generate all possible cases as scalar prongs. 12396 if (info.is_inline) { 12397 var emit_bb = false; 12398 12399 for (ranges, 0..) |range_items, range_i| { 12400 var item = sema.resolveConstDefinedValue(block, .unneeded, range_items[0], undefined) catch unreachable; 12401 const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_items[1], undefined) catch unreachable; 12402 12403 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({ 12404 // Previous validation has resolved any possible lazy values. 12405 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) { 12406 .int => .{ item, operand_ty }, 12407 .@"enum" => b: { 12408 const int_val = Value.fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int); 12409 break :b .{ int_val, int_val.typeOf(zcu) }; 12410 }, 12411 else => unreachable, 12412 }; 12413 const result = try arith.incrementDefinedInt(sema, int_ty, int_val); 12414 assert(!result.overflow); 12415 item = switch (operand_ty.zigTypeTag(zcu)) { 12416 .int => result.val, 12417 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{ 12418 .ty = operand_ty.toIntern(), 12419 .int = result.val.toIntern(), 12420 } })), 12421 else => unreachable, 12422 }; 12423 }) { 12424 cases_len += 1; 12425 12426 const item_ref = Air.internedToRef(item.toIntern()); 12427 12428 case_block.instructions.shrinkRetainingCapacity(0); 12429 case_block.error_return_trace_index = child_block.error_return_trace_index; 12430 12431 if (emit_bb) { 12432 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{ 12433 .switch_node_offset = switch_node_offset, 12434 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12435 .item_idx = .{ .kind = .range, .index = @intCast(range_i) }, 12436 } }); 12437 try sema.emitBackwardBranch(block, bb_src); 12438 } 12439 emit_bb = true; 12440 12441 const prong_hint = try spa.analyzeProngRuntime( 12442 &case_block, 12443 .normal, 12444 body, 12445 info.capture, 12446 child_block.src(.{ .switch_capture = .{ 12447 .switch_node_offset = switch_node_offset, 12448 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12449 } }), 12450 undefined, // case_vals may be undefined for ranges 12451 item_ref, 12452 info.has_tag_capture, 12453 ); 12454 try branch_hints.append(gpa, prong_hint); 12455 12456 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12457 1 + // `item`, no ranges 12458 case_block.instructions.items.len); 12459 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12460 .items_len = 1, 12461 .ranges_len = 0, 12462 .body_len = @intCast(case_block.instructions.items.len), 12463 })); 12464 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12465 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12466 12467 if (item.compareScalar(.eq, item_last, operand_ty, zcu)) break; 12468 } 12469 } 12470 12471 for (items, 0..) |item, item_i| { 12472 cases_len += 1; 12473 12474 case_block.instructions.shrinkRetainingCapacity(0); 12475 case_block.error_return_trace_index = child_block.error_return_trace_index; 12476 12477 const analyze_body = if (union_originally) blk: { 12478 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 12479 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; 12480 break :blk field_ty.zigTypeTag(zcu) != .noreturn; 12481 } else true; 12482 12483 if (emit_bb) { 12484 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{ 12485 .switch_node_offset = switch_node_offset, 12486 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12487 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 12488 } }); 12489 try sema.emitBackwardBranch(block, bb_src); 12490 } 12491 emit_bb = true; 12492 12493 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: { 12494 break :h try spa.analyzeProngRuntime( 12495 &case_block, 12496 .normal, 12497 body, 12498 info.capture, 12499 child_block.src(.{ .switch_capture = .{ 12500 .switch_node_offset = switch_node_offset, 12501 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12502 } }), 12503 &.{item}, 12504 item, 12505 info.has_tag_capture, 12506 ); 12507 } else h: { 12508 _ = try case_block.addNoOp(.unreach); 12509 break :h .none; 12510 }; 12511 try branch_hints.append(gpa, prong_hint); 12512 12513 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12514 1 + // `item`, no ranges 12515 case_block.instructions.items.len); 12516 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12517 .items_len = 1, 12518 .ranges_len = 0, 12519 .body_len = @intCast(case_block.instructions.items.len), 12520 })); 12521 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12522 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12523 } 12524 12525 continue; 12526 } 12527 12528 cases_len += 1; 12529 12530 const analyze_body = if (union_originally) 12531 for (items) |item| { 12532 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 12533 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; 12534 if (field_ty.zigTypeTag(zcu) != .noreturn) break true; 12535 } else false 12536 else 12537 true; 12538 12539 const prong_hint: std.builtin.BranchHint = if (err_set and 12540 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) 12541 h: { 12542 // nothing to do here. weight against error branch 12543 break :h .unlikely; 12544 } else if (analyze_body) h: { 12545 break :h try spa.analyzeProngRuntime( 12546 &case_block, 12547 .normal, 12548 body, 12549 info.capture, 12550 child_block.src(.{ .switch_capture = .{ 12551 .switch_node_offset = switch_node_offset, 12552 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12553 } }), 12554 items, 12555 .none, 12556 false, 12557 ); 12558 } else h: { 12559 _ = try case_block.addNoOp(.unreach); 12560 break :h .none; 12561 }; 12562 12563 try branch_hints.append(gpa, prong_hint); 12564 12565 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12566 items.len + ranges.len * 2 + 12567 case_block.instructions.items.len); 12568 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12569 .items_len = @intCast(items.len), 12570 .ranges_len = @intCast(ranges.len), 12571 .body_len = @intCast(case_block.instructions.items.len), 12572 })); 12573 12574 for (items) |item| { 12575 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12576 } 12577 for (ranges) |range| { 12578 cases_extra.appendSliceAssumeCapacity(&.{ 12579 @intFromEnum(range[0]), 12580 @intFromEnum(range[1]), 12581 }); 12582 } 12583 12584 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12585 } 12586 12587 const else_body: []const Air.Inst.Index = if (else_prong.body.len != 0 or case_block.wantSafety()) else_body: { 12588 var emit_bb = false; 12589 // If this is true we must have a 'true' else prong and not an underscore because 12590 // underscore prongs can never be inlined. We've already checked for this. 12591 if (else_prong.is_inline) switch (operand_ty.zigTypeTag(zcu)) { 12592 .@"enum" => { 12593 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) { 12594 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{ 12595 operand_ty.fmt(pt), 12596 }); 12597 } 12598 for (seen_enum_fields, 0..) |f, i| { 12599 if (f != null) continue; 12600 cases_len += 1; 12601 12602 const item_val = try pt.enumValueFieldIndex(operand_ty, @intCast(i)); 12603 const item_ref = Air.internedToRef(item_val.toIntern()); 12604 12605 case_block.instructions.shrinkRetainingCapacity(0); 12606 case_block.error_return_trace_index = child_block.error_return_trace_index; 12607 12608 const analyze_body = if (union_originally) blk: { 12609 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; 12610 break :blk field_ty.zigTypeTag(zcu) != .noreturn; 12611 } else true; 12612 12613 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); 12614 emit_bb = true; 12615 12616 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: { 12617 break :h try spa.analyzeProngRuntime( 12618 &case_block, 12619 .special, 12620 else_prong.body, 12621 else_prong.capture, 12622 child_block.src(.{ .switch_capture = .{ 12623 .switch_node_offset = switch_node_offset, 12624 .case_idx = .special_else, 12625 } }), 12626 &.{item_ref}, 12627 item_ref, 12628 else_prong.has_tag_capture, 12629 ); 12630 } else h: { 12631 _ = try case_block.addNoOp(.unreach); 12632 break :h .none; 12633 }; 12634 try branch_hints.append(gpa, prong_hint); 12635 12636 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12637 1 + // `item`, no ranges 12638 case_block.instructions.items.len); 12639 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12640 .items_len = 1, 12641 .ranges_len = 0, 12642 .body_len = @intCast(case_block.instructions.items.len), 12643 })); 12644 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12645 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12646 } 12647 }, 12648 .error_set => { 12649 if (operand_ty.isAnyError(zcu)) { 12650 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{ 12651 operand_ty.fmt(pt), 12652 }); 12653 } 12654 const error_names = operand_ty.errorSetNames(zcu); 12655 for (0..error_names.len) |name_index| { 12656 const error_name = error_names.get(ip)[name_index]; 12657 if (seen_errors.contains(error_name)) continue; 12658 cases_len += 1; 12659 12660 const item_val = try pt.intern(.{ .err = .{ 12661 .ty = operand_ty.toIntern(), 12662 .name = error_name, 12663 } }); 12664 const item_ref = Air.internedToRef(item_val); 12665 12666 case_block.instructions.shrinkRetainingCapacity(0); 12667 case_block.error_return_trace_index = child_block.error_return_trace_index; 12668 12669 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); 12670 emit_bb = true; 12671 12672 const prong_hint = try spa.analyzeProngRuntime( 12673 &case_block, 12674 .special, 12675 else_prong.body, 12676 else_prong.capture, 12677 child_block.src(.{ .switch_capture = .{ 12678 .switch_node_offset = switch_node_offset, 12679 .case_idx = .special_else, 12680 } }), 12681 &.{item_ref}, 12682 item_ref, 12683 else_prong.has_tag_capture, 12684 ); 12685 try branch_hints.append(gpa, prong_hint); 12686 12687 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12688 1 + // `item`, no ranges 12689 case_block.instructions.items.len); 12690 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12691 .items_len = 1, 12692 .ranges_len = 0, 12693 .body_len = @intCast(case_block.instructions.items.len), 12694 })); 12695 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12696 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12697 } 12698 }, 12699 .int => { 12700 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set); 12701 while (try it.next()) |cur| { 12702 cases_len += 1; 12703 12704 const item_ref = Air.internedToRef(cur); 12705 12706 case_block.instructions.shrinkRetainingCapacity(0); 12707 case_block.error_return_trace_index = child_block.error_return_trace_index; 12708 12709 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); 12710 emit_bb = true; 12711 12712 const prong_hint = try spa.analyzeProngRuntime( 12713 &case_block, 12714 .special, 12715 else_prong.body, 12716 else_prong.capture, 12717 child_block.src(.{ .switch_capture = .{ 12718 .switch_node_offset = switch_node_offset, 12719 .case_idx = .special_else, 12720 } }), 12721 &.{item_ref}, 12722 item_ref, 12723 else_prong.has_tag_capture, 12724 ); 12725 try branch_hints.append(gpa, prong_hint); 12726 12727 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12728 1 + // `item`, no ranges 12729 case_block.instructions.items.len); 12730 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12731 .items_len = 1, 12732 .ranges_len = 0, 12733 .body_len = @intCast(case_block.instructions.items.len), 12734 })); 12735 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12736 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12737 } 12738 }, 12739 .bool => { 12740 if (true_count == 0) { 12741 cases_len += 1; 12742 12743 case_block.instructions.shrinkRetainingCapacity(0); 12744 case_block.error_return_trace_index = child_block.error_return_trace_index; 12745 12746 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); 12747 emit_bb = true; 12748 12749 const prong_hint = try spa.analyzeProngRuntime( 12750 &case_block, 12751 .special, 12752 else_prong.body, 12753 else_prong.capture, 12754 child_block.src(.{ .switch_capture = .{ 12755 .switch_node_offset = switch_node_offset, 12756 .case_idx = .special_else, 12757 } }), 12758 &.{.bool_true}, 12759 .bool_true, 12760 else_prong.has_tag_capture, 12761 ); 12762 try branch_hints.append(gpa, prong_hint); 12763 12764 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12765 1 + // `item`, no ranges 12766 case_block.instructions.items.len); 12767 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12768 .items_len = 1, 12769 .ranges_len = 0, 12770 .body_len = @intCast(case_block.instructions.items.len), 12771 })); 12772 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true)); 12773 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12774 } 12775 if (false_count == 0) { 12776 cases_len += 1; 12777 12778 case_block.instructions.shrinkRetainingCapacity(0); 12779 case_block.error_return_trace_index = child_block.error_return_trace_index; 12780 12781 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); 12782 emit_bb = true; 12783 12784 const prong_hint = try spa.analyzeProngRuntime( 12785 &case_block, 12786 .special, 12787 else_prong.body, 12788 else_prong.capture, 12789 child_block.src(.{ .switch_capture = .{ 12790 .switch_node_offset = switch_node_offset, 12791 .case_idx = .special_else, 12792 } }), 12793 &.{.bool_false}, 12794 .bool_false, 12795 else_prong.has_tag_capture, 12796 ); 12797 try branch_hints.append(gpa, prong_hint); 12798 12799 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12800 1 + // `item`, no ranges 12801 case_block.instructions.items.len); 12802 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12803 .items_len = 1, 12804 .ranges_len = 0, 12805 .body_len = @intCast(case_block.instructions.items.len), 12806 })); 12807 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false)); 12808 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12809 } 12810 }, 12811 else => return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{ 12812 operand_ty.fmt(pt), 12813 }), 12814 }; 12815 12816 case_block.instructions.shrinkRetainingCapacity(0); 12817 case_block.error_return_trace_index = child_block.error_return_trace_index; 12818 12819 if (zcu.backendSupportsFeature(.is_named_enum_value) and 12820 else_prong.body.len != 0 and block.wantSafety() and 12821 operand_ty.zigTypeTag(zcu) == .@"enum" and 12822 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally)) 12823 { 12824 try sema.zirDbgStmt(&case_block, cond_dbg_node_index); 12825 const ok = try case_block.addUnOp(.is_named_enum_value, operand); 12826 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); 12827 } 12828 12829 const else_src_idx: LazySrcLoc.Offset.SwitchCaseIndex = if (else_prong_is_underscore) 12830 .special_under 12831 else 12832 .special_else; 12833 12834 const analyze_body = if (union_originally and !else_prong.is_inline) 12835 for (seen_enum_fields, 0..) |seen_field, index| { 12836 if (seen_field != null) continue; 12837 const union_obj = zcu.typeToUnion(maybe_union_ty).?; 12838 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]); 12839 if (field_ty.zigTypeTag(zcu) != .noreturn) break true; 12840 } else false 12841 else 12842 true; 12843 const else_hint: std.builtin.BranchHint = if (else_prong.body.len != 0 and err_set and 12844 try sema.maybeErrorUnwrap(&case_block, else_prong.body, operand, operand_src, allow_err_code_unwrap)) 12845 h: { 12846 // nothing to do here. weight against error branch 12847 break :h .unlikely; 12848 } else if (else_prong.body.len != 0 and analyze_body and !else_prong.is_inline) h: { 12849 break :h try spa.analyzeProngRuntime( 12850 &case_block, 12851 .special, 12852 else_prong.body, 12853 else_prong.capture, 12854 child_block.src(.{ .switch_capture = .{ 12855 .switch_node_offset = switch_node_offset, 12856 .case_idx = else_src_idx, 12857 } }), 12858 undefined, // case_vals may be undefined for special prongs 12859 .none, 12860 false, 12861 ); 12862 } else h: { 12863 // We still need a terminator in this block, but we have proven 12864 // that it is unreachable. 12865 if (case_block.wantSafety()) { 12866 try sema.zirDbgStmt(&case_block, cond_dbg_node_index); 12867 try sema.safetyPanic(&case_block, src, .corrupt_switch); 12868 } else { 12869 _ = try case_block.addNoOp(.unreach); 12870 } 12871 // Safety check / unreachable branches are cold. 12872 break :h .cold; 12873 }; 12874 12875 try branch_hints.append(gpa, else_hint); 12876 break :else_body case_block.instructions.items; 12877 } else else_body: { 12878 try branch_hints.append(gpa, .none); 12879 break :else_body &.{}; 12880 }; 12881 12882 assert(branch_hints.items.len == cases_len + 1); 12883 12884 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + 12885 cases_extra.items.len + else_body.len + 12886 (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints 12887 12888 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{ 12889 .cases_len = @intCast(cases_len), 12890 .else_body_len = @intCast(else_body.len), 12891 }); 12892 12893 { 12894 // Add branch hints. 12895 var cur_bag: u32 = 0; 12896 for (branch_hints.items, 0..) |hint, idx| { 12897 const idx_in_bag = idx % 10; 12898 cur_bag |= @as(u32, @intFromEnum(hint)) << @intCast(idx_in_bag * 3); 12899 if (idx_in_bag == 9) { 12900 sema.air_extra.appendAssumeCapacity(cur_bag); 12901 cur_bag = 0; 12902 } 12903 } 12904 if (branch_hints.items.len % 10 != 0) { 12905 sema.air_extra.appendAssumeCapacity(cur_bag); 12906 } 12907 } 12908 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items)); 12909 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_body)); 12910 12911 const has_any_continues = spa.operand == .loop and child_block.label.?.merges.extra_insts.items.len > 0; 12912 12913 return try child_block.addInst(.{ 12914 .tag = if (has_any_continues) .loop_switch_br else .switch_br, 12915 .data = .{ .pl_op = .{ 12916 .operand = operand, 12917 .payload = payload_index, 12918 } }, 12919 }); 12920 } 12921 12922 fn resolveSwitchComptimeLoop( 12923 sema: *Sema, 12924 init_spa: SwitchProngAnalysis, 12925 child_block: *Block, 12926 maybe_ptr_operand_ty: Type, 12927 cond_ty: Type, 12928 init_cond_val: Value, 12929 switch_node_offset: std.zig.Ast.Node.Offset, 12930 special_members_only: ?SpecialProng, 12931 special_generic: SpecialProng, 12932 special_generic_is_under: bool, 12933 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref), 12934 scalar_cases_len: u32, 12935 multi_cases_len: u32, 12936 err_set: bool, 12937 empty_enum: bool, 12938 operand_is_ref: bool, 12939 ) CompileError!Air.Inst.Ref { 12940 var spa = init_spa; 12941 var cond_val = init_cond_val; 12942 12943 while (true) { 12944 if (resolveSwitchComptime( 12945 sema, 12946 spa, 12947 child_block, 12948 spa.operand.simple.cond, 12949 cond_val, 12950 cond_ty, 12951 switch_node_offset, 12952 special_members_only, 12953 special_generic, 12954 special_generic_is_under, 12955 case_vals, 12956 scalar_cases_len, 12957 multi_cases_len, 12958 err_set, 12959 empty_enum, 12960 )) |result| { 12961 return result; 12962 } else |err| switch (err) { 12963 error.ComptimeBreak => { 12964 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst)); 12965 if (break_inst.tag != .switch_continue) return error.ComptimeBreak; 12966 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data; 12967 if (extra.block_inst != spa.switch_block_inst) return error.ComptimeBreak; 12968 // This is a `switch_continue` targeting this block. Change the operand and start over. 12969 const src = child_block.nodeOffset(extra.operand_src_node.unwrap().?); 12970 const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand); 12971 const new_operand = try sema.coerce(child_block, maybe_ptr_operand_ty, new_operand_uncoerced, src); 12972 12973 try sema.emitBackwardBranch(child_block, src); 12974 12975 const val, const ref = if (operand_is_ref) 12976 .{ try sema.analyzeLoad(child_block, src, new_operand, src), new_operand } 12977 else 12978 .{ new_operand, undefined }; 12979 12980 const cond_ref = try sema.switchCond(child_block, src, val); 12981 12982 cond_val = try sema.resolveConstDefinedValue(child_block, src, cond_ref, null); 12983 spa.operand = .{ .simple = .{ 12984 .by_val = val, 12985 .by_ref = ref, 12986 .cond = cond_ref, 12987 } }; 12988 }, 12989 else => |e| return e, 12990 } 12991 } 12992 } 12993 12994 fn resolveSwitchComptime( 12995 sema: *Sema, 12996 spa: SwitchProngAnalysis, 12997 child_block: *Block, 12998 cond_operand: Air.Inst.Ref, 12999 operand_val: Value, 13000 operand_ty: Type, 13001 switch_node_offset: std.zig.Ast.Node.Offset, 13002 special_members_only: ?SpecialProng, 13003 special_generic: SpecialProng, 13004 special_generic_is_under: bool, 13005 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref), 13006 scalar_cases_len: u32, 13007 multi_cases_len: u32, 13008 err_set: bool, 13009 empty_enum: bool, 13010 ) CompileError!Air.Inst.Ref { 13011 const zcu = sema.pt.zcu; 13012 const merges = &child_block.label.?.merges; 13013 const resolved_operand_val = try sema.resolveLazyValue(operand_val); 13014 13015 var extra_index: usize = special_generic.end; 13016 { 13017 var scalar_i: usize = 0; 13018 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 13019 extra_index += 1; 13020 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 13021 extra_index += 1; 13022 const body = sema.code.bodySlice(extra_index, info.body_len); 13023 extra_index += info.body_len; 13024 13025 const item = case_vals.items[scalar_i]; 13026 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 13027 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) { 13028 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand); 13029 return spa.resolveProngComptime( 13030 child_block, 13031 .normal, 13032 body, 13033 info.capture, 13034 child_block.src(.{ .switch_capture = .{ 13035 .switch_node_offset = switch_node_offset, 13036 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 13037 } }), 13038 &.{item}, 13039 if (info.is_inline) cond_operand else .none, 13040 info.has_tag_capture, 13041 merges, 13042 ); 13043 } 13044 } 13045 } 13046 { 13047 var multi_i: usize = 0; 13048 var case_val_idx: usize = scalar_cases_len; 13049 while (multi_i < multi_cases_len) : (multi_i += 1) { 13050 const items_len = sema.code.extra[extra_index]; 13051 extra_index += 1; 13052 const ranges_len = sema.code.extra[extra_index]; 13053 extra_index += 1; 13054 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 13055 extra_index += 1 + items_len; 13056 const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len); 13057 13058 const items = case_vals.items[case_val_idx..][0..items_len]; 13059 case_val_idx += items_len; 13060 13061 for (items) |item| { 13062 // Validation above ensured these will succeed. 13063 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 13064 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) { 13065 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand); 13066 return spa.resolveProngComptime( 13067 child_block, 13068 .normal, 13069 body, 13070 info.capture, 13071 child_block.src(.{ .switch_capture = .{ 13072 .switch_node_offset = switch_node_offset, 13073 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 13074 } }), 13075 items, 13076 if (info.is_inline) cond_operand else .none, 13077 info.has_tag_capture, 13078 merges, 13079 ); 13080 } 13081 } 13082 13083 var range_i: usize = 0; 13084 while (range_i < ranges_len) : (range_i += 1) { 13085 const range_items = case_vals.items[case_val_idx..][0..2]; 13086 extra_index += 2; 13087 case_val_idx += 2; 13088 13089 // Validation above ensured these will succeed. 13090 const first_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable; 13091 const last_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable; 13092 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and 13093 (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty))) 13094 { 13095 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand); 13096 return spa.resolveProngComptime( 13097 child_block, 13098 .normal, 13099 body, 13100 info.capture, 13101 child_block.src(.{ .switch_capture = .{ 13102 .switch_node_offset = switch_node_offset, 13103 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 13104 } }), 13105 undefined, // case_vals may be undefined for ranges 13106 if (info.is_inline) cond_operand else .none, 13107 info.has_tag_capture, 13108 merges, 13109 ); 13110 } 13111 } 13112 13113 extra_index += info.body_len; 13114 } 13115 } 13116 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special_generic.body, cond_operand); 13117 if (empty_enum) { 13118 return .void_value; 13119 } 13120 if (special_members_only) |special| { 13121 assert(operand_ty.isNonexhaustiveEnum(zcu)); 13122 if (operand_ty.enumTagFieldIndex(operand_val, zcu)) |_| { 13123 return spa.resolveProngComptime( 13124 child_block, 13125 .special, 13126 special.body, 13127 special.capture, 13128 child_block.src(.{ .switch_capture = .{ 13129 .switch_node_offset = switch_node_offset, 13130 .case_idx = .special_else, 13131 } }), 13132 undefined, // case_vals may be undefined for special prongs 13133 if (special.is_inline) cond_operand else .none, 13134 special.has_tag_capture, 13135 merges, 13136 ); 13137 } 13138 } 13139 13140 return spa.resolveProngComptime( 13141 child_block, 13142 .special, 13143 special_generic.body, 13144 special_generic.capture, 13145 child_block.src(.{ .switch_capture = .{ 13146 .switch_node_offset = switch_node_offset, 13147 .case_idx = if (special_generic_is_under) 13148 .special_under 13149 else 13150 .special_else, 13151 } }), 13152 undefined, // case_vals may be undefined for special prongs 13153 if (special_generic.is_inline) cond_operand else .none, 13154 special_generic.has_tag_capture, 13155 merges, 13156 ); 13157 } 13158 13159 const RangeSetUnhandledIterator = struct { 13160 pt: Zcu.PerThread, 13161 cur: ?InternPool.Index, 13162 max: InternPool.Index, 13163 range_i: usize, 13164 ranges: []const RangeSet.Range, 13165 limbs: []math.big.Limb, 13166 13167 const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128); 13168 13169 fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator { 13170 const pt = sema.pt; 13171 const int_type = pt.zcu.intern_pool.indexToKey(ty.toIntern()).int_type; 13172 const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits); 13173 return .{ 13174 .pt = pt, 13175 .cur = (try ty.minInt(pt, ty)).toIntern(), 13176 .max = (try ty.maxInt(pt, ty)).toIntern(), 13177 .range_i = 0, 13178 .ranges = range_set.ranges.items, 13179 .limbs = if (needed_limbs > preallocated_limbs) 13180 try sema.arena.alloc(math.big.Limb, needed_limbs) 13181 else 13182 &.{}, 13183 }; 13184 } 13185 13186 fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index { 13187 if (val == it.max) return null; 13188 const int = it.pt.zcu.intern_pool.indexToKey(val).int; 13189 13190 switch (int.storage) { 13191 inline .u64, .i64 => |val_int| { 13192 const next_int = @addWithOverflow(val_int, 1); 13193 if (next_int[1] == 0) 13194 return (try it.pt.intValue(.fromInterned(int.ty), next_int[0])).toIntern(); 13195 }, 13196 .big_int => {}, 13197 .lazy_align, .lazy_size => unreachable, 13198 } 13199 13200 var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined; 13201 const val_bigint = int.storage.toBigInt(&val_space); 13202 13203 var result_limbs: [preallocated_limbs]math.big.Limb = undefined; 13204 var result_bigint = math.big.int.Mutable.init( 13205 if (it.limbs.len > 0) it.limbs else &result_limbs, 13206 0, 13207 ); 13208 13209 result_bigint.addScalar(val_bigint, 1); 13210 return (try it.pt.intValue_big(.fromInterned(int.ty), result_bigint.toConst())).toIntern(); 13211 } 13212 13213 fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index { 13214 var cur = it.cur orelse return null; 13215 while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) { 13216 defer it.range_i += 1; 13217 cur = (try it.addOne(it.ranges[it.range_i].last)) orelse { 13218 it.cur = null; 13219 return null; 13220 }; 13221 } 13222 it.cur = try it.addOne(cur); 13223 return cur; 13224 } 13225 }; 13226 13227 const ResolvedSwitchItem = struct { 13228 ref: Air.Inst.Ref, 13229 val: InternPool.Index, 13230 }; 13231 fn resolveSwitchItemVal( 13232 sema: *Sema, 13233 block: *Block, 13234 item_ref: Zir.Inst.Ref, 13235 /// Coerce `item_ref` to this type. 13236 coerce_ty: Type, 13237 item_src: LazySrcLoc, 13238 ) CompileError!ResolvedSwitchItem { 13239 const uncoerced_item = try sema.resolveInst(item_ref); 13240 13241 // Constructing a LazySrcLoc is costly because we only have the switch AST node. 13242 // Only if we know for sure we need to report a compile error do we resolve the 13243 // full source locations. 13244 13245 const item = try sema.coerce(block, coerce_ty, uncoerced_item, item_src); 13246 13247 const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item, .{ .simple = .switch_item }); 13248 13249 const val = try sema.resolveLazyValue(maybe_lazy); 13250 const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: { 13251 break :blk Air.internedToRef(val.toIntern()); 13252 } else item; 13253 13254 return .{ .ref = new_item, .val = val.toIntern() }; 13255 } 13256 13257 fn validateErrSetSwitch( 13258 sema: *Sema, 13259 block: *Block, 13260 seen_errors: *SwitchErrorSet, 13261 case_vals: *std.ArrayListUnmanaged(Air.Inst.Ref), 13262 operand_ty: Type, 13263 inst_data: @FieldType(Zir.Inst.Data, "pl_node"), 13264 scalar_cases_len: u32, 13265 multi_cases_len: u32, 13266 else_case: struct { body: []const Zir.Inst.Index, end: usize, src: LazySrcLoc }, 13267 has_else: bool, 13268 ) CompileError!?Type { 13269 const gpa = sema.gpa; 13270 const pt = sema.pt; 13271 const zcu = pt.zcu; 13272 const ip = &zcu.intern_pool; 13273 13274 const src_node_offset = inst_data.src_node; 13275 const src = block.nodeOffset(src_node_offset); 13276 13277 var extra_index: usize = else_case.end; 13278 { 13279 var scalar_i: u32 = 0; 13280 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 13281 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 13282 extra_index += 1; 13283 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 13284 extra_index += 1 + info.body_len; 13285 13286 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( 13287 block, 13288 seen_errors, 13289 item_ref, 13290 operand_ty, 13291 block.src(.{ .switch_case_item = .{ 13292 .switch_node_offset = src_node_offset, 13293 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 13294 .item_idx = .{ .kind = .single, .index = 0 }, 13295 } }), 13296 )); 13297 } 13298 } 13299 { 13300 var multi_i: u32 = 0; 13301 while (multi_i < multi_cases_len) : (multi_i += 1) { 13302 const items_len = sema.code.extra[extra_index]; 13303 extra_index += 1; 13304 const ranges_len = sema.code.extra[extra_index]; 13305 extra_index += 1; 13306 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 13307 extra_index += 1; 13308 const items = sema.code.refSlice(extra_index, items_len); 13309 extra_index += items_len + info.body_len; 13310 13311 try case_vals.ensureUnusedCapacity(gpa, items.len); 13312 for (items, 0..) |item_ref, item_i| { 13313 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( 13314 block, 13315 seen_errors, 13316 item_ref, 13317 operand_ty, 13318 block.src(.{ .switch_case_item = .{ 13319 .switch_node_offset = src_node_offset, 13320 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 13321 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 13322 } }), 13323 )); 13324 } 13325 13326 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 13327 } 13328 } 13329 13330 switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) { 13331 .anyerror_type => { 13332 if (!has_else) { 13333 return sema.fail( 13334 block, 13335 src, 13336 "else prong required when switching on type 'anyerror'", 13337 .{}, 13338 ); 13339 } 13340 return .anyerror; 13341 }, 13342 else => |err_set_ty_index| else_validation: { 13343 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names; 13344 var maybe_msg: ?*Zcu.ErrorMsg = null; 13345 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); 13346 13347 for (error_names.get(ip)) |error_name| { 13348 if (!seen_errors.contains(error_name) and !has_else) { 13349 const msg = maybe_msg orelse blk: { 13350 maybe_msg = try sema.errMsg( 13351 src, 13352 "switch must handle all possibilities", 13353 .{}, 13354 ); 13355 break :blk maybe_msg.?; 13356 }; 13357 13358 try sema.errNote( 13359 src, 13360 msg, 13361 "unhandled error value: 'error.{f}'", 13362 .{error_name.fmt(ip)}, 13363 ); 13364 } 13365 } 13366 13367 if (maybe_msg) |msg| { 13368 maybe_msg = null; 13369 try sema.addDeclaredHereNote(msg, operand_ty); 13370 return sema.failWithOwnedErrorMsg(block, msg); 13371 } 13372 13373 if (has_else and seen_errors.count() == error_names.len) { 13374 // In order to enable common patterns for generic code allow simple else bodies 13375 // else => unreachable, 13376 // else => return, 13377 // else => |e| return e, 13378 // even if all the possible errors were already handled. 13379 const tags = sema.code.instructions.items(.tag); 13380 const datas = sema.code.instructions.items(.data); 13381 for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) { 13382 .dbg_stmt, 13383 .dbg_var_val, 13384 .ret_type, 13385 .as_node, 13386 .ret_node, 13387 .@"unreachable", 13388 .@"defer", 13389 .defer_err_code, 13390 .err_union_code, 13391 .ret_err_value_code, 13392 .save_err_ret_index, 13393 .restore_err_ret_index_unconditional, 13394 .restore_err_ret_index_fn_entry, 13395 .is_non_err, 13396 .ret_is_non_err, 13397 .condbr, 13398 => {}, 13399 .extended => switch (datas[@intFromEnum(else_inst)].extended.opcode) { 13400 .restore_err_ret_index => {}, 13401 else => break, 13402 }, 13403 else => break, 13404 } else break :else_validation; 13405 13406 return sema.fail( 13407 block, 13408 else_case.src, 13409 "unreachable else prong; all cases already handled", 13410 .{}, 13411 ); 13412 } 13413 13414 var names: InferredErrorSet.NameMap = .{}; 13415 try names.ensureUnusedCapacity(sema.arena, error_names.len); 13416 for (error_names.get(ip)) |error_name| { 13417 if (seen_errors.contains(error_name)) continue; 13418 13419 names.putAssumeCapacityNoClobber(error_name, {}); 13420 } 13421 // No need to keep the hash map metadata correct; here we 13422 // extract the (sorted) keys only. 13423 return try pt.errorSetFromUnsortedNames(names.keys()); 13424 }, 13425 } 13426 return null; 13427 } 13428 13429 fn validateSwitchRange( 13430 sema: *Sema, 13431 block: *Block, 13432 range_set: *RangeSet, 13433 first_ref: Zir.Inst.Ref, 13434 last_ref: Zir.Inst.Ref, 13435 operand_ty: Type, 13436 item_src: LazySrcLoc, 13437 ) CompileError![2]Air.Inst.Ref { 13438 const first_src: LazySrcLoc = .{ 13439 .base_node_inst = item_src.base_node_inst, 13440 .offset = .{ .switch_case_item_range_first = item_src.offset.switch_case_item }, 13441 }; 13442 const last_src: LazySrcLoc = .{ 13443 .base_node_inst = item_src.base_node_inst, 13444 .offset = .{ .switch_case_item_range_last = item_src.offset.switch_case_item }, 13445 }; 13446 const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, first_src); 13447 const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, last_src); 13448 if (try Value.fromInterned(first.val).compareAll(.gt, Value.fromInterned(last.val), operand_ty, sema.pt)) { 13449 return sema.fail(block, item_src, "range start value is greater than the end value", .{}); 13450 } 13451 const maybe_prev_src = try range_set.add(first.val, last.val, item_src); 13452 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13453 return .{ first.ref, last.ref }; 13454 } 13455 13456 fn validateSwitchItemInt( 13457 sema: *Sema, 13458 block: *Block, 13459 range_set: *RangeSet, 13460 item_ref: Zir.Inst.Ref, 13461 operand_ty: Type, 13462 item_src: LazySrcLoc, 13463 ) CompileError!Air.Inst.Ref { 13464 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); 13465 const maybe_prev_src = try range_set.add(item.val, item.val, item_src); 13466 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13467 return item.ref; 13468 } 13469 13470 fn validateSwitchItemEnum( 13471 sema: *Sema, 13472 block: *Block, 13473 seen_fields: []?LazySrcLoc, 13474 range_set: *RangeSet, 13475 item_ref: Zir.Inst.Ref, 13476 operand_ty: Type, 13477 item_src: LazySrcLoc, 13478 ) CompileError!Air.Inst.Ref { 13479 const ip = &sema.pt.zcu.intern_pool; 13480 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); 13481 const int = ip.indexToKey(item.val).enum_tag.int; 13482 const field_index = ip.loadEnumType(ip.typeOf(item.val)).tagValueIndex(ip, int) orelse { 13483 const maybe_prev_src = try range_set.add(int, int, item_src); 13484 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13485 return item.ref; 13486 }; 13487 const maybe_prev_src = seen_fields[field_index]; 13488 seen_fields[field_index] = item_src; 13489 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13490 return item.ref; 13491 } 13492 13493 fn validateSwitchItemError( 13494 sema: *Sema, 13495 block: *Block, 13496 seen_errors: *SwitchErrorSet, 13497 item_ref: Zir.Inst.Ref, 13498 operand_ty: Type, 13499 item_src: LazySrcLoc, 13500 ) CompileError!Air.Inst.Ref { 13501 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); 13502 const error_name = sema.pt.zcu.intern_pool.indexToKey(item.val).err.name; 13503 const maybe_prev_src = if (try seen_errors.fetchPut(error_name, item_src)) |prev| 13504 prev.value 13505 else 13506 null; 13507 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13508 return item.ref; 13509 } 13510 13511 fn validateSwitchDupe( 13512 sema: *Sema, 13513 block: *Block, 13514 maybe_prev_src: ?LazySrcLoc, 13515 item_src: LazySrcLoc, 13516 ) CompileError!void { 13517 const prev_item_src = maybe_prev_src orelse return; 13518 return sema.failWithOwnedErrorMsg(block, msg: { 13519 const msg = try sema.errMsg( 13520 item_src, 13521 "duplicate switch value", 13522 .{}, 13523 ); 13524 errdefer msg.destroy(sema.gpa); 13525 try sema.errNote( 13526 prev_item_src, 13527 msg, 13528 "previous value here", 13529 .{}, 13530 ); 13531 break :msg msg; 13532 }); 13533 } 13534 13535 fn validateSwitchItemBool( 13536 sema: *Sema, 13537 block: *Block, 13538 true_count: *u8, 13539 false_count: *u8, 13540 item_ref: Zir.Inst.Ref, 13541 item_src: LazySrcLoc, 13542 ) CompileError!Air.Inst.Ref { 13543 const item = try sema.resolveSwitchItemVal(block, item_ref, .bool, item_src); 13544 if (Value.fromInterned(item.val).toBool()) { 13545 true_count.* += 1; 13546 } else { 13547 false_count.* += 1; 13548 } 13549 if (true_count.* > 1 or false_count.* > 1) { 13550 return sema.fail(block, item_src, "duplicate switch value", .{}); 13551 } 13552 return item.ref; 13553 } 13554 13555 const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc); 13556 13557 fn validateSwitchItemSparse( 13558 sema: *Sema, 13559 block: *Block, 13560 seen_values: *ValueSrcMap, 13561 item_ref: Zir.Inst.Ref, 13562 operand_ty: Type, 13563 item_src: LazySrcLoc, 13564 ) CompileError!Air.Inst.Ref { 13565 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); 13566 const kv = try seen_values.fetchPut(sema.gpa, item.val, item_src) orelse return item.ref; 13567 try sema.validateSwitchDupe(block, kv.value, item_src); 13568 unreachable; 13569 } 13570 13571 fn validateSwitchNoRange( 13572 sema: *Sema, 13573 block: *Block, 13574 ranges_len: u32, 13575 operand_ty: Type, 13576 src_node_offset: std.zig.Ast.Node.Offset, 13577 ) CompileError!void { 13578 if (ranges_len == 0) 13579 return; 13580 13581 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); 13582 const range_src = block.src(.{ .node_offset_switch_range = src_node_offset }); 13583 13584 const msg = msg: { 13585 const msg = try sema.errMsg( 13586 operand_src, 13587 "ranges not allowed when switching on type '{f}'", 13588 .{operand_ty.fmt(sema.pt)}, 13589 ); 13590 errdefer msg.destroy(sema.gpa); 13591 try sema.errNote( 13592 range_src, 13593 msg, 13594 "range here", 13595 .{}, 13596 ); 13597 break :msg msg; 13598 }; 13599 return sema.failWithOwnedErrorMsg(block, msg); 13600 } 13601 13602 fn maybeErrorUnwrap( 13603 sema: *Sema, 13604 block: *Block, 13605 body: []const Zir.Inst.Index, 13606 operand: Air.Inst.Ref, 13607 operand_src: LazySrcLoc, 13608 allow_err_code_inst: bool, 13609 ) !bool { 13610 const pt = sema.pt; 13611 const zcu = pt.zcu; 13612 13613 const tags = sema.code.instructions.items(.tag); 13614 for (body) |inst| { 13615 switch (tags[@intFromEnum(inst)]) { 13616 .@"unreachable" => if (!block.wantSafety()) return false, 13617 .err_union_code => if (!allow_err_code_inst) return false, 13618 .save_err_ret_index, 13619 .dbg_stmt, 13620 .str, 13621 .as_node, 13622 .panic, 13623 => {}, 13624 else => return false, 13625 } 13626 } 13627 13628 for (body) |inst| { 13629 const air_inst = switch (tags[@intFromEnum(inst)]) { 13630 .err_union_code => continue, 13631 .dbg_stmt => { 13632 try sema.zirDbgStmt(block, inst); 13633 continue; 13634 }, 13635 .save_err_ret_index => { 13636 try sema.zirSaveErrRetIndex(block, inst); 13637 continue; 13638 }, 13639 .str => try sema.zirStr(inst), 13640 .as_node => try sema.zirAsNode(block, inst), 13641 .@"unreachable" => { 13642 try safetyPanicUnwrapError(sema, block, operand_src, operand); 13643 return true; 13644 }, 13645 .panic => { 13646 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 13647 const msg_inst = try sema.resolveInst(inst_data.operand); 13648 13649 const panic_fn = try getBuiltin(sema, operand_src, .@"panic.call"); 13650 const args: [2]Air.Inst.Ref = .{ msg_inst, .null_value }; 13651 try sema.callBuiltin(block, operand_src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check"); 13652 return true; 13653 }, 13654 else => unreachable, 13655 }; 13656 if (sema.typeOf(air_inst).isNoReturn(zcu)) 13657 return true; 13658 sema.inst_map.putAssumeCapacity(inst, air_inst); 13659 } 13660 unreachable; 13661 } 13662 13663 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void { 13664 const pt = sema.pt; 13665 const zcu = pt.zcu; 13666 const index = cond.toIndex() orelse return; 13667 if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) return; 13668 13669 const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node; 13670 const err_operand = try sema.resolveInst(err_inst_data.operand); 13671 const operand_ty = sema.typeOf(err_operand); 13672 if (operand_ty.zigTypeTag(zcu) == .error_set) { 13673 try sema.maybeErrorUnwrapComptime(block, body, err_operand); 13674 return; 13675 } 13676 if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| { 13677 if (!operand_ty.isError(zcu)) return; 13678 if (val.getErrorName(zcu) == .none) return; 13679 try sema.maybeErrorUnwrapComptime(block, body, err_operand); 13680 } 13681 } 13682 13683 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void { 13684 const tags = sema.code.instructions.items(.tag); 13685 const inst = for (body) |inst| { 13686 switch (tags[@intFromEnum(inst)]) { 13687 .dbg_stmt, 13688 .save_err_ret_index, 13689 => {}, 13690 .@"unreachable" => break inst, 13691 else => return, 13692 } 13693 } else return; 13694 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable"; 13695 const src = block.nodeOffset(inst_data.src_node); 13696 13697 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 13698 if (val.getErrorName(sema.pt.zcu).unwrap()) |name| { 13699 return sema.failWithComptimeErrorRetTrace(block, src, name); 13700 } 13701 } 13702 } 13703 13704 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13705 const pt = sema.pt; 13706 const zcu = pt.zcu; 13707 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13708 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13709 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 13710 const name_src = block.builtinCallArgSrc(inst_data.src_node, 1); 13711 const ty = try sema.resolveType(block, ty_src, extra.lhs); 13712 const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name }); 13713 try ty.resolveFields(pt); 13714 const ip = &zcu.intern_pool; 13715 13716 const has_field = hf: { 13717 switch (ip.indexToKey(ty.toIntern())) { 13718 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 13719 .slice => { 13720 if (field_name.eqlSlice("ptr", ip)) break :hf true; 13721 if (field_name.eqlSlice("len", ip)) break :hf true; 13722 break :hf false; 13723 }, 13724 else => {}, 13725 }, 13726 .tuple_type => |tuple| { 13727 const field_index = field_name.toUnsigned(ip) orelse break :hf false; 13728 break :hf field_index < tuple.types.len; 13729 }, 13730 .struct_type => { 13731 break :hf ip.loadStructType(ty.toIntern()).nameIndex(ip, field_name) != null; 13732 }, 13733 .union_type => { 13734 const union_type = ip.loadUnionType(ty.toIntern()); 13735 break :hf union_type.loadTagType(ip).nameIndex(ip, field_name) != null; 13736 }, 13737 .enum_type => { 13738 break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null; 13739 }, 13740 .array_type => break :hf field_name.eqlSlice("len", ip), 13741 else => {}, 13742 } 13743 return sema.fail(block, ty_src, "type '{f}' does not support '@hasField'", .{ 13744 ty.fmt(pt), 13745 }); 13746 }; 13747 return if (has_field) .bool_true else .bool_false; 13748 } 13749 13750 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13751 const pt = sema.pt; 13752 const zcu = pt.zcu; 13753 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13754 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13755 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 13756 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 13757 const container_type = try sema.resolveType(block, lhs_src, extra.lhs); 13758 const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{ .simple = .decl_name }); 13759 13760 try sema.checkNamespaceType(block, lhs_src, container_type); 13761 13762 const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false; 13763 if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| { 13764 if (lookup.accessible) { 13765 return .bool_true; 13766 } 13767 } 13768 return .bool_false; 13769 } 13770 13771 fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13772 const tracy = trace(@src()); 13773 defer tracy.end(); 13774 13775 const pt = sema.pt; 13776 const zcu = pt.zcu; 13777 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; 13778 const extra = sema.code.extraData(Zir.Inst.Import, inst_data.payload_index).data; 13779 const operand_src = block.tokenOffset(inst_data.src_tok); 13780 const operand = sema.code.nullTerminatedString(extra.path); 13781 13782 const result = pt.doImport(block.getFileScope(zcu), operand) catch |err| switch (err) { 13783 error.ModuleNotFound => return sema.fail(block, operand_src, "no module named '{s}' available within module '{s}'", .{ 13784 operand, block.getFileScope(zcu).mod.?.fully_qualified_name, 13785 }), 13786 error.IllegalZigImport => unreachable, // caught before semantic analysis 13787 error.OutOfMemory => |e| return e, 13788 }; 13789 const file_index = result.file; 13790 const file = zcu.fileByIndex(file_index); 13791 switch (file.getMode()) { 13792 .zig => { 13793 try pt.ensureFileAnalyzed(file_index); 13794 const ty = zcu.fileRootType(file_index); 13795 try sema.declareDependency(.{ .interned = ty }); 13796 try sema.addTypeReferenceEntry(operand_src, ty); 13797 return Air.internedToRef(ty); 13798 }, 13799 .zon => { 13800 const res_ty: InternPool.Index = b: { 13801 if (extra.res_ty == .none) break :b .none; 13802 const res_ty_inst = try sema.resolveInst(extra.res_ty); 13803 const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst); 13804 if (res_ty.isGenericPoison()) break :b .none; 13805 break :b res_ty.toIntern(); 13806 }; 13807 13808 try sema.declareDependency(.{ .zon_file = file_index }); 13809 const interned = try LowerZon.run( 13810 sema, 13811 file, 13812 file_index, 13813 res_ty, 13814 operand_src, 13815 block, 13816 ); 13817 return Air.internedToRef(interned); 13818 }, 13819 } 13820 } 13821 13822 fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13823 const tracy = trace(@src()); 13824 defer tracy.end(); 13825 13826 const pt = sema.pt; 13827 const zcu = pt.zcu; 13828 13829 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 13830 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 13831 const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .operand_embedFile }); 13832 13833 if (name.len == 0) { 13834 return sema.fail(block, operand_src, "file path name cannot be empty", .{}); 13835 } 13836 13837 const ef_idx = pt.embedFile(block.getFileScope(zcu), name) catch |err| switch (err) { 13838 error.ImportOutsideModulePath => { 13839 return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name}); 13840 }, 13841 error.CurrentWorkingDirectoryUnlinked => { 13842 // TODO: this should be some kind of retryable failure, in case the cwd is put back 13843 return sema.fail(block, operand_src, "unable to resolve '{s}': working directory has been unlinked", .{name}); 13844 }, 13845 error.OutOfMemory => |e| return e, 13846 }; 13847 try sema.declareDependency(.{ .embed_file = ef_idx }); 13848 13849 const result = ef_idx.get(zcu); 13850 if (result.val == .none) { 13851 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(result.err.?) }); 13852 } 13853 13854 return Air.internedToRef(result.val); 13855 } 13856 13857 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13858 const pt = sema.pt; 13859 const zcu = pt.zcu; 13860 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 13861 const name = try zcu.intern_pool.getOrPutString( 13862 sema.gpa, 13863 pt.tid, 13864 inst_data.get(sema.code), 13865 .no_embedded_nulls, 13866 ); 13867 _ = try pt.getErrorValue(name); 13868 const error_set_type = try pt.singleErrorSetType(name); 13869 return Air.internedToRef((try pt.intern(.{ .err = .{ 13870 .ty = error_set_type.toIntern(), 13871 .name = name, 13872 } }))); 13873 } 13874 13875 fn zirShl( 13876 sema: *Sema, 13877 block: *Block, 13878 inst: Zir.Inst.Index, 13879 air_tag: Air.Inst.Tag, 13880 ) CompileError!Air.Inst.Ref { 13881 const tracy = trace(@src()); 13882 defer tracy.end(); 13883 13884 const pt = sema.pt; 13885 const zcu = pt.zcu; 13886 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13887 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13888 const lhs = try sema.resolveInst(extra.lhs); 13889 const rhs = try sema.resolveInst(extra.rhs); 13890 const lhs_ty = sema.typeOf(lhs); 13891 const rhs_ty = sema.typeOf(rhs); 13892 13893 const src = block.nodeOffset(inst_data.src_node); 13894 const lhs_src, const rhs_src = switch (air_tag) { 13895 .shl, .shl_sat => .{ 13896 block.src(.{ .node_offset_bin_lhs = inst_data.src_node }), 13897 block.src(.{ .node_offset_bin_rhs = inst_data.src_node }), 13898 }, 13899 .shl_exact => .{ 13900 block.builtinCallArgSrc(inst_data.src_node, 0), 13901 block.builtinCallArgSrc(inst_data.src_node, 1), 13902 }, 13903 else => unreachable, 13904 }; 13905 13906 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13907 13908 const scalar_ty = lhs_ty.scalarType(zcu); 13909 const scalar_rhs_ty = rhs_ty.scalarType(zcu); 13910 13911 // AstGen currently forces the rhs of `<<` to coerce to the correct type before the `.shl` instruction, so 13912 // we already know `scalar_rhs_ty` is valid for `.shl` -- we only need to validate for `.shl_sat`. 13913 if (air_tag == .shl_sat) _ = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); 13914 13915 const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs); 13916 const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs); 13917 13918 const runtime_src = rs: { 13919 if (maybe_rhs_val) |rhs_val| { 13920 if (maybe_lhs_val) |lhs_val| { 13921 return .fromValue(try arith.shl(sema, block, lhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, switch (air_tag) { 13922 .shl => .shl, 13923 .shl_sat => .shl_sat, 13924 .shl_exact => .shl_exact, 13925 else => unreachable, 13926 })); 13927 } 13928 if (rhs_val.isUndef(zcu)) switch (air_tag) { 13929 .shl_sat => return pt.undefRef(lhs_ty), 13930 .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, null), 13931 else => unreachable, 13932 }; 13933 const bits = scalar_ty.intInfo(zcu).bits; 13934 switch (rhs_ty.zigTypeTag(zcu)) { 13935 .int, .comptime_int => { 13936 switch (try rhs_val.orderAgainstZeroSema(pt)) { 13937 .gt => { 13938 if (air_tag != .shl_sat) { 13939 var rhs_space: Value.BigIntSpace = undefined; 13940 const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt); 13941 if (rhs_bigint.orderAgainstScalar(bits) != .lt) { 13942 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); 13943 } 13944 } 13945 }, 13946 .eq => return lhs, 13947 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null), 13948 } 13949 }, 13950 .vector => { 13951 var any_positive: bool = false; 13952 for (0..rhs_ty.vectorLen(zcu)) |elem_idx| { 13953 const rhs_elem = try rhs_val.elemValue(pt, elem_idx); 13954 if (rhs_elem.isUndef(zcu)) switch (air_tag) { 13955 .shl_sat => continue, 13956 .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, elem_idx), 13957 else => unreachable, 13958 }; 13959 switch (try rhs_elem.orderAgainstZeroSema(pt)) { 13960 .gt => { 13961 if (air_tag != .shl_sat) { 13962 var rhs_elem_space: Value.BigIntSpace = undefined; 13963 const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt); 13964 if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { 13965 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); 13966 } 13967 } 13968 any_positive = true; 13969 }, 13970 .eq => {}, 13971 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx), 13972 } 13973 } 13974 if (!any_positive) return lhs; 13975 }, 13976 else => unreachable, 13977 } 13978 break :rs lhs_src; 13979 } else { 13980 if (air_tag == .shl_sat and scalar_rhs_ty.isSignedInt(zcu)) { 13981 return sema.fail(block, rhs_src, "shift by signed type '{f}'", .{rhs_ty.fmt(pt)}); 13982 } 13983 if (scalar_ty.toIntern() == .comptime_int_type) { 13984 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 13985 } 13986 if (maybe_lhs_val) |lhs_val| { 13987 switch (air_tag) { 13988 .shl_sat => if (lhs_val.isUndef(zcu)) return pt.undefRef(lhs_ty), 13989 .shl, .shl_exact => try sema.checkAllScalarsDefined(block, lhs_src, lhs_val), 13990 else => unreachable, 13991 } 13992 if (try lhs_val.compareAllWithZeroSema(.eq, pt)) return lhs; 13993 } 13994 } 13995 break :rs rhs_src; 13996 }; 13997 const rt_rhs: Air.Inst.Ref = switch (air_tag) { 13998 else => unreachable, 13999 .shl, .shl_exact => rhs, 14000 // The backend can handle a large runtime rhs better than we can, but 14001 // we can limit a large comptime rhs better here. This also has the 14002 // necessary side effect of preventing rhs from being a `comptime_int`. 14003 .shl_sat => if (maybe_rhs_val) |rhs_val| .fromValue(rt_rhs: { 14004 const bit_count = scalar_ty.intInfo(zcu).bits; 14005 const rt_rhs_scalar_ty = try pt.smallestUnsignedInt(bit_count); 14006 if (!rhs_ty.isVector(zcu)) break :rt_rhs try pt.intValue( 14007 rt_rhs_scalar_ty, 14008 @min(try rhs_val.getUnsignedIntSema(pt) orelse bit_count, bit_count), 14009 ); 14010 const rhs_len = rhs_ty.vectorLen(zcu); 14011 const rhs_elems = try sema.arena.alloc(InternPool.Index, rhs_len); 14012 for (rhs_elems, 0..) |*rhs_elem, i| rhs_elem.* = (try pt.intValue( 14013 rt_rhs_scalar_ty, 14014 @min(try (try rhs_val.elemValue(pt, i)).getUnsignedIntSema(pt) orelse bit_count, bit_count), 14015 )).toIntern(); 14016 break :rt_rhs try pt.aggregateValue(try pt.vectorType(.{ 14017 .len = rhs_len, 14018 .child = rt_rhs_scalar_ty.toIntern(), 14019 }), rhs_elems); 14020 }) else rhs, 14021 }; 14022 14023 try sema.requireRuntimeBlock(block, src, runtime_src); 14024 if (block.wantSafety()) { 14025 const bit_count = scalar_ty.intInfo(zcu).bits; 14026 if (air_tag != .shl_sat and !std.math.isPowerOfTwo(bit_count)) { 14027 const bit_count_val = try pt.intValue(scalar_rhs_ty, bit_count); 14028 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { 14029 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern()); 14030 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); 14031 break :ok try block.addReduce(lt, .And); 14032 } else ok: { 14033 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern()); 14034 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); 14035 }; 14036 try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big); 14037 } 14038 14039 if (air_tag == .shl_exact) { 14040 const op_ov_tuple_ty = try pt.overflowArithmeticTupleType(lhs_ty); 14041 const op_ov = try block.addInst(.{ 14042 .tag = .shl_with_overflow, 14043 .data = .{ .ty_pl = .{ 14044 .ty = .fromIntern(op_ov_tuple_ty.toIntern()), 14045 .payload = try sema.addExtra(Air.Bin{ 14046 .lhs = lhs, 14047 .rhs = rhs, 14048 }), 14049 } }, 14050 }); 14051 const ov_bit = try sema.tupleFieldValByIndex(block, op_ov, 1, op_ov_tuple_ty); 14052 const any_ov_bit = if (lhs_ty.zigTypeTag(zcu) == .vector) 14053 try block.addReduce(ov_bit, .Or) 14054 else 14055 ov_bit; 14056 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, .zero_u1); 14057 14058 try sema.addSafetyCheck(block, src, no_ov, .shl_overflow); 14059 return sema.tupleFieldValByIndex(block, op_ov, 0, op_ov_tuple_ty); 14060 } 14061 } 14062 return block.addBinOp(air_tag, lhs, rt_rhs); 14063 } 14064 14065 fn zirShr( 14066 sema: *Sema, 14067 block: *Block, 14068 inst: Zir.Inst.Index, 14069 air_tag: Air.Inst.Tag, 14070 ) CompileError!Air.Inst.Ref { 14071 const tracy = trace(@src()); 14072 defer tracy.end(); 14073 14074 const pt = sema.pt; 14075 const zcu = pt.zcu; 14076 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14077 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14078 const lhs = try sema.resolveInst(extra.lhs); 14079 const rhs = try sema.resolveInst(extra.rhs); 14080 const lhs_ty = sema.typeOf(lhs); 14081 const rhs_ty = sema.typeOf(rhs); 14082 14083 const src = block.nodeOffset(inst_data.src_node); 14084 const lhs_src = switch (air_tag) { 14085 .shr => block.src(.{ .node_offset_bin_lhs = inst_data.src_node }), 14086 .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 0), 14087 else => unreachable, 14088 }; 14089 const rhs_src = switch (air_tag) { 14090 .shr => block.src(.{ .node_offset_bin_rhs = inst_data.src_node }), 14091 .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 1), 14092 else => unreachable, 14093 }; 14094 14095 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14096 const scalar_ty = lhs_ty.scalarType(zcu); 14097 14098 const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs); 14099 const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs); 14100 14101 const runtime_src = rs: { 14102 if (maybe_rhs_val) |rhs_val| { 14103 if (maybe_lhs_val) |lhs_val| { 14104 return .fromValue(try arith.shr(sema, block, lhs_ty, rhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, switch (air_tag) { 14105 .shr => .shr, 14106 .shr_exact => .shr_exact, 14107 else => unreachable, 14108 })); 14109 } 14110 if (rhs_val.isUndef(zcu)) { 14111 return sema.failWithUseOfUndef(block, rhs_src, null); 14112 } 14113 const bits = scalar_ty.intInfo(zcu).bits; 14114 switch (rhs_ty.zigTypeTag(zcu)) { 14115 .int, .comptime_int => { 14116 switch (try rhs_val.orderAgainstZeroSema(pt)) { 14117 .gt => { 14118 var rhs_space: Value.BigIntSpace = undefined; 14119 const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt); 14120 if (rhs_bigint.orderAgainstScalar(bits) != .lt) { 14121 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); 14122 } 14123 }, 14124 .eq => return lhs, 14125 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null), 14126 } 14127 }, 14128 .vector => { 14129 var any_positive: bool = false; 14130 for (0..rhs_ty.vectorLen(zcu)) |elem_idx| { 14131 const rhs_elem = try rhs_val.elemValue(pt, elem_idx); 14132 if (rhs_elem.isUndef(zcu)) { 14133 return sema.failWithUseOfUndef(block, rhs_src, elem_idx); 14134 } 14135 switch (try rhs_elem.orderAgainstZeroSema(pt)) { 14136 .gt => { 14137 var rhs_elem_space: Value.BigIntSpace = undefined; 14138 const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt); 14139 if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { 14140 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); 14141 } 14142 any_positive = true; 14143 }, 14144 .eq => {}, 14145 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx), 14146 } 14147 } 14148 if (!any_positive) return lhs; 14149 }, 14150 else => unreachable, 14151 } 14152 break :rs lhs_src; 14153 } else { 14154 if (scalar_ty.toIntern() == .comptime_int_type) { 14155 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 14156 } 14157 if (maybe_lhs_val) |lhs_val| { 14158 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 14159 if (try lhs_val.compareAllWithZeroSema(.eq, pt)) return lhs; 14160 } 14161 } 14162 break :rs rhs_src; 14163 }; 14164 try sema.requireRuntimeBlock(block, src, runtime_src); 14165 const result = try block.addBinOp(air_tag, lhs, rhs); 14166 if (block.wantSafety()) { 14167 const bit_count = scalar_ty.intInfo(zcu).bits; 14168 if (!std.math.isPowerOfTwo(bit_count)) { 14169 const bit_count_val = try pt.intValue(rhs_ty.scalarType(zcu), bit_count); 14170 14171 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { 14172 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern()); 14173 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); 14174 break :ok try block.addReduce(lt, .And); 14175 } else ok: { 14176 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern()); 14177 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); 14178 }; 14179 try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big); 14180 } 14181 14182 if (air_tag == .shr_exact) { 14183 const back = try block.addBinOp(.shl, result, rhs); 14184 14185 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { 14186 const eql = try block.addCmpVector(lhs, back, .eq); 14187 break :ok try block.addReduce(eql, .And); 14188 } else try block.addBinOp(.cmp_eq, lhs, back); 14189 try sema.addSafetyCheck(block, src, ok, .shr_overflow); 14190 } 14191 } 14192 return result; 14193 } 14194 14195 fn zirBitwise( 14196 sema: *Sema, 14197 block: *Block, 14198 inst: Zir.Inst.Index, 14199 air_tag: Air.Inst.Tag, 14200 ) CompileError!Air.Inst.Ref { 14201 const tracy = trace(@src()); 14202 defer tracy.end(); 14203 14204 const pt = sema.pt; 14205 const zcu = pt.zcu; 14206 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14207 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 14208 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 14209 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 14210 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14211 const lhs = try sema.resolveInst(extra.lhs); 14212 const rhs = try sema.resolveInst(extra.rhs); 14213 const lhs_ty = sema.typeOf(lhs); 14214 const rhs_ty = sema.typeOf(rhs); 14215 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14216 14217 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14218 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); 14219 const scalar_type = resolved_type.scalarType(zcu); 14220 const scalar_tag = scalar_type.zigTypeTag(zcu); 14221 14222 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14223 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14224 14225 const is_int_or_bool = scalar_tag == .int or scalar_tag == .comptime_int or scalar_tag == .bool; 14226 14227 if (!is_int_or_bool) { 14228 return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(zcu)), @tagName(rhs_ty.zigTypeTag(zcu)) }); 14229 } 14230 14231 const runtime_src = runtime: { 14232 // TODO: ask the linker what kind of relocations are available, and 14233 // in some cases emit a Value that means "this decl's address AND'd with this operand". 14234 if (try sema.resolveValueResolveLazy(casted_lhs)) |lhs_val| { 14235 if (try sema.resolveValueResolveLazy(casted_rhs)) |rhs_val| { 14236 const result_val = switch (air_tag) { 14237 // zig fmt: off 14238 .bit_and => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"and"), 14239 .bit_or => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"or"), 14240 .xor => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .xor), 14241 else => unreachable, 14242 // zig fmt: on 14243 }; 14244 return Air.internedToRef(result_val.toIntern()); 14245 } else { 14246 break :runtime rhs_src; 14247 } 14248 } else { 14249 break :runtime lhs_src; 14250 } 14251 }; 14252 14253 try sema.requireRuntimeBlock(block, src, runtime_src); 14254 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 14255 } 14256 14257 fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14258 const pt = sema.pt; 14259 const zcu = pt.zcu; 14260 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 14261 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 14262 const src = block.nodeOffset(inst_data.src_node); 14263 const operand = try sema.resolveInst(inst_data.operand); 14264 const operand_ty = sema.typeOf(operand); 14265 const scalar_ty = operand_ty.scalarType(zcu); 14266 const scalar_tag = scalar_ty.zigTypeTag(zcu); 14267 14268 if (scalar_tag != .int and scalar_tag != .bool) 14269 return sema.fail(block, operand_src, "bitwise not operation on type '{f}'", .{operand_ty.fmt(pt)}); 14270 14271 return analyzeBitNot(sema, block, operand, src); 14272 } 14273 14274 fn analyzeBitNot( 14275 sema: *Sema, 14276 block: *Block, 14277 operand: Air.Inst.Ref, 14278 src: LazySrcLoc, 14279 ) CompileError!Air.Inst.Ref { 14280 const operand_ty = sema.typeOf(operand); 14281 if (try sema.resolveValue(operand)) |operand_val| { 14282 const result_val = try arith.bitwiseNot(sema, operand_ty, operand_val); 14283 return Air.internedToRef(result_val.toIntern()); 14284 } 14285 try sema.requireRuntimeBlock(block, src, null); 14286 return block.addTyOp(.not, operand_ty, operand); 14287 } 14288 14289 fn analyzeTupleCat( 14290 sema: *Sema, 14291 block: *Block, 14292 src_node: std.zig.Ast.Node.Offset, 14293 lhs: Air.Inst.Ref, 14294 rhs: Air.Inst.Ref, 14295 ) CompileError!Air.Inst.Ref { 14296 const pt = sema.pt; 14297 const zcu = pt.zcu; 14298 const lhs_ty = sema.typeOf(lhs); 14299 const rhs_ty = sema.typeOf(rhs); 14300 const src = block.nodeOffset(src_node); 14301 14302 const lhs_len = lhs_ty.structFieldCount(zcu); 14303 const rhs_len = rhs_ty.structFieldCount(zcu); 14304 const dest_fields = lhs_len + rhs_len; 14305 14306 if (dest_fields == 0) { 14307 return .empty_tuple; 14308 } 14309 if (lhs_len == 0) { 14310 return rhs; 14311 } 14312 if (rhs_len == 0) { 14313 return lhs; 14314 } 14315 const final_len = try sema.usizeCast(block, src, dest_fields); 14316 14317 const types = try sema.arena.alloc(InternPool.Index, final_len); 14318 const values = try sema.arena.alloc(InternPool.Index, final_len); 14319 14320 const opt_runtime_src = rs: { 14321 var runtime_src: ?LazySrcLoc = null; 14322 var i: u32 = 0; 14323 while (i < lhs_len) : (i += 1) { 14324 types[i] = lhs_ty.fieldType(i, zcu).toIntern(); 14325 const default_val = lhs_ty.structFieldDefaultValue(i, zcu); 14326 values[i] = default_val.toIntern(); 14327 const operand_src = block.src(.{ .array_cat_lhs = .{ 14328 .array_cat_offset = src_node, 14329 .elem_index = i, 14330 } }); 14331 if (default_val.toIntern() == .unreachable_value) { 14332 runtime_src = operand_src; 14333 values[i] = .none; 14334 } 14335 } 14336 i = 0; 14337 while (i < rhs_len) : (i += 1) { 14338 types[i + lhs_len] = rhs_ty.fieldType(i, zcu).toIntern(); 14339 const default_val = rhs_ty.structFieldDefaultValue(i, zcu); 14340 values[i + lhs_len] = default_val.toIntern(); 14341 const operand_src = block.src(.{ .array_cat_rhs = .{ 14342 .array_cat_offset = src_node, 14343 .elem_index = i, 14344 } }); 14345 if (default_val.toIntern() == .unreachable_value) { 14346 runtime_src = operand_src; 14347 values[i + lhs_len] = .none; 14348 } 14349 } 14350 break :rs runtime_src; 14351 }; 14352 14353 const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{ 14354 .types = types, 14355 .values = values, 14356 })); 14357 14358 const runtime_src = opt_runtime_src orelse { 14359 const tuple_val = try pt.aggregateValue(tuple_ty, values); 14360 return Air.internedToRef(tuple_val.toIntern()); 14361 }; 14362 14363 try sema.requireRuntimeBlock(block, src, runtime_src); 14364 14365 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); 14366 var i: u32 = 0; 14367 while (i < lhs_len) : (i += 1) { 14368 element_refs[i] = try sema.tupleFieldValByIndex(block, lhs, i, lhs_ty); 14369 } 14370 i = 0; 14371 while (i < rhs_len) : (i += 1) { 14372 element_refs[i + lhs_len] = 14373 try sema.tupleFieldValByIndex(block, rhs, i, rhs_ty); 14374 } 14375 14376 return block.addAggregateInit(tuple_ty, element_refs); 14377 } 14378 14379 fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14380 const tracy = trace(@src()); 14381 defer tracy.end(); 14382 14383 const pt = sema.pt; 14384 const zcu = pt.zcu; 14385 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14386 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14387 const lhs = try sema.resolveInst(extra.lhs); 14388 const rhs = try sema.resolveInst(extra.rhs); 14389 const lhs_ty = sema.typeOf(lhs); 14390 const rhs_ty = sema.typeOf(rhs); 14391 const src = block.nodeOffset(inst_data.src_node); 14392 14393 const lhs_is_tuple = lhs_ty.isTuple(zcu); 14394 const rhs_is_tuple = rhs_ty.isTuple(zcu); 14395 if (lhs_is_tuple and rhs_is_tuple) { 14396 return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); 14397 } 14398 14399 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 14400 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 14401 14402 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: { 14403 if (lhs_is_tuple) break :lhs_info undefined; 14404 return sema.fail(block, lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)}); 14405 }; 14406 const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse { 14407 assert(!rhs_is_tuple); 14408 return sema.fail(block, rhs_src, "expected indexable; found '{f}'", .{rhs_ty.fmt(pt)}); 14409 }; 14410 14411 const resolved_elem_ty = t: { 14412 var trash_block = block.makeSubBlock(); 14413 trash_block.comptime_reason = null; 14414 defer trash_block.instructions.deinit(sema.gpa); 14415 14416 const instructions = [_]Air.Inst.Ref{ 14417 try trash_block.addBitCast(lhs_info.elem_type, .void_value), 14418 try trash_block.addBitCast(rhs_info.elem_type, .void_value), 14419 }; 14420 break :t try sema.resolvePeerTypes(block, src, &instructions, .{ 14421 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14422 }); 14423 }; 14424 14425 // When there is a sentinel mismatch, no sentinel on the result. 14426 // Otherwise, use the sentinel value provided by either operand, 14427 // coercing it to the peer-resolved element type. 14428 const res_sent_val: ?Value = s: { 14429 if (lhs_info.sentinel) |lhs_sent_val| { 14430 const lhs_sent = Air.internedToRef(lhs_sent_val.toIntern()); 14431 if (rhs_info.sentinel) |rhs_sent_val| { 14432 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern()); 14433 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); 14434 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); 14435 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?; 14436 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?; 14437 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { 14438 break :s lhs_sent_casted_val; 14439 } else { 14440 break :s null; 14441 } 14442 } else { 14443 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); 14444 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?; 14445 break :s lhs_sent_casted_val; 14446 } 14447 } else { 14448 if (rhs_info.sentinel) |rhs_sent_val| { 14449 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern()); 14450 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); 14451 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?; 14452 break :s rhs_sent_casted_val; 14453 } else { 14454 break :s null; 14455 } 14456 } 14457 }; 14458 14459 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); 14460 const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len); 14461 const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) { 14462 error.Overflow => return sema.fail( 14463 block, 14464 src, 14465 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle", 14466 .{ lhs_len, rhs_len }, 14467 ), 14468 }; 14469 14470 const result_ty = try pt.arrayType(.{ 14471 .len = result_len, 14472 .sentinel = if (res_sent_val) |v| v.toIntern() else .none, 14473 .child = resolved_elem_ty.toIntern(), 14474 }); 14475 const ptr_addrspace = p: { 14476 if (lhs_ty.zigTypeTag(zcu) == .pointer) break :p lhs_ty.ptrAddressSpace(zcu); 14477 if (rhs_ty.zigTypeTag(zcu) == .pointer) break :p rhs_ty.ptrAddressSpace(zcu); 14478 break :p null; 14479 }; 14480 14481 const runtime_src = if (switch (lhs_ty.zigTypeTag(zcu)) { 14482 .array, .@"struct" => try sema.resolveValue(lhs), 14483 .pointer => try sema.resolveDefinedValue(block, lhs_src, lhs), 14484 else => unreachable, 14485 }) |lhs_val| rs: { 14486 if (switch (rhs_ty.zigTypeTag(zcu)) { 14487 .array, .@"struct" => try sema.resolveValue(rhs), 14488 .pointer => try sema.resolveDefinedValue(block, rhs_src, rhs), 14489 else => unreachable, 14490 }) |rhs_val| { 14491 const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu)) 14492 try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :rs lhs_src 14493 else if (lhs_ty.isSlice(zcu)) 14494 try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :rs lhs_src 14495 else 14496 lhs_val; 14497 14498 const rhs_sub_val = if (rhs_ty.isSinglePointer(zcu)) 14499 try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty) orelse break :rs rhs_src 14500 else if (rhs_ty.isSlice(zcu)) 14501 try sema.maybeDerefSliceAsArray(block, rhs_src, rhs_val) orelse break :rs rhs_src 14502 else 14503 rhs_val; 14504 14505 const element_vals = try sema.arena.alloc(InternPool.Index, result_len); 14506 var elem_i: u32 = 0; 14507 while (elem_i < lhs_len) : (elem_i += 1) { 14508 const lhs_elem_i = elem_i; 14509 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else Value.@"unreachable"; 14510 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(pt, lhs_elem_i) else elem_default_val; 14511 const elem_val_inst = Air.internedToRef(elem_val.toIntern()); 14512 const operand_src = block.src(.{ .array_cat_lhs = .{ 14513 .array_cat_offset = inst_data.src_node, 14514 .elem_index = elem_i, 14515 } }); 14516 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src); 14517 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined); 14518 element_vals[elem_i] = coerced_elem_val.toIntern(); 14519 } 14520 while (elem_i < result_len) : (elem_i += 1) { 14521 const rhs_elem_i = elem_i - lhs_len; 14522 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else Value.@"unreachable"; 14523 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(pt, rhs_elem_i) else elem_default_val; 14524 const elem_val_inst = Air.internedToRef(elem_val.toIntern()); 14525 const operand_src = block.src(.{ .array_cat_rhs = .{ 14526 .array_cat_offset = inst_data.src_node, 14527 .elem_index = @intCast(rhs_elem_i), 14528 } }); 14529 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src); 14530 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined); 14531 element_vals[elem_i] = coerced_elem_val.toIntern(); 14532 } 14533 return sema.addConstantMaybeRef( 14534 (try pt.aggregateValue(result_ty, element_vals)).toIntern(), 14535 ptr_addrspace != null, 14536 ); 14537 } else break :rs rhs_src; 14538 } else lhs_src; 14539 14540 try sema.requireRuntimeBlock(block, src, runtime_src); 14541 14542 if (ptr_addrspace) |ptr_as| { 14543 const constant_alloc_ty = try pt.ptrTypeSema(.{ 14544 .child = result_ty.toIntern(), 14545 .flags = .{ 14546 .address_space = ptr_as, 14547 .is_const = true, 14548 }, 14549 }); 14550 const alloc_ty = try pt.ptrTypeSema(.{ 14551 .child = result_ty.toIntern(), 14552 .flags = .{ .address_space = ptr_as }, 14553 }); 14554 const elem_ptr_ty = try pt.ptrTypeSema(.{ 14555 .child = resolved_elem_ty.toIntern(), 14556 .flags = .{ .address_space = ptr_as }, 14557 }); 14558 14559 const mutable_alloc = try block.addTy(.alloc, alloc_ty); 14560 14561 // if both the source and destination are arrays 14562 // we can hotpath via a memcpy. 14563 if (lhs_ty.zigTypeTag(zcu) == .pointer and 14564 rhs_ty.zigTypeTag(zcu) == .pointer) 14565 { 14566 const slice_ty = try pt.ptrTypeSema(.{ 14567 .child = resolved_elem_ty.toIntern(), 14568 .flags = .{ 14569 .size = .slice, 14570 .address_space = ptr_as, 14571 }, 14572 }); 14573 14574 const many_ty = slice_ty.slicePtrFieldType(zcu); 14575 const many_alloc = try block.addBitCast(many_ty, mutable_alloc); 14576 14577 // lhs_dest_slice = dest[0..lhs.len] 14578 const slice_ty_ref = Air.internedToRef(slice_ty.toIntern()); 14579 const lhs_len_ref = try pt.intRef(.usize, lhs_len); 14580 const lhs_dest_slice = try block.addInst(.{ 14581 .tag = .slice, 14582 .data = .{ .ty_pl = .{ 14583 .ty = slice_ty_ref, 14584 .payload = try sema.addExtra(Air.Bin{ 14585 .lhs = many_alloc, 14586 .rhs = lhs_len_ref, 14587 }), 14588 } }, 14589 }); 14590 14591 _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs); 14592 14593 // rhs_dest_slice = dest[lhs.len..][0..rhs.len] 14594 const rhs_len_ref = try pt.intRef(.usize, rhs_len); 14595 const rhs_dest_offset = try block.addInst(.{ 14596 .tag = .ptr_add, 14597 .data = .{ .ty_pl = .{ 14598 .ty = Air.internedToRef(many_ty.toIntern()), 14599 .payload = try sema.addExtra(Air.Bin{ 14600 .lhs = many_alloc, 14601 .rhs = lhs_len_ref, 14602 }), 14603 } }, 14604 }); 14605 const rhs_dest_slice = try block.addInst(.{ 14606 .tag = .slice, 14607 .data = .{ .ty_pl = .{ 14608 .ty = slice_ty_ref, 14609 .payload = try sema.addExtra(Air.Bin{ 14610 .lhs = rhs_dest_offset, 14611 .rhs = rhs_len_ref, 14612 }), 14613 } }, 14614 }); 14615 14616 _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs); 14617 14618 if (res_sent_val) |sent_val| { 14619 const elem_index = try pt.intRef(.usize, result_len); 14620 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); 14621 const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern()); 14622 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 14623 } 14624 14625 return block.addBitCast(constant_alloc_ty, mutable_alloc); 14626 } 14627 14628 var elem_i: u32 = 0; 14629 while (elem_i < lhs_len) : (elem_i += 1) { 14630 const elem_index = try pt.intRef(.usize, elem_i); 14631 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); 14632 const operand_src = block.src(.{ .array_cat_lhs = .{ 14633 .array_cat_offset = inst_data.src_node, 14634 .elem_index = elem_i, 14635 } }); 14636 const init = try sema.elemVal(block, operand_src, lhs, elem_index, src, true); 14637 try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store); 14638 } 14639 while (elem_i < result_len) : (elem_i += 1) { 14640 const rhs_elem_i = elem_i - lhs_len; 14641 const elem_index = try pt.intRef(.usize, elem_i); 14642 const rhs_index = try pt.intRef(.usize, rhs_elem_i); 14643 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); 14644 const operand_src = block.src(.{ .array_cat_rhs = .{ 14645 .array_cat_offset = inst_data.src_node, 14646 .elem_index = @intCast(rhs_elem_i), 14647 } }); 14648 const init = try sema.elemVal(block, operand_src, rhs, rhs_index, src, true); 14649 try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store); 14650 } 14651 if (res_sent_val) |sent_val| { 14652 const elem_index = try pt.intRef(.usize, result_len); 14653 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); 14654 const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern()); 14655 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 14656 } 14657 14658 return block.addBitCast(constant_alloc_ty, mutable_alloc); 14659 } 14660 14661 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); 14662 { 14663 var elem_i: u32 = 0; 14664 while (elem_i < lhs_len) : (elem_i += 1) { 14665 const index = try pt.intRef(.usize, elem_i); 14666 const operand_src = block.src(.{ .array_cat_lhs = .{ 14667 .array_cat_offset = inst_data.src_node, 14668 .elem_index = elem_i, 14669 } }); 14670 const init = try sema.elemVal(block, operand_src, lhs, index, src, true); 14671 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src); 14672 } 14673 while (elem_i < result_len) : (elem_i += 1) { 14674 const rhs_elem_i = elem_i - lhs_len; 14675 const index = try pt.intRef(.usize, rhs_elem_i); 14676 const operand_src = block.src(.{ .array_cat_rhs = .{ 14677 .array_cat_offset = inst_data.src_node, 14678 .elem_index = @intCast(rhs_elem_i), 14679 } }); 14680 const init = try sema.elemVal(block, operand_src, rhs, index, src, true); 14681 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src); 14682 } 14683 } 14684 14685 return block.addAggregateInit(result_ty, element_refs); 14686 } 14687 14688 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo { 14689 const pt = sema.pt; 14690 const zcu = pt.zcu; 14691 const operand_ty = sema.typeOf(operand); 14692 switch (operand_ty.zigTypeTag(zcu)) { 14693 .array => return operand_ty.arrayInfo(zcu), 14694 .pointer => { 14695 const ptr_info = operand_ty.ptrInfo(zcu); 14696 switch (ptr_info.flags.size) { 14697 .slice => { 14698 const val = try sema.resolveConstDefinedValue(block, src, operand, .{ .simple = .slice_cat_operand }); 14699 return .{ 14700 .elem_type = .fromInterned(ptr_info.child), 14701 .sentinel = switch (ptr_info.sentinel) { 14702 .none => null, 14703 else => Value.fromInterned(ptr_info.sentinel), 14704 }, 14705 .len = try val.sliceLen(pt), 14706 }; 14707 }, 14708 .one => { 14709 if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { 14710 return Type.fromInterned(ptr_info.child).arrayInfo(zcu); 14711 } 14712 }, 14713 .c, .many => {}, 14714 } 14715 }, 14716 .@"struct" => { 14717 if (operand_ty.isTuple(zcu) and peer_ty.isIndexable(zcu)) { 14718 assert(!peer_ty.isTuple(zcu)); 14719 return .{ 14720 .elem_type = peer_ty.elemType2(zcu), 14721 .sentinel = null, 14722 .len = operand_ty.arrayLen(zcu), 14723 }; 14724 } 14725 }, 14726 else => {}, 14727 } 14728 return null; 14729 } 14730 14731 fn analyzeTupleMul( 14732 sema: *Sema, 14733 block: *Block, 14734 src_node: std.zig.Ast.Node.Offset, 14735 operand: Air.Inst.Ref, 14736 factor: usize, 14737 ) CompileError!Air.Inst.Ref { 14738 const pt = sema.pt; 14739 const zcu = pt.zcu; 14740 const operand_ty = sema.typeOf(operand); 14741 const src = block.nodeOffset(src_node); 14742 const len_src = block.src(.{ .node_offset_bin_rhs = src_node }); 14743 14744 const tuple_len = operand_ty.structFieldCount(zcu); 14745 const final_len = std.math.mul(usize, tuple_len, factor) catch 14746 return sema.fail(block, len_src, "operation results in overflow", .{}); 14747 14748 if (final_len == 0) { 14749 return .empty_tuple; 14750 } 14751 const types = try sema.arena.alloc(InternPool.Index, final_len); 14752 const values = try sema.arena.alloc(InternPool.Index, final_len); 14753 14754 const opt_runtime_src = rs: { 14755 var runtime_src: ?LazySrcLoc = null; 14756 for (0..tuple_len) |i| { 14757 types[i] = operand_ty.fieldType(i, zcu).toIntern(); 14758 values[i] = operand_ty.structFieldDefaultValue(i, zcu).toIntern(); 14759 const operand_src = block.src(.{ .array_cat_lhs = .{ 14760 .array_cat_offset = src_node, 14761 .elem_index = @intCast(i), 14762 } }); 14763 if (values[i] == .unreachable_value) { 14764 runtime_src = operand_src; 14765 values[i] = .none; // TODO don't treat unreachable_value as special 14766 } 14767 } 14768 for (0..factor) |i| { 14769 @memmove(types[tuple_len * i ..][0..tuple_len], types[0..tuple_len]); 14770 @memmove(values[tuple_len * i ..][0..tuple_len], values[0..tuple_len]); 14771 } 14772 break :rs runtime_src; 14773 }; 14774 14775 const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{ 14776 .types = types, 14777 .values = values, 14778 })); 14779 14780 const runtime_src = opt_runtime_src orelse { 14781 const tuple_val = try pt.aggregateValue(tuple_ty, values); 14782 return Air.internedToRef(tuple_val.toIntern()); 14783 }; 14784 14785 try sema.requireRuntimeBlock(block, src, runtime_src); 14786 14787 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); 14788 var i: u32 = 0; 14789 while (i < tuple_len) : (i += 1) { 14790 element_refs[i] = try sema.tupleFieldValByIndex(block, operand, @intCast(i), operand_ty); 14791 } 14792 i = 1; 14793 while (i < factor) : (i += 1) { 14794 @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]); 14795 } 14796 14797 return block.addAggregateInit(tuple_ty, element_refs); 14798 } 14799 14800 fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14801 const tracy = trace(@src()); 14802 defer tracy.end(); 14803 14804 const pt = sema.pt; 14805 const zcu = pt.zcu; 14806 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14807 const extra = sema.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data; 14808 const uncoerced_lhs = try sema.resolveInst(extra.lhs); 14809 const uncoerced_lhs_ty = sema.typeOf(uncoerced_lhs); 14810 const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); 14811 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 14812 const operator_src = block.src(.{ .node_offset_main_token = inst_data.src_node }); 14813 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 14814 14815 const lhs, const lhs_ty = coerced_lhs: { 14816 // If we have a result type, we might be able to do this more efficiently 14817 // by coercing the LHS first. Specifically, if we want an array or vector 14818 // and have a tuple, coerce the tuple immediately. 14819 no_coerce: { 14820 if (extra.res_ty == .none) break :no_coerce; 14821 const res_ty = try sema.resolveTypeOrPoison(block, src, extra.res_ty) orelse break :no_coerce; 14822 if (!uncoerced_lhs_ty.isTuple(zcu)) break :no_coerce; 14823 const lhs_len = uncoerced_lhs_ty.structFieldCount(zcu); 14824 const lhs_dest_ty = switch (res_ty.zigTypeTag(zcu)) { 14825 else => break :no_coerce, 14826 .array => try pt.arrayType(.{ 14827 .child = res_ty.childType(zcu).toIntern(), 14828 .len = lhs_len, 14829 .sentinel = if (res_ty.sentinel(zcu)) |s| s.toIntern() else .none, 14830 }), 14831 .vector => try pt.vectorType(.{ 14832 .child = res_ty.childType(zcu).toIntern(), 14833 .len = lhs_len, 14834 }), 14835 }; 14836 // Attempt to coerce to this type, but don't emit an error if it fails. Instead, 14837 // just exit out of this path and let the usual error happen later, so that error 14838 // messages are consistent. 14839 const coerced = sema.coerceExtra(block, lhs_dest_ty, uncoerced_lhs, lhs_src, .{ .report_err = false }) catch |err| switch (err) { 14840 error.NotCoercible => break :no_coerce, 14841 else => |e| return e, 14842 }; 14843 break :coerced_lhs .{ coerced, lhs_dest_ty }; 14844 } 14845 break :coerced_lhs .{ uncoerced_lhs, uncoerced_lhs_ty }; 14846 }; 14847 14848 if (lhs_ty.isTuple(zcu)) { 14849 // In `**` rhs must be comptime-known, but lhs can be runtime-known 14850 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor }); 14851 const factor_casted = try sema.usizeCast(block, rhs_src, factor); 14852 return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted); 14853 } 14854 14855 // Analyze the lhs first, to catch the case that someone tried to do exponentiation 14856 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse { 14857 const msg = msg: { 14858 const msg = try sema.errMsg(lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)}); 14859 errdefer msg.destroy(sema.gpa); 14860 switch (lhs_ty.zigTypeTag(zcu)) { 14861 .int, .float, .comptime_float, .comptime_int, .vector => { 14862 try sema.errNote(operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{}); 14863 }, 14864 else => {}, 14865 } 14866 break :msg msg; 14867 }; 14868 return sema.failWithOwnedErrorMsg(block, msg); 14869 }; 14870 14871 // In `**` rhs must be comptime-known, but lhs can be runtime-known 14872 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor }); 14873 14874 const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch 14875 return sema.fail(block, rhs_src, "operation results in overflow", .{}); 14876 const result_len = try sema.usizeCast(block, src, result_len_u64); 14877 14878 const result_ty = try pt.arrayType(.{ 14879 .len = result_len, 14880 .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none, 14881 .child = lhs_info.elem_type.toIntern(), 14882 }); 14883 14884 const ptr_addrspace = if (lhs_ty.zigTypeTag(zcu) == .pointer) lhs_ty.ptrAddressSpace(zcu) else null; 14885 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); 14886 14887 if (try sema.resolveValue(lhs)) |lhs_val| ct: { 14888 const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu)) 14889 try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :ct 14890 else if (lhs_ty.isSlice(zcu)) 14891 try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :ct 14892 else 14893 lhs_val; 14894 14895 const val = v: { 14896 // Optimization for the common pattern of a single element repeated N times, such 14897 // as zero-filling a byte array. 14898 if (lhs_len == 1 and lhs_info.sentinel == null) { 14899 const elem_val = try lhs_sub_val.elemValue(pt, 0); 14900 break :v try pt.aggregateSplatValue(result_ty, elem_val); 14901 } 14902 14903 const element_vals = try sema.arena.alloc(InternPool.Index, result_len); 14904 var elem_i: usize = 0; 14905 while (elem_i < result_len) { 14906 var lhs_i: usize = 0; 14907 while (lhs_i < lhs_len) : (lhs_i += 1) { 14908 const elem_val = try lhs_sub_val.elemValue(pt, lhs_i); 14909 element_vals[elem_i] = elem_val.toIntern(); 14910 elem_i += 1; 14911 } 14912 } 14913 break :v try pt.aggregateValue(result_ty, element_vals); 14914 }; 14915 return sema.addConstantMaybeRef(val.toIntern(), ptr_addrspace != null); 14916 } 14917 14918 try sema.requireRuntimeBlock(block, src, lhs_src); 14919 14920 // Grab all the LHS values ahead of time, rather than repeatedly emitting instructions 14921 // to get the same elem values. 14922 const lhs_vals = try sema.arena.alloc(Air.Inst.Ref, lhs_len); 14923 for (lhs_vals, 0..) |*lhs_val, idx| { 14924 const idx_ref = try pt.intRef(.usize, idx); 14925 lhs_val.* = try sema.elemVal(block, lhs_src, lhs, idx_ref, src, false); 14926 } 14927 14928 if (ptr_addrspace) |ptr_as| { 14929 const alloc_ty = try pt.ptrTypeSema(.{ 14930 .child = result_ty.toIntern(), 14931 .flags = .{ 14932 .address_space = ptr_as, 14933 .is_const = true, 14934 }, 14935 }); 14936 const alloc = try block.addTy(.alloc, alloc_ty); 14937 const elem_ptr_ty = try pt.ptrTypeSema(.{ 14938 .child = lhs_info.elem_type.toIntern(), 14939 .flags = .{ .address_space = ptr_as }, 14940 }); 14941 14942 var elem_i: usize = 0; 14943 while (elem_i < result_len) { 14944 for (lhs_vals) |lhs_val| { 14945 const elem_index = try pt.intRef(.usize, elem_i); 14946 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 14947 try sema.storePtr2(block, src, elem_ptr, src, lhs_val, lhs_src, .store); 14948 elem_i += 1; 14949 } 14950 } 14951 if (lhs_info.sentinel) |sent_val| { 14952 const elem_index = try pt.intRef(.usize, result_len); 14953 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 14954 const init = Air.internedToRef(sent_val.toIntern()); 14955 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 14956 } 14957 14958 return alloc; 14959 } 14960 14961 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); 14962 for (0..try sema.usizeCast(block, rhs_src, factor)) |i| { 14963 @memcpy(element_refs[i * lhs_len ..][0..lhs_len], lhs_vals); 14964 } 14965 return block.addAggregateInit(result_ty, element_refs); 14966 } 14967 14968 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14969 const pt = sema.pt; 14970 const zcu = pt.zcu; 14971 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 14972 const src = block.nodeOffset(inst_data.src_node); 14973 const lhs_src = src; 14974 const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 14975 14976 const rhs = try sema.resolveInst(inst_data.operand); 14977 const rhs_ty = sema.typeOf(rhs); 14978 const rhs_scalar_ty = rhs_ty.scalarType(zcu); 14979 14980 if (rhs_scalar_ty.isUnsignedInt(zcu) or switch (rhs_scalar_ty.zigTypeTag(zcu)) { 14981 .int, .comptime_int, .float, .comptime_float => false, 14982 else => true, 14983 }) { 14984 return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}); 14985 } 14986 14987 if (rhs_scalar_ty.isAnyFloat()) { 14988 // We handle float negation here to ensure negative zero is represented in the bits. 14989 if (try sema.resolveValue(rhs)) |rhs_val| { 14990 const result = try arith.negateFloat(sema, rhs_ty, rhs_val); 14991 return Air.internedToRef(result.toIntern()); 14992 } 14993 try sema.requireRuntimeBlock(block, src, null); 14994 return block.addUnOp(if (block.float_mode == .optimized) .neg_optimized else .neg, rhs); 14995 } 14996 14997 const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern()); 14998 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true); 14999 } 15000 15001 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15002 const pt = sema.pt; 15003 const zcu = pt.zcu; 15004 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 15005 const src = block.nodeOffset(inst_data.src_node); 15006 const lhs_src = src; 15007 const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 15008 15009 const rhs = try sema.resolveInst(inst_data.operand); 15010 const rhs_ty = sema.typeOf(rhs); 15011 const rhs_scalar_ty = rhs_ty.scalarType(zcu); 15012 15013 switch (rhs_scalar_ty.zigTypeTag(zcu)) { 15014 .int, .comptime_int, .float, .comptime_float => {}, 15015 else => return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}), 15016 } 15017 15018 const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern()); 15019 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true); 15020 } 15021 15022 fn zirArithmetic( 15023 sema: *Sema, 15024 block: *Block, 15025 inst: Zir.Inst.Index, 15026 zir_tag: Zir.Inst.Tag, 15027 safety: bool, 15028 ) CompileError!Air.Inst.Ref { 15029 const tracy = trace(@src()); 15030 defer tracy.end(); 15031 15032 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15033 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15034 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 15035 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 15036 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15037 const lhs = try sema.resolveInst(extra.lhs); 15038 const rhs = try sema.resolveInst(extra.rhs); 15039 15040 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, src, lhs_src, rhs_src, safety); 15041 } 15042 15043 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15044 const pt = sema.pt; 15045 const zcu = pt.zcu; 15046 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15047 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15048 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 15049 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 15050 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15051 const lhs = try sema.resolveInst(extra.lhs); 15052 const rhs = try sema.resolveInst(extra.rhs); 15053 const lhs_ty = sema.typeOf(lhs); 15054 const rhs_ty = sema.typeOf(rhs); 15055 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15056 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15057 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15058 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15059 15060 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15061 .override = &.{ lhs_src, rhs_src }, 15062 }); 15063 15064 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15065 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15066 15067 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 15068 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15069 15070 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15071 15072 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div); 15073 15074 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15075 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15076 15077 if ((lhs_ty.zigTypeTag(zcu) == .comptime_float and rhs_ty.zigTypeTag(zcu) == .comptime_int) or 15078 (lhs_ty.zigTypeTag(zcu) == .comptime_int and rhs_ty.zigTypeTag(zcu) == .comptime_float)) 15079 { 15080 // If it makes a difference whether we coerce to ints or floats before doing the division, error. 15081 // If lhs % rhs is 0, it doesn't matter. 15082 const lhs_val = maybe_lhs_val orelse unreachable; 15083 const rhs_val = maybe_rhs_val orelse unreachable; 15084 const rem = arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem) catch unreachable; 15085 if (!rem.compareAllWithZero(.eq, zcu)) { 15086 return sema.fail( 15087 block, 15088 src, 15089 "ambiguous coercion of division operands '{f}' and '{f}'; non-zero remainder '{f}'", 15090 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt), rem.fmtValueSema(pt, sema) }, 15091 ); 15092 } 15093 } 15094 15095 // TODO: emit compile error when .div is used on integers and there would be an 15096 // ambiguous result between div_floor and div_trunc. 15097 15098 // The rules here are like those in `analyzeArithmetic`: 15099 // 15100 // * If both operands are comptime-known, call the corresponding function in `arith`. 15101 // Inputs which would be IB at runtime are compile errors. 15102 // 15103 // * Otherwise, if one operand is comptime-known `undefined`, we either trigger a compile error 15104 // or return `undefined`, depending on whether this operator can trigger IB. 15105 // 15106 // * No other comptime operand determines a comptime result, so remaining cases are runtime ops. 15107 15108 const allow_div_zero = !is_int and 15109 resolved_type.toIntern() != .comptime_float_type and 15110 block.float_mode == .strict; 15111 15112 if (maybe_lhs_val) |lhs_val| { 15113 if (maybe_rhs_val) |rhs_val| { 15114 return .fromValue(try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div)); 15115 } 15116 if (allow_div_zero) { 15117 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15118 } else { 15119 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 15120 } 15121 } else if (maybe_rhs_val) |rhs_val| { 15122 if (allow_div_zero) { 15123 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15124 } else { 15125 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); 15126 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15127 } 15128 } 15129 15130 if (block.wantSafety()) { 15131 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 15132 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15133 } 15134 15135 const air_tag: Air.Inst.Tag = if (is_int) blk: { 15136 if (lhs_ty.isSignedInt(zcu) or rhs_ty.isSignedInt(zcu)) { 15137 return sema.fail( 15138 block, 15139 src, 15140 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, or @divExact", 15141 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) }, 15142 ); 15143 } 15144 break :blk .div_trunc; 15145 } else switch (block.float_mode) { 15146 .optimized => .div_float_optimized, 15147 .strict => .div_float, 15148 }; 15149 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15150 } 15151 15152 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15153 const pt = sema.pt; 15154 const zcu = pt.zcu; 15155 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15156 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15157 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 15158 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 15159 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15160 const lhs = try sema.resolveInst(extra.lhs); 15161 const rhs = try sema.resolveInst(extra.rhs); 15162 const lhs_ty = sema.typeOf(lhs); 15163 const rhs_ty = sema.typeOf(rhs); 15164 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15165 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15166 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15167 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15168 15169 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15170 .override = &.{ lhs_src, rhs_src }, 15171 }); 15172 15173 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15174 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15175 15176 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 15177 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15178 15179 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15180 15181 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); 15182 15183 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15184 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15185 15186 // Because `@divExact` can trigger Illegal Behavior, undefined operands trigger Illegal Behavior. 15187 15188 if (maybe_lhs_val) |lhs_val| { 15189 if (maybe_rhs_val) |rhs_val| { 15190 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_exact); 15191 return Air.internedToRef(result.toIntern()); 15192 } 15193 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 15194 } else if (maybe_rhs_val) |rhs_val| { 15195 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); 15196 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15197 } 15198 15199 // Depending on whether safety is enabled, we will have a slightly different strategy 15200 // here. The `div_exact` AIR instruction causes illegal behavior if a remainder 15201 // is produced, so in the safety check case, it cannot be used. Instead we do a 15202 // div_trunc and check for remainder. 15203 15204 if (block.wantSafety()) { 15205 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 15206 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15207 15208 const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs); 15209 const ok = if (!is_int) ok: { 15210 const floored = try block.addUnOp(.floor, result); 15211 15212 if (resolved_type.zigTypeTag(zcu) == .vector) { 15213 const eql = try block.addCmpVector(result, floored, .eq); 15214 break :ok try block.addReduce(eql, .And); 15215 } else { 15216 const is_in_range = try block.addBinOp(switch (block.float_mode) { 15217 .strict => .cmp_eq, 15218 .optimized => .cmp_eq_optimized, 15219 }, result, floored); 15220 break :ok is_in_range; 15221 } 15222 } else ok: { 15223 const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); 15224 15225 const scalar_zero = switch (scalar_tag) { 15226 .comptime_float, .float => try pt.floatValue(resolved_type.scalarType(zcu), 0.0), 15227 .comptime_int, .int => try pt.intValue(resolved_type.scalarType(zcu), 0), 15228 else => unreachable, 15229 }; 15230 if (resolved_type.zigTypeTag(zcu) == .vector) { 15231 const zero_val = try sema.splat(resolved_type, scalar_zero); 15232 const zero = Air.internedToRef(zero_val.toIntern()); 15233 const eql = try block.addCmpVector(remainder, zero, .eq); 15234 break :ok try block.addReduce(eql, .And); 15235 } else { 15236 const zero = Air.internedToRef(scalar_zero.toIntern()); 15237 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero); 15238 break :ok is_in_range; 15239 } 15240 }; 15241 try sema.addSafetyCheck(block, src, ok, .exact_division_remainder); 15242 return result; 15243 } 15244 15245 return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs); 15246 } 15247 15248 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15249 const pt = sema.pt; 15250 const zcu = pt.zcu; 15251 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15252 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15253 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 15254 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 15255 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15256 const lhs = try sema.resolveInst(extra.lhs); 15257 const rhs = try sema.resolveInst(extra.rhs); 15258 const lhs_ty = sema.typeOf(lhs); 15259 const rhs_ty = sema.typeOf(rhs); 15260 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15261 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15262 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15263 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15264 15265 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15266 .override = &.{ lhs_src, rhs_src }, 15267 }); 15268 15269 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15270 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15271 15272 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 15273 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15274 15275 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15276 15277 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor); 15278 15279 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15280 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15281 15282 const allow_div_zero = !is_int and 15283 resolved_type.toIntern() != .comptime_float_type and 15284 block.float_mode == .strict; 15285 15286 if (maybe_lhs_val) |lhs_val| { 15287 if (maybe_rhs_val) |rhs_val| { 15288 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_floor); 15289 return Air.internedToRef(result.toIntern()); 15290 } 15291 if (allow_div_zero) { 15292 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15293 } else { 15294 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 15295 } 15296 } else if (maybe_rhs_val) |rhs_val| { 15297 if (allow_div_zero) { 15298 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15299 } else { 15300 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); 15301 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15302 } 15303 } 15304 15305 if (block.wantSafety()) { 15306 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 15307 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15308 } 15309 15310 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); 15311 } 15312 15313 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15314 const pt = sema.pt; 15315 const zcu = pt.zcu; 15316 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15317 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15318 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 15319 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 15320 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15321 const lhs = try sema.resolveInst(extra.lhs); 15322 const rhs = try sema.resolveInst(extra.rhs); 15323 const lhs_ty = sema.typeOf(lhs); 15324 const rhs_ty = sema.typeOf(rhs); 15325 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15326 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15327 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15328 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15329 15330 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15331 .override = &.{ lhs_src, rhs_src }, 15332 }); 15333 15334 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15335 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15336 15337 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 15338 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15339 15340 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15341 15342 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc); 15343 15344 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15345 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15346 15347 const allow_div_zero = !is_int and 15348 resolved_type.toIntern() != .comptime_float_type and 15349 block.float_mode == .strict; 15350 15351 if (maybe_lhs_val) |lhs_val| { 15352 if (maybe_rhs_val) |rhs_val| { 15353 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_trunc); 15354 return Air.internedToRef(result.toIntern()); 15355 } 15356 if (allow_div_zero) { 15357 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15358 } else { 15359 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 15360 } 15361 } else if (maybe_rhs_val) |rhs_val| { 15362 if (allow_div_zero) { 15363 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15364 } else { 15365 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); 15366 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15367 } 15368 } 15369 15370 if (block.wantSafety()) { 15371 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 15372 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15373 } 15374 15375 return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs); 15376 } 15377 15378 fn addDivIntOverflowSafety( 15379 sema: *Sema, 15380 block: *Block, 15381 src: LazySrcLoc, 15382 resolved_type: Type, 15383 lhs_scalar_ty: Type, 15384 maybe_lhs_val: ?Value, 15385 maybe_rhs_val: ?Value, 15386 casted_lhs: Air.Inst.Ref, 15387 casted_rhs: Air.Inst.Ref, 15388 is_int: bool, 15389 ) CompileError!void { 15390 const pt = sema.pt; 15391 const zcu = pt.zcu; 15392 if (!is_int) return; 15393 15394 // If the LHS is unsigned, it cannot cause overflow. 15395 if (!lhs_scalar_ty.isSignedInt(zcu)) return; 15396 15397 // If the LHS is widened to a larger integer type, no overflow is possible. 15398 if (lhs_scalar_ty.intInfo(zcu).bits < resolved_type.intInfo(zcu).bits) { 15399 return; 15400 } 15401 15402 const min_int = try resolved_type.minInt(pt, resolved_type); 15403 const neg_one_scalar = try pt.intValue(lhs_scalar_ty, -1); 15404 const neg_one = try sema.splat(resolved_type, neg_one_scalar); 15405 15406 // If the LHS is comptime-known to be not equal to the min int, 15407 // no overflow is possible. 15408 if (maybe_lhs_val) |lhs_val| { 15409 if (try lhs_val.compareAll(.neq, min_int, resolved_type, pt)) return; 15410 } 15411 15412 // If the RHS is comptime-known to not be equal to -1, no overflow is possible. 15413 if (maybe_rhs_val) |rhs_val| { 15414 if (try rhs_val.compareAll(.neq, neg_one, resolved_type, pt)) return; 15415 } 15416 15417 if (resolved_type.zigTypeTag(zcu) == .vector) { 15418 const vec_len = resolved_type.vectorLen(zcu); 15419 15420 // This is a bool vector whose elements are true if the LHS element does NOT equal `min_int`. 15421 const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val) |lhs_val| ok: { 15422 // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements. 15423 const min_int_scalar = try min_int.elemValue(pt, 0); 15424 const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len); 15425 for (elems_ok, 0..) |*elem_ok, elem_idx| { 15426 const elem_val = try lhs_val.elemValue(pt, elem_idx); 15427 elem_ok.* = if (elem_val.eqlScalarNum(min_int_scalar, zcu)) .bool_false else .bool_true; 15428 } 15429 break :ok .fromValue(try pt.aggregateValue(try pt.vectorType(.{ 15430 .len = vec_len, 15431 .child = .bool_type, 15432 }), elems_ok)); 15433 } else ok: { 15434 // The operand isn't comptime-known; add a runtime comparison. 15435 const min_int_ref = Air.internedToRef(min_int.toIntern()); 15436 break :ok try block.addCmpVector(casted_lhs, min_int_ref, .neq); 15437 }; 15438 15439 // This is a bool vector whose elements are true if the RHS element does NOT equal -1. 15440 const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val) |rhs_val| ok: { 15441 // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements. 15442 const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len); 15443 for (elems_ok, 0..) |*elem_ok, elem_idx| { 15444 const elem_val = try rhs_val.elemValue(pt, elem_idx); 15445 elem_ok.* = if (elem_val.eqlScalarNum(neg_one_scalar, zcu)) .bool_false else .bool_true; 15446 } 15447 break :ok .fromValue(try pt.aggregateValue(try pt.vectorType(.{ 15448 .len = vec_len, 15449 .child = .bool_type, 15450 }), elems_ok)); 15451 } else ok: { 15452 // The operand isn't comptime-known; add a runtime comparison. 15453 const neg_one_ref = Air.internedToRef(neg_one.toIntern()); 15454 break :ok try block.addCmpVector(casted_rhs, neg_one_ref, .neq); 15455 }; 15456 15457 const ok = try block.addReduce(try block.addBinOp(.bool_or, lhs_ok, rhs_ok), .And); 15458 try sema.addSafetyCheck(block, src, ok, .integer_overflow); 15459 } else { 15460 const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val == null) ok: { 15461 const min_int_ref = Air.internedToRef(min_int.toIntern()); 15462 break :ok try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); 15463 } else .none; // means false 15464 const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val == null) ok: { 15465 const neg_one_ref = Air.internedToRef(neg_one.toIntern()); 15466 break :ok try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref); 15467 } else .none; // means false 15468 15469 const ok = if (lhs_ok != .none and rhs_ok != .none) 15470 try block.addBinOp(.bool_or, lhs_ok, rhs_ok) 15471 else if (lhs_ok != .none) 15472 lhs_ok 15473 else if (rhs_ok != .none) 15474 rhs_ok 15475 else 15476 unreachable; 15477 15478 try sema.addSafetyCheck(block, src, ok, .integer_overflow); 15479 } 15480 } 15481 15482 fn addDivByZeroSafety( 15483 sema: *Sema, 15484 block: *Block, 15485 src: LazySrcLoc, 15486 resolved_type: Type, 15487 maybe_rhs_val: ?Value, 15488 casted_rhs: Air.Inst.Ref, 15489 is_int: bool, 15490 ) CompileError!void { 15491 // Strict IEEE floats have well-defined division by zero. 15492 if (!is_int and block.float_mode == .strict) return; 15493 15494 // If rhs was comptime-known to be zero a compile error would have been 15495 // emitted above. 15496 if (maybe_rhs_val != null) return; 15497 15498 const pt = sema.pt; 15499 const zcu = pt.zcu; 15500 const scalar_zero = if (is_int) 15501 try pt.intValue(resolved_type.scalarType(zcu), 0) 15502 else 15503 try pt.floatValue(resolved_type.scalarType(zcu), 0.0); 15504 const ok = if (resolved_type.zigTypeTag(zcu) == .vector) ok: { 15505 const zero_val = try sema.splat(resolved_type, scalar_zero); 15506 const zero = Air.internedToRef(zero_val.toIntern()); 15507 const ok = try block.addCmpVector(casted_rhs, zero, .neq); 15508 break :ok try block.addReduce(ok, .And); 15509 } else ok: { 15510 const zero = Air.internedToRef(scalar_zero.toIntern()); 15511 break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); 15512 }; 15513 try sema.addSafetyCheck(block, src, ok, .divide_by_zero); 15514 } 15515 15516 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag { 15517 if (is_int) return normal; 15518 return switch (block.float_mode) { 15519 .strict => normal, 15520 .optimized => optimized, 15521 }; 15522 } 15523 15524 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15525 const pt = sema.pt; 15526 const zcu = pt.zcu; 15527 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15528 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15529 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 15530 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 15531 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15532 const lhs = try sema.resolveInst(extra.lhs); 15533 const rhs = try sema.resolveInst(extra.rhs); 15534 const lhs_ty = sema.typeOf(lhs); 15535 const rhs_ty = sema.typeOf(rhs); 15536 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15537 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15538 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15539 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15540 15541 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15542 .override = &.{ lhs_src, rhs_src }, 15543 }); 15544 15545 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15546 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15547 15548 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 15549 const rhs_scalar_ty = rhs_ty.scalarType(zcu); 15550 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15551 15552 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15553 15554 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem); 15555 15556 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15557 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15558 15559 const lhs_maybe_negative = a: { 15560 if (lhs_scalar_ty.isUnsignedInt(zcu)) break :a false; 15561 const lhs_val = maybe_lhs_val orelse break :a true; 15562 if (lhs_val.compareAllWithZero(.gte, zcu)) break :a false; 15563 break :a true; 15564 }; 15565 const rhs_maybe_negative = a: { 15566 if (rhs_scalar_ty.isUnsignedInt(zcu)) break :a false; 15567 const rhs_val = maybe_rhs_val orelse break :a true; 15568 if (rhs_val.compareAllWithZero(.gte, zcu)) break :a false; 15569 break :a true; 15570 }; 15571 15572 if (maybe_lhs_val) |lhs_val| { 15573 if (maybe_rhs_val) |rhs_val| { 15574 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem); 15575 if (lhs_maybe_negative or rhs_maybe_negative) { 15576 if (!result.compareAllWithZero(.eq, zcu)) { 15577 // Non-zero result means ambiguity between mod and rem 15578 return sema.failWithModRemNegative(block, src: { 15579 if (lhs_maybe_negative) break :src lhs_src; 15580 if (rhs_maybe_negative) break :src rhs_src; 15581 unreachable; 15582 }, lhs_ty, rhs_ty); 15583 } 15584 } 15585 return Air.internedToRef(result.toIntern()); 15586 } 15587 } 15588 15589 // Result not comptime-known, so floats and signed integers are illegal due to mod/rem ambiguity 15590 if (lhs_maybe_negative or rhs_maybe_negative) { 15591 return sema.failWithModRemNegative(block, src: { 15592 if (lhs_maybe_negative) break :src lhs_src; 15593 if (rhs_maybe_negative) break :src rhs_src; 15594 unreachable; 15595 }, lhs_ty, rhs_ty); 15596 } 15597 15598 const allow_div_zero = !is_int and 15599 resolved_type.toIntern() != .comptime_float_type and 15600 block.float_mode == .strict; 15601 15602 if (maybe_lhs_val) |lhs_val| { 15603 if (allow_div_zero) { 15604 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15605 } else { 15606 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 15607 } 15608 } else if (maybe_rhs_val) |rhs_val| { 15609 if (allow_div_zero) { 15610 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15611 } else { 15612 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); 15613 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15614 } 15615 } 15616 15617 if (block.wantSafety()) { 15618 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15619 } 15620 15621 const air_tag = airTag(block, is_int, .rem, .rem_optimized); 15622 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15623 } 15624 15625 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15626 const pt = sema.pt; 15627 const zcu = pt.zcu; 15628 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15629 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15630 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 15631 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 15632 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15633 const lhs = try sema.resolveInst(extra.lhs); 15634 const rhs = try sema.resolveInst(extra.rhs); 15635 const lhs_ty = sema.typeOf(lhs); 15636 const rhs_ty = sema.typeOf(rhs); 15637 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15638 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15639 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15640 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15641 15642 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15643 .override = &.{ lhs_src, rhs_src }, 15644 }); 15645 15646 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15647 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15648 15649 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15650 15651 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15652 15653 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod); 15654 15655 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15656 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15657 15658 const allow_div_zero = !is_int and 15659 resolved_type.toIntern() != .comptime_float_type and 15660 block.float_mode == .strict; 15661 15662 if (maybe_lhs_val) |lhs_val| { 15663 if (maybe_rhs_val) |rhs_val| { 15664 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .mod); 15665 return Air.internedToRef(result.toIntern()); 15666 } 15667 if (allow_div_zero) { 15668 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15669 } else { 15670 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 15671 } 15672 } else if (maybe_rhs_val) |rhs_val| { 15673 if (allow_div_zero) { 15674 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15675 } else { 15676 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); 15677 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15678 } 15679 } 15680 15681 if (block.wantSafety()) { 15682 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15683 } 15684 15685 const air_tag = airTag(block, is_int, .mod, .mod_optimized); 15686 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15687 } 15688 15689 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15690 const pt = sema.pt; 15691 const zcu = pt.zcu; 15692 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15693 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15694 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 15695 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 15696 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15697 const lhs = try sema.resolveInst(extra.lhs); 15698 const rhs = try sema.resolveInst(extra.rhs); 15699 const lhs_ty = sema.typeOf(lhs); 15700 const rhs_ty = sema.typeOf(rhs); 15701 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15702 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15703 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15704 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15705 15706 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15707 .override = &.{ lhs_src, rhs_src }, 15708 }); 15709 15710 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15711 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15712 15713 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15714 15715 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15716 15717 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem); 15718 15719 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15720 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15721 15722 const allow_div_zero = !is_int and 15723 resolved_type.toIntern() != .comptime_float_type and 15724 block.float_mode == .strict; 15725 15726 if (maybe_lhs_val) |lhs_val| { 15727 if (maybe_rhs_val) |rhs_val| { 15728 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem); 15729 return Air.internedToRef(result.toIntern()); 15730 } 15731 if (allow_div_zero) { 15732 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15733 } else { 15734 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 15735 } 15736 } else if (maybe_rhs_val) |rhs_val| { 15737 if (allow_div_zero) { 15738 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 15739 } else { 15740 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); 15741 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15742 } 15743 } 15744 15745 if (block.wantSafety()) { 15746 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15747 } 15748 15749 const air_tag = airTag(block, is_int, .rem, .rem_optimized); 15750 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15751 } 15752 15753 fn zirOverflowArithmetic( 15754 sema: *Sema, 15755 block: *Block, 15756 extended: Zir.Inst.Extended.InstData, 15757 zir_tag: Zir.Inst.Extended, 15758 ) CompileError!Air.Inst.Ref { 15759 const tracy = trace(@src()); 15760 defer tracy.end(); 15761 15762 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 15763 const src = block.nodeOffset(extra.node); 15764 15765 const lhs_src = block.builtinCallArgSrc(extra.node, 0); 15766 const rhs_src = block.builtinCallArgSrc(extra.node, 1); 15767 15768 const uncasted_lhs = try sema.resolveInst(extra.lhs); 15769 const uncasted_rhs = try sema.resolveInst(extra.rhs); 15770 15771 const lhs_ty = sema.typeOf(uncasted_lhs); 15772 const rhs_ty = sema.typeOf(uncasted_rhs); 15773 const pt = sema.pt; 15774 const zcu = pt.zcu; 15775 const ip = &zcu.intern_pool; 15776 15777 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15778 15779 const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs }; 15780 const dest_ty = if (zir_tag == .shl_with_overflow) 15781 lhs_ty 15782 else 15783 try sema.resolvePeerTypes(block, src, instructions, .{ 15784 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 15785 }); 15786 15787 const rhs_dest_ty = if (zir_tag == .shl_with_overflow) 15788 try sema.log2IntType(block, lhs_ty, src) 15789 else 15790 dest_ty; 15791 15792 const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src); 15793 const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src); 15794 15795 if (dest_ty.scalarType(zcu).zigTypeTag(zcu) != .int) { 15796 return sema.fail(block, src, "expected vector of integers or integer tag type, found '{f}'", .{dest_ty.fmt(pt)}); 15797 } 15798 15799 const maybe_lhs_val = try sema.resolveValue(lhs); 15800 const maybe_rhs_val = try sema.resolveValue(rhs); 15801 15802 const tuple_ty = try pt.overflowArithmeticTupleType(dest_ty); 15803 const overflow_ty: Type = .fromInterned(ip.indexToKey(tuple_ty.toIntern()).tuple_type.types.get(ip)[1]); 15804 15805 var result: struct { 15806 inst: Air.Inst.Ref = .none, 15807 wrapped: Value = Value.@"unreachable", 15808 overflow_bit: Value, 15809 } = result: { 15810 switch (zir_tag) { 15811 .add_with_overflow => { 15812 // If either of the arguments is zero, `false` is returned and the other is stored 15813 // to the result, even if it is undefined.. 15814 // Otherwise, if either of the argument is undefined, undefined is returned. 15815 if (maybe_lhs_val) |lhs_val| { 15816 if (!lhs_val.isUndef(zcu) and (try lhs_val.compareAllWithZeroSema(.eq, pt))) { 15817 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; 15818 } 15819 } 15820 if (maybe_rhs_val) |rhs_val| { 15821 if (!rhs_val.isUndef(zcu) and (try rhs_val.compareAllWithZeroSema(.eq, pt))) { 15822 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15823 } 15824 } 15825 if (maybe_lhs_val) |lhs_val| { 15826 if (maybe_rhs_val) |rhs_val| { 15827 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) { 15828 break :result .{ .overflow_bit = .undef, .wrapped = .undef }; 15829 } 15830 15831 const result = try arith.addWithOverflow(sema, dest_ty, lhs_val, rhs_val); 15832 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15833 } 15834 } 15835 }, 15836 .sub_with_overflow => { 15837 // If the rhs is zero, then the result is lhs and no overflow occured. 15838 // Otherwise, if either result is undefined, both results are undefined. 15839 if (maybe_rhs_val) |rhs_val| { 15840 if (rhs_val.isUndef(zcu)) { 15841 break :result .{ .overflow_bit = .undef, .wrapped = .undef }; 15842 } else if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { 15843 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15844 } else if (maybe_lhs_val) |lhs_val| { 15845 if (lhs_val.isUndef(zcu)) { 15846 break :result .{ .overflow_bit = .undef, .wrapped = .undef }; 15847 } 15848 15849 const result = try arith.subWithOverflow(sema, dest_ty, lhs_val, rhs_val); 15850 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15851 } 15852 } 15853 }, 15854 .mul_with_overflow => { 15855 // If either of the arguments is zero, the result is zero and no overflow occured. 15856 // If either of the arguments is one, the result is the other and no overflow occured. 15857 // Otherwise, if either of the arguments is undefined, both results are undefined. 15858 const scalar_one = try pt.intValue(dest_ty.scalarType(zcu), 1); 15859 if (maybe_lhs_val) |lhs_val| { 15860 if (!lhs_val.isUndef(zcu)) { 15861 if (try lhs_val.compareAllWithZeroSema(.eq, pt)) { 15862 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15863 } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { 15864 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; 15865 } 15866 } 15867 } 15868 15869 if (maybe_rhs_val) |rhs_val| { 15870 if (!rhs_val.isUndef(zcu)) { 15871 if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { 15872 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; 15873 } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { 15874 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15875 } 15876 } 15877 } 15878 15879 if (maybe_lhs_val) |lhs_val| { 15880 if (maybe_rhs_val) |rhs_val| { 15881 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) { 15882 break :result .{ .overflow_bit = .undef, .wrapped = .undef }; 15883 } 15884 15885 const result = try arith.mulWithOverflow(sema, dest_ty, lhs_val, rhs_val); 15886 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15887 } 15888 } 15889 }, 15890 .shl_with_overflow => { 15891 // If either of the arguments is undefined, IB is possible and we return an error. 15892 // If lhs is zero, the result is zero and no overflow occurred. 15893 // If rhs is zero, the result is lhs and no overflow occurred. 15894 const scalar_ty = lhs_ty.scalarType(zcu); 15895 if (maybe_rhs_val) |rhs_val| { 15896 if (maybe_lhs_val) |lhs_val| { 15897 const result = try arith.shlWithOverflow(sema, block, lhs_ty, lhs_val, rhs_val, lhs_src, rhs_src); 15898 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15899 } 15900 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, null); 15901 const bits = scalar_ty.intInfo(zcu).bits; 15902 switch (rhs_ty.zigTypeTag(zcu)) { 15903 .int, .comptime_int => { 15904 switch (try rhs_val.orderAgainstZeroSema(pt)) { 15905 .gt => { 15906 var rhs_space: Value.BigIntSpace = undefined; 15907 const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt); 15908 if (rhs_bigint.orderAgainstScalar(bits) != .lt) { 15909 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); 15910 } 15911 }, 15912 .eq => break :result .{ .overflow_bit = .zero_u1, .inst = lhs }, 15913 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null), 15914 } 15915 }, 15916 .vector => { 15917 var any_positive: bool = false; 15918 for (0..rhs_ty.vectorLen(zcu)) |elem_idx| { 15919 const rhs_elem = try rhs_val.elemValue(pt, elem_idx); 15920 if (rhs_elem.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, elem_idx); 15921 switch (try rhs_elem.orderAgainstZeroSema(pt)) { 15922 .gt => { 15923 var rhs_elem_space: Value.BigIntSpace = undefined; 15924 const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt); 15925 if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { 15926 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); 15927 } 15928 any_positive = true; 15929 }, 15930 .eq => {}, 15931 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx), 15932 } 15933 } 15934 if (!any_positive) break :result .{ .overflow_bit = try pt.aggregateSplatValue(overflow_ty, .zero_u1), .inst = lhs }; 15935 }, 15936 else => unreachable, 15937 } 15938 if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { 15939 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15940 } 15941 } else { 15942 if (scalar_ty.toIntern() == .comptime_int_type) { 15943 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 15944 } 15945 if (maybe_lhs_val) |lhs_val| { 15946 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 15947 if (try lhs_val.compareAllWithZeroSema(.eq, pt)) { 15948 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15949 } 15950 } 15951 } 15952 }, 15953 else => unreachable, 15954 } 15955 15956 const air_tag: Air.Inst.Tag = switch (zir_tag) { 15957 .add_with_overflow => .add_with_overflow, 15958 .mul_with_overflow => .mul_with_overflow, 15959 .sub_with_overflow => .sub_with_overflow, 15960 .shl_with_overflow => .shl_with_overflow, 15961 else => unreachable, 15962 }; 15963 15964 return block.addInst(.{ 15965 .tag = air_tag, 15966 .data = .{ .ty_pl = .{ 15967 .ty = Air.internedToRef(tuple_ty.toIntern()), 15968 .payload = try block.sema.addExtra(Air.Bin{ 15969 .lhs = lhs, 15970 .rhs = rhs, 15971 }), 15972 } }, 15973 }); 15974 }; 15975 15976 if (result.inst != .none) { 15977 if (try sema.resolveValue(result.inst)) |some| { 15978 result.wrapped = some; 15979 result.inst = .none; 15980 } 15981 } 15982 15983 if (result.inst == .none) { 15984 return Air.internedToRef((try pt.aggregateValue(tuple_ty, &.{ 15985 result.wrapped.toIntern(), 15986 result.overflow_bit.toIntern(), 15987 })).toIntern()); 15988 } 15989 15990 const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2); 15991 element_refs[0] = result.inst; 15992 element_refs[1] = Air.internedToRef(result.overflow_bit.toIntern()); 15993 return block.addAggregateInit(tuple_ty, element_refs); 15994 } 15995 15996 fn splat(sema: *Sema, ty: Type, val: Value) !Value { 15997 const pt = sema.pt; 15998 if (ty.zigTypeTag(pt.zcu) != .vector) return val; 15999 return pt.aggregateSplatValue(ty, val); 16000 } 16001 16002 fn analyzeArithmetic( 16003 sema: *Sema, 16004 block: *Block, 16005 zir_tag: Zir.Inst.Tag, 16006 lhs: Air.Inst.Ref, 16007 rhs: Air.Inst.Ref, 16008 src: LazySrcLoc, 16009 lhs_src: LazySrcLoc, 16010 rhs_src: LazySrcLoc, 16011 want_safety: bool, 16012 ) CompileError!Air.Inst.Ref { 16013 const pt = sema.pt; 16014 const zcu = pt.zcu; 16015 const lhs_ty = sema.typeOf(lhs); 16016 const rhs_ty = sema.typeOf(rhs); 16017 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 16018 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 16019 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 16020 16021 if (lhs_zig_ty_tag == .pointer) { 16022 if (rhs_zig_ty_tag == .pointer) { 16023 if (lhs_ty.ptrSize(zcu) != .slice and rhs_ty.ptrSize(zcu) != .slice) { 16024 if (zir_tag != .sub) { 16025 return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction"); 16026 } 16027 if (!lhs_ty.elemType2(zcu).eql(rhs_ty.elemType2(zcu), zcu)) { 16028 return sema.fail(block, src, "incompatible pointer arithmetic operands '{f}' and '{f}'", .{ 16029 lhs_ty.fmt(pt), rhs_ty.fmt(pt), 16030 }); 16031 } 16032 16033 const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu); 16034 if (elem_size == 0) { 16035 return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{ 16036 lhs_ty.elemType2(zcu).fmt(pt), 16037 }); 16038 } 16039 16040 const runtime_src = runtime_src: { 16041 if (try sema.resolveValue(lhs)) |lhs_value| { 16042 if (try sema.resolveValue(rhs)) |rhs_value| { 16043 const lhs_ptr = switch (zcu.intern_pool.indexToKey(lhs_value.toIntern())) { 16044 .undef => return sema.failWithUseOfUndef(block, lhs_src, null), 16045 .ptr => |ptr| ptr, 16046 else => unreachable, 16047 }; 16048 const rhs_ptr = switch (zcu.intern_pool.indexToKey(rhs_value.toIntern())) { 16049 .undef => return sema.failWithUseOfUndef(block, rhs_src, null), 16050 .ptr => |ptr| ptr, 16051 else => unreachable, 16052 }; 16053 // Make sure the pointers point to the same data. 16054 if (!lhs_ptr.base_addr.eql(rhs_ptr.base_addr)) break :runtime_src src; 16055 const address = std.math.sub(u64, lhs_ptr.byte_offset, rhs_ptr.byte_offset) catch 16056 return sema.fail(block, src, "operation results in overflow", .{}); 16057 const result = address / elem_size; 16058 return try pt.intRef(.usize, result); 16059 } else { 16060 break :runtime_src lhs_src; 16061 } 16062 } else { 16063 break :runtime_src rhs_src; 16064 } 16065 }; 16066 16067 try sema.requireRuntimeBlock(block, src, runtime_src); 16068 try sema.checkLogicalPtrOperation(block, src, lhs_ty); 16069 try sema.checkLogicalPtrOperation(block, src, rhs_ty); 16070 const lhs_int = try block.addBitCast(.usize, lhs); 16071 const rhs_int = try block.addBitCast(.usize, rhs); 16072 const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int); 16073 return try block.addBinOp(.div_exact, address, try pt.intRef(.usize, elem_size)); 16074 } 16075 } else { 16076 switch (lhs_ty.ptrSize(zcu)) { 16077 .one, .slice => {}, 16078 .many, .c => { 16079 const air_tag: Air.Inst.Tag = switch (zir_tag) { 16080 .add => .ptr_add, 16081 .sub => .ptr_sub, 16082 else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), 16083 }; 16084 16085 if (!try lhs_ty.elemType2(zcu).hasRuntimeBitsSema(pt)) { 16086 return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{ 16087 lhs_ty.elemType2(zcu).fmt(pt), 16088 }); 16089 } 16090 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); 16091 }, 16092 } 16093 } 16094 } 16095 16096 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 16097 .override = &.{ lhs_src, rhs_src }, 16098 }); 16099 16100 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 16101 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 16102 16103 const scalar_type = resolved_type.scalarType(zcu); 16104 const scalar_tag = scalar_type.zigTypeTag(zcu); 16105 16106 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag); 16107 16108 // The rules we'll implement below are as follows: 16109 // 16110 // * If both operands are comptime-known, we call the corresponding function in `arith` to get 16111 // the comptime-known result. Inputs which would be IB at runtime are compile errors. 16112 // 16113 // * Otherwise, if one operand is comptime-known `undefined`, we trigger a compile error if this 16114 // operator can ever possibly trigger IB, or otherwise return comptime-known `undefined`. 16115 // 16116 // * No other comptime operand detemines a comptime result; e.g. `0 * x` isn't always `0` because 16117 // of `undefined`. Therefore, the remaining cases all become runtime operations. 16118 16119 const is_int = switch (scalar_tag) { 16120 .int, .comptime_int => true, 16121 .float, .comptime_float => false, 16122 else => unreachable, 16123 }; 16124 16125 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 16126 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 16127 16128 if (maybe_lhs_val) |lhs_val| { 16129 if (maybe_rhs_val) |rhs_val| { 16130 const result_val = switch (zir_tag) { 16131 .add, .add_unsafe => try arith.add(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), 16132 .addwrap => try arith.addWrap(sema, resolved_type, lhs_val, rhs_val), 16133 .add_sat => try arith.addSat(sema, resolved_type, lhs_val, rhs_val), 16134 .sub => try arith.sub(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), 16135 .subwrap => try arith.subWrap(sema, resolved_type, lhs_val, rhs_val), 16136 .sub_sat => try arith.subSat(sema, resolved_type, lhs_val, rhs_val), 16137 .mul => try arith.mul(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), 16138 .mulwrap => try arith.mulWrap(sema, resolved_type, lhs_val, rhs_val), 16139 .mul_sat => try arith.mulSat(sema, resolved_type, lhs_val, rhs_val), 16140 else => unreachable, 16141 }; 16142 return Air.internedToRef(result_val.toIntern()); 16143 } 16144 } 16145 16146 const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag, const allow_undef: bool = switch (zir_tag) { 16147 .add, .add_unsafe => .{ if (block.float_mode == .optimized) .add_optimized else .add, .add_safe, !is_int }, 16148 .addwrap => .{ .add_wrap, .add_wrap, true }, 16149 .add_sat => .{ .add_sat, .add_sat, true }, 16150 .sub => .{ if (block.float_mode == .optimized) .sub_optimized else .sub, .sub_safe, !is_int }, 16151 .subwrap => .{ .sub_wrap, .sub_wrap, true }, 16152 .sub_sat => .{ .sub_sat, .sub_sat, true }, 16153 .mul => .{ if (block.float_mode == .optimized) .mul_optimized else .mul, .mul_safe, !is_int }, 16154 .mulwrap => .{ .mul_wrap, .mul_wrap, true }, 16155 .mul_sat => .{ .mul_sat, .mul_sat, true }, 16156 else => unreachable, 16157 }; 16158 16159 if (allow_undef) { 16160 if (maybe_lhs_val) |lhs_val| { 16161 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 16162 } 16163 if (maybe_rhs_val) |rhs_val| { 16164 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); 16165 } 16166 } else { 16167 if (maybe_lhs_val) |lhs_val| { 16168 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); 16169 } 16170 if (maybe_rhs_val) |rhs_val| { 16171 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); 16172 } 16173 } 16174 16175 if (block.wantSafety() and want_safety and scalar_tag == .int) { 16176 if (air_tag != air_tag_safe) try sema.preparePanicId(src, .integer_overflow); 16177 return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs); 16178 } 16179 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 16180 } 16181 16182 fn analyzePtrArithmetic( 16183 sema: *Sema, 16184 block: *Block, 16185 op_src: LazySrcLoc, 16186 ptr: Air.Inst.Ref, 16187 uncasted_offset: Air.Inst.Ref, 16188 air_tag: Air.Inst.Tag, 16189 ptr_src: LazySrcLoc, 16190 offset_src: LazySrcLoc, 16191 ) CompileError!Air.Inst.Ref { 16192 // TODO if the operand is comptime-known to be negative, or is a negative int, 16193 // coerce to isize instead of usize. 16194 const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src); 16195 const pt = sema.pt; 16196 const zcu = pt.zcu; 16197 const opt_ptr_val = try sema.resolveValue(ptr); 16198 const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset); 16199 const ptr_ty = sema.typeOf(ptr); 16200 const ptr_info = ptr_ty.ptrInfo(zcu); 16201 assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c); 16202 16203 if ((try sema.typeHasOnePossibleValue(.fromInterned(ptr_info.child))) != null) { 16204 // Offset will be multiplied by zero, so result is the same as the base pointer. 16205 return ptr; 16206 } 16207 16208 const new_ptr_ty = t: { 16209 // Calculate the new pointer alignment. 16210 // This code is duplicated in `Type.elemPtrType`. 16211 if (ptr_info.flags.alignment == .none) { 16212 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. 16213 break :t ptr_ty; 16214 } 16215 // If the addend is not a comptime-known value we can still count on 16216 // it being a multiple of the type size. 16217 const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt); 16218 const addend = if (opt_off_val) |off_val| a: { 16219 const off_int = try sema.usizeCast(block, offset_src, try off_val.toUnsignedIntSema(pt)); 16220 break :a elem_size * off_int; 16221 } else elem_size; 16222 16223 // The resulting pointer is aligned to the lcd between the offset (an 16224 // arbitrary number) and the alignment factor (always a power of two, 16225 // non zero). 16226 const new_align: Alignment = @enumFromInt(@min( 16227 @ctz(addend), 16228 @intFromEnum(ptr_info.flags.alignment), 16229 )); 16230 assert(new_align != .none); 16231 16232 break :t try pt.ptrTypeSema(.{ 16233 .child = ptr_info.child, 16234 .sentinel = ptr_info.sentinel, 16235 .flags = .{ 16236 .size = ptr_info.flags.size, 16237 .alignment = new_align, 16238 .is_const = ptr_info.flags.is_const, 16239 .is_volatile = ptr_info.flags.is_volatile, 16240 .is_allowzero = ptr_info.flags.is_allowzero, 16241 .address_space = ptr_info.flags.address_space, 16242 }, 16243 }); 16244 }; 16245 16246 const runtime_src = rs: { 16247 if (opt_ptr_val) |ptr_val| { 16248 if (opt_off_val) |offset_val| { 16249 if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty); 16250 16251 const offset_int = try sema.usizeCast(block, offset_src, try offset_val.toUnsignedIntSema(pt)); 16252 if (offset_int == 0) return ptr; 16253 if (air_tag == .ptr_sub) { 16254 const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt); 16255 const new_ptr_val = try sema.ptrSubtract(block, op_src, ptr_val, offset_int * elem_size, new_ptr_ty); 16256 return Air.internedToRef(new_ptr_val.toIntern()); 16257 } else { 16258 const new_ptr_val = try pt.getCoerced(try ptr_val.ptrElem(offset_int, pt), new_ptr_ty); 16259 return Air.internedToRef(new_ptr_val.toIntern()); 16260 } 16261 } else break :rs offset_src; 16262 } else break :rs ptr_src; 16263 }; 16264 16265 try sema.requireRuntimeBlock(block, op_src, runtime_src); 16266 try sema.checkLogicalPtrOperation(block, op_src, ptr_ty); 16267 16268 return block.addInst(.{ 16269 .tag = air_tag, 16270 .data = .{ .ty_pl = .{ 16271 .ty = Air.internedToRef(new_ptr_ty.toIntern()), 16272 .payload = try sema.addExtra(Air.Bin{ 16273 .lhs = ptr, 16274 .rhs = offset, 16275 }), 16276 } }, 16277 }); 16278 } 16279 16280 fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16281 const tracy = trace(@src()); 16282 defer tracy.end(); 16283 16284 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16285 const src = block.nodeOffset(inst_data.src_node); 16286 const ptr_src = src; // TODO better source location 16287 const ptr = try sema.resolveInst(inst_data.operand); 16288 return sema.analyzeLoad(block, src, ptr, ptr_src); 16289 } 16290 16291 fn zirAsm( 16292 sema: *Sema, 16293 block: *Block, 16294 extended: Zir.Inst.Extended.InstData, 16295 tmpl_is_expr: bool, 16296 ) CompileError!Air.Inst.Ref { 16297 const tracy = trace(@src()); 16298 defer tracy.end(); 16299 16300 const pt = sema.pt; 16301 const zcu = pt.zcu; 16302 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); 16303 const src = block.nodeOffset(extra.data.src_node); 16304 const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node }); 16305 const small: Zir.Inst.Asm.Small = @bitCast(extended.small); 16306 const outputs_len = small.outputs_len; 16307 const inputs_len = small.inputs_len; 16308 const is_volatile = small.is_volatile; 16309 const is_global_assembly = sema.func_index == .none; 16310 const zir_tags = sema.code.instructions.items(.tag); 16311 16312 const asm_source: []const u8 = if (tmpl_is_expr) s: { 16313 const tmpl: Zir.Inst.Ref = @enumFromInt(@intFromEnum(extra.data.asm_source)); 16314 break :s try sema.resolveConstString(block, src, tmpl, .{ .simple = .inline_assembly_code }); 16315 } else sema.code.nullTerminatedString(extra.data.asm_source); 16316 16317 if (is_global_assembly) { 16318 if (outputs_len != 0) { 16319 return sema.fail(block, src, "module-level assembly does not support outputs", .{}); 16320 } 16321 if (inputs_len != 0) { 16322 return sema.fail(block, src, "module-level assembly does not support inputs", .{}); 16323 } 16324 if (extra.data.clobbers != .none) { 16325 return sema.fail(block, src, "module-level assembly does not support clobbers", .{}); 16326 } 16327 if (is_volatile) { 16328 return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{}); 16329 } 16330 try zcu.addGlobalAssembly(sema.owner, asm_source); 16331 return .void_value; 16332 } 16333 16334 try sema.requireRuntimeBlock(block, src, null); 16335 16336 var extra_i = extra.end; 16337 var output_type_bits = extra.data.output_type_bits; 16338 var needed_capacity: usize = @typeInfo(Air.Asm).@"struct".fields.len + outputs_len + inputs_len; 16339 16340 const ConstraintName = struct { c: []const u8, n: []const u8 }; 16341 const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len); 16342 const outputs = try sema.arena.alloc(ConstraintName, outputs_len); 16343 var expr_ty = Air.Inst.Ref.void_type; 16344 16345 for (out_args, 0..) |*arg, out_i| { 16346 const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); 16347 extra_i = output.end; 16348 16349 const is_type = @as(u1, @truncate(output_type_bits)) != 0; 16350 output_type_bits >>= 1; 16351 16352 if (is_type) { 16353 // Indicate the output is the asm instruction return value. 16354 arg.* = .none; 16355 const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); 16356 expr_ty = Air.internedToRef(out_ty.toIntern()); 16357 } else { 16358 arg.* = try sema.resolveInst(output.data.operand); 16359 } 16360 16361 const constraint = sema.code.nullTerminatedString(output.data.constraint); 16362 const name = sema.code.nullTerminatedString(output.data.name); 16363 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; 16364 16365 if (output.data.operand.toIndex()) |index| { 16366 if (zir_tags[@intFromEnum(index)] == .ref) { 16367 // TODO: better error location; it would be even nicer if there were notes that pointed at the output and the variable definition 16368 return sema.fail(block, src, "asm cannot output to const local '{s}'", .{name}); 16369 } 16370 } 16371 16372 outputs[out_i] = .{ .c = constraint, .n = name }; 16373 } 16374 16375 const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len); 16376 const inputs = try sema.arena.alloc(ConstraintName, inputs_len); 16377 16378 for (args, 0..) |*arg, arg_i| { 16379 const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); 16380 extra_i = input.end; 16381 16382 const uncasted_arg = try sema.resolveInst(input.data.operand); 16383 const uncasted_arg_ty = sema.typeOf(uncasted_arg); 16384 switch (uncasted_arg_ty.zigTypeTag(zcu)) { 16385 .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src), 16386 .comptime_float => arg.* = try sema.coerce(block, .f64, uncasted_arg, src), 16387 else => { 16388 arg.* = uncasted_arg; 16389 }, 16390 } 16391 16392 const constraint = sema.code.nullTerminatedString(input.data.constraint); 16393 const name = sema.code.nullTerminatedString(input.data.name); 16394 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; 16395 inputs[arg_i] = .{ .c = constraint, .n = name }; 16396 } 16397 16398 const clobbers = if (extra.data.clobbers == .none) empty: { 16399 const clobbers_ty = try sema.getBuiltinType(src, .@"assembly.Clobbers"); 16400 break :empty try sema.structInitEmpty(block, clobbers_ty, src, src); 16401 } else try sema.resolveInst(extra.data.clobbers); // Already coerced by AstGen. 16402 const clobbers_val = try sema.resolveConstDefinedValue(block, src, clobbers, .{ .simple = .clobber }); 16403 needed_capacity += asm_source.len / 4 + 1; 16404 16405 const gpa = sema.gpa; 16406 try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); 16407 const asm_air = try block.addInst(.{ 16408 .tag = .assembly, 16409 .data = .{ .ty_pl = .{ 16410 .ty = expr_ty, 16411 .payload = sema.addExtraAssumeCapacity(Air.Asm{ 16412 .source_len = @intCast(asm_source.len), 16413 .inputs_len = @intCast(args.len), 16414 .clobbers = clobbers_val.toIntern(), 16415 .flags = .{ 16416 .is_volatile = is_volatile, 16417 .outputs_len = outputs_len, 16418 }, 16419 }), 16420 } }, 16421 }); 16422 sema.appendRefsAssumeCapacity(out_args); 16423 sema.appendRefsAssumeCapacity(args); 16424 for (outputs) |o| { 16425 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16426 @memcpy(buffer[0..o.c.len], o.c); 16427 buffer[o.c.len] = 0; 16428 @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n); 16429 buffer[o.c.len + 1 + o.n.len] = 0; 16430 sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4; 16431 } 16432 for (inputs) |input| { 16433 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16434 @memcpy(buffer[0..input.c.len], input.c); 16435 buffer[input.c.len] = 0; 16436 @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n); 16437 buffer[input.c.len + 1 + input.n.len] = 0; 16438 sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; 16439 } 16440 { 16441 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16442 @memcpy(buffer[0..asm_source.len], asm_source); 16443 buffer[asm_source.len] = 0; 16444 sema.air_extra.items.len += asm_source.len / 4 + 1; 16445 } 16446 return asm_air; 16447 } 16448 16449 /// Only called for equality operators. See also `zirCmp`. 16450 fn zirCmpEq( 16451 sema: *Sema, 16452 block: *Block, 16453 inst: Zir.Inst.Index, 16454 op: std.math.CompareOperator, 16455 air_tag: Air.Inst.Tag, 16456 ) CompileError!Air.Inst.Ref { 16457 const tracy = trace(@src()); 16458 defer tracy.end(); 16459 16460 const pt = sema.pt; 16461 const zcu = pt.zcu; 16462 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 16463 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 16464 const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); 16465 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 16466 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 16467 const lhs = try sema.resolveInst(extra.lhs); 16468 const rhs = try sema.resolveInst(extra.rhs); 16469 16470 const lhs_ty = sema.typeOf(lhs); 16471 const rhs_ty = sema.typeOf(rhs); 16472 const lhs_ty_tag = lhs_ty.zigTypeTag(zcu); 16473 const rhs_ty_tag = rhs_ty.zigTypeTag(zcu); 16474 if (lhs_ty_tag == .null and rhs_ty_tag == .null) { 16475 // null == null, null != null 16476 return if (op == .eq) .bool_true else .bool_false; 16477 } 16478 16479 // comparing null with optionals 16480 if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) { 16481 return sema.analyzeIsNull(block, rhs, op == .neq); 16482 } 16483 if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) { 16484 return sema.analyzeIsNull(block, lhs, op == .neq); 16485 } 16486 16487 if (lhs_ty_tag == .null or rhs_ty_tag == .null) { 16488 const non_null_type = if (lhs_ty_tag == .null) rhs_ty else lhs_ty; 16489 return sema.fail(block, src, "comparison of '{f}' with null", .{non_null_type.fmt(pt)}); 16490 } 16491 16492 if (lhs_ty_tag == .@"union" and (rhs_ty_tag == .enum_literal or rhs_ty_tag == .@"enum")) { 16493 return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op); 16494 } 16495 if (rhs_ty_tag == .@"union" and (lhs_ty_tag == .enum_literal or lhs_ty_tag == .@"enum")) { 16496 return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op); 16497 } 16498 16499 if (lhs_ty_tag == .error_set and rhs_ty_tag == .error_set) { 16500 const runtime_src: LazySrcLoc = src: { 16501 if (try sema.resolveValue(lhs)) |lval| { 16502 if (try sema.resolveValue(rhs)) |rval| { 16503 if (lval.isUndef(zcu) or rval.isUndef(zcu)) return .undef_bool; 16504 const lkey = zcu.intern_pool.indexToKey(lval.toIntern()); 16505 const rkey = zcu.intern_pool.indexToKey(rval.toIntern()); 16506 return if ((lkey.err.name == rkey.err.name) == (op == .eq)) 16507 .bool_true 16508 else 16509 .bool_false; 16510 } else { 16511 break :src rhs_src; 16512 } 16513 } else { 16514 break :src lhs_src; 16515 } 16516 }; 16517 try sema.requireRuntimeBlock(block, src, runtime_src); 16518 return block.addBinOp(air_tag, lhs, rhs); 16519 } 16520 if (lhs_ty_tag == .type and rhs_ty_tag == .type) { 16521 const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); 16522 const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); 16523 return if (lhs_as_type.eql(rhs_as_type, zcu) == (op == .eq)) .bool_true else .bool_false; 16524 } 16525 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); 16526 } 16527 16528 fn analyzeCmpUnionTag( 16529 sema: *Sema, 16530 block: *Block, 16531 src: LazySrcLoc, 16532 un: Air.Inst.Ref, 16533 un_src: LazySrcLoc, 16534 tag: Air.Inst.Ref, 16535 tag_src: LazySrcLoc, 16536 op: std.math.CompareOperator, 16537 ) CompileError!Air.Inst.Ref { 16538 const pt = sema.pt; 16539 const zcu = pt.zcu; 16540 const union_ty = sema.typeOf(un); 16541 try union_ty.resolveFields(pt); 16542 const union_tag_ty = union_ty.unionTagType(zcu) orelse { 16543 const msg = msg: { 16544 const msg = try sema.errMsg(un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); 16545 errdefer msg.destroy(sema.gpa); 16546 try sema.errNote(union_ty.srcLoc(zcu), msg, "union '{f}' is not a tagged union", .{union_ty.fmt(pt)}); 16547 break :msg msg; 16548 }; 16549 return sema.failWithOwnedErrorMsg(block, msg); 16550 }; 16551 // Coerce both the union and the tag to the union's tag type, and then execute the 16552 // enum comparison codepath. 16553 const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); 16554 const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); 16555 16556 if (try sema.resolveValue(coerced_tag)) |enum_val| { 16557 if (enum_val.isUndef(zcu)) return .undef_bool; 16558 const field_ty = union_ty.unionFieldType(enum_val, zcu).?; 16559 if (field_ty.zigTypeTag(zcu) == .noreturn) { 16560 return .bool_false; 16561 } 16562 } 16563 16564 return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src); 16565 } 16566 16567 /// Only called for non-equality operators. See also `zirCmpEq`. 16568 fn zirCmp( 16569 sema: *Sema, 16570 block: *Block, 16571 inst: Zir.Inst.Index, 16572 op: std.math.CompareOperator, 16573 ) CompileError!Air.Inst.Ref { 16574 const tracy = trace(@src()); 16575 defer tracy.end(); 16576 16577 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 16578 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 16579 const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); 16580 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 16581 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 16582 const lhs = try sema.resolveInst(extra.lhs); 16583 const rhs = try sema.resolveInst(extra.rhs); 16584 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false); 16585 } 16586 16587 fn analyzeCmp( 16588 sema: *Sema, 16589 block: *Block, 16590 src: LazySrcLoc, 16591 lhs: Air.Inst.Ref, 16592 rhs: Air.Inst.Ref, 16593 op: std.math.CompareOperator, 16594 lhs_src: LazySrcLoc, 16595 rhs_src: LazySrcLoc, 16596 is_equality_cmp: bool, 16597 ) CompileError!Air.Inst.Ref { 16598 const pt = sema.pt; 16599 const zcu = pt.zcu; 16600 const lhs_ty = sema.typeOf(lhs); 16601 const rhs_ty = sema.typeOf(rhs); 16602 if (lhs_ty.zigTypeTag(zcu) != .optional and rhs_ty.zigTypeTag(zcu) != .optional) { 16603 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 16604 } 16605 16606 if (lhs_ty.zigTypeTag(zcu) == .vector and rhs_ty.zigTypeTag(zcu) == .vector) { 16607 return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src); 16608 } 16609 if (lhs_ty.isNumeric(zcu) and rhs_ty.isNumeric(zcu)) { 16610 // This operation allows any combination of integer and float types, regardless of the 16611 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for 16612 // numeric types. 16613 return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); 16614 } 16615 if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_union and rhs_ty.zigTypeTag(zcu) == .error_set) { 16616 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { 16617 if (lhs_val.errorUnionIsPayload(zcu)) return .bool_false; 16618 } 16619 const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs); 16620 return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src); 16621 } 16622 if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_set and rhs_ty.zigTypeTag(zcu) == .error_union) { 16623 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { 16624 if (rhs_val.errorUnionIsPayload(zcu)) return .bool_false; 16625 } 16626 const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs); 16627 return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src); 16628 } 16629 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 16630 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); 16631 if (!resolved_type.isSelfComparable(zcu, is_equality_cmp)) { 16632 return sema.fail(block, src, "operator {s} not allowed for type '{f}'", .{ 16633 compareOperatorName(op), resolved_type.fmt(pt), 16634 }); 16635 } 16636 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 16637 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 16638 return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src); 16639 } 16640 16641 fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { 16642 return switch (comp) { 16643 .lt => "<", 16644 .lte => "<=", 16645 .eq => "==", 16646 .gte => ">=", 16647 .gt => ">", 16648 .neq => "!=", 16649 }; 16650 } 16651 16652 fn cmpSelf( 16653 sema: *Sema, 16654 block: *Block, 16655 src: LazySrcLoc, 16656 casted_lhs: Air.Inst.Ref, 16657 casted_rhs: Air.Inst.Ref, 16658 op: std.math.CompareOperator, 16659 lhs_src: LazySrcLoc, 16660 rhs_src: LazySrcLoc, 16661 ) CompileError!Air.Inst.Ref { 16662 const pt = sema.pt; 16663 const zcu = pt.zcu; 16664 const resolved_type = sema.typeOf(casted_lhs); 16665 16666 const maybe_lhs_val = try sema.resolveValue(casted_lhs); 16667 const maybe_rhs_val = try sema.resolveValue(casted_rhs); 16668 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; 16669 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; 16670 16671 const runtime_src: LazySrcLoc = src: { 16672 if (maybe_lhs_val) |lhs_val| { 16673 if (maybe_rhs_val) |rhs_val| { 16674 if (resolved_type.zigTypeTag(zcu) == .vector) { 16675 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type); 16676 return Air.internedToRef(cmp_val.toIntern()); 16677 } 16678 16679 return if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type)) 16680 .bool_true 16681 else 16682 .bool_false; 16683 } else { 16684 if (resolved_type.zigTypeTag(zcu) == .bool) { 16685 // We can lower bool eq/neq more efficiently. 16686 return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src); 16687 } 16688 break :src rhs_src; 16689 } 16690 } else { 16691 // For bools, we still check the other operand, because we can lower 16692 // bool eq/neq more efficiently. 16693 if (resolved_type.zigTypeTag(zcu) == .bool) { 16694 if (maybe_rhs_val) |rhs_val| { 16695 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src); 16696 } 16697 } 16698 break :src lhs_src; 16699 } 16700 }; 16701 try sema.requireRuntimeBlock(block, src, runtime_src); 16702 if (resolved_type.zigTypeTag(zcu) == .vector) { 16703 return block.addCmpVector(casted_lhs, casted_rhs, op); 16704 } 16705 const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized); 16706 return block.addBinOp(tag, casted_lhs, casted_rhs); 16707 } 16708 16709 /// cmp_eq (x, false) => not(x) 16710 /// cmp_eq (x, true ) => x 16711 /// cmp_neq(x, false) => x 16712 /// cmp_neq(x, true ) => not(x) 16713 fn runtimeBoolCmp( 16714 sema: *Sema, 16715 block: *Block, 16716 src: LazySrcLoc, 16717 op: std.math.CompareOperator, 16718 lhs: Air.Inst.Ref, 16719 rhs: bool, 16720 runtime_src: LazySrcLoc, 16721 ) CompileError!Air.Inst.Ref { 16722 if ((op == .neq) == rhs) { 16723 try sema.requireRuntimeBlock(block, src, runtime_src); 16724 return block.addTyOp(.not, .bool, lhs); 16725 } else { 16726 return lhs; 16727 } 16728 } 16729 16730 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16731 const pt = sema.pt; 16732 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16733 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 16734 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 16735 switch (ty.zigTypeTag(pt.zcu)) { 16736 .@"fn", 16737 .noreturn, 16738 .undefined, 16739 .null, 16740 .@"opaque", 16741 => return sema.fail(block, operand_src, "no size available for type '{f}'", .{ty.fmt(pt)}), 16742 16743 .type, 16744 .enum_literal, 16745 .comptime_float, 16746 .comptime_int, 16747 .void, 16748 => return .zero, 16749 16750 .bool, 16751 .int, 16752 .float, 16753 .pointer, 16754 .array, 16755 .@"struct", 16756 .optional, 16757 .error_union, 16758 .error_set, 16759 .@"enum", 16760 .@"union", 16761 .vector, 16762 .frame, 16763 .@"anyframe", 16764 => {}, 16765 } 16766 const val = try ty.abiSizeLazy(pt); 16767 return Air.internedToRef(val.toIntern()); 16768 } 16769 16770 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16771 const pt = sema.pt; 16772 const zcu = pt.zcu; 16773 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16774 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 16775 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); 16776 switch (operand_ty.zigTypeTag(zcu)) { 16777 .@"fn", 16778 .noreturn, 16779 .undefined, 16780 .null, 16781 .@"opaque", 16782 => return sema.fail(block, operand_src, "no size available for type '{f}'", .{operand_ty.fmt(pt)}), 16783 16784 .type, 16785 .enum_literal, 16786 .comptime_float, 16787 .comptime_int, 16788 .void, 16789 => return .zero, 16790 16791 .bool, 16792 .int, 16793 .float, 16794 .pointer, 16795 .array, 16796 .@"struct", 16797 .optional, 16798 .error_union, 16799 .error_set, 16800 .@"enum", 16801 .@"union", 16802 .vector, 16803 .frame, 16804 .@"anyframe", 16805 => {}, 16806 } 16807 const bit_size = try operand_ty.bitSizeSema(pt); 16808 return pt.intRef(.comptime_int, bit_size); 16809 } 16810 16811 fn zirThis( 16812 sema: *Sema, 16813 block: *Block, 16814 extended: Zir.Inst.Extended.InstData, 16815 ) CompileError!Air.Inst.Ref { 16816 _ = extended; 16817 const pt = sema.pt; 16818 const zcu = pt.zcu; 16819 const namespace = pt.zcu.namespacePtr(block.namespace); 16820 16821 switch (pt.zcu.intern_pool.indexToKey(namespace.owner_type)) { 16822 .opaque_type => { 16823 // Opaque types are never outdated since they don't undergo type resolution, so nothing to do! 16824 return Air.internedToRef(namespace.owner_type); 16825 }, 16826 .struct_type, .union_type => { 16827 const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); 16828 try sema.declareDependency(.{ .interned = new_ty }); 16829 return Air.internedToRef(new_ty); 16830 }, 16831 .enum_type => { 16832 const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); 16833 try sema.declareDependency(.{ .interned = new_ty }); 16834 // Since this is an enum, it has to be resolved immediately. 16835 // `ensureTypeUpToDate` has resolved the new type if necessary. 16836 // We just need to check for resolution failures. 16837 const ty_unit: AnalUnit = .wrap(.{ .type = new_ty }); 16838 if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) { 16839 return error.AnalysisFail; 16840 } 16841 return Air.internedToRef(new_ty); 16842 }, 16843 else => unreachable, 16844 } 16845 } 16846 16847 fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 16848 const pt = sema.pt; 16849 const zcu = pt.zcu; 16850 const ip = &zcu.intern_pool; 16851 const captures = Type.fromInterned(zcu.namespacePtr(block.namespace).owner_type).getCaptures(zcu); 16852 16853 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 16854 const src = block.nodeOffset(src_node); 16855 16856 const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) { 16857 .@"comptime" => |index| return Air.internedToRef(index), 16858 .runtime => |index| index, 16859 .nav_val => |nav| return sema.analyzeNavVal(block, src, nav), 16860 .nav_ref => |nav| return sema.analyzeNavRef(block, src, nav), 16861 }; 16862 16863 // The comptime case is handled already above. Runtime case below. 16864 16865 if (!block.is_typeof and sema.func_index == .none) { 16866 const msg = msg: { 16867 const name = name: { 16868 // TODO: we should probably store this name in the ZIR to avoid this complexity. 16869 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; 16870 const tree = file.getTree(zcu) catch |err| { 16871 // In this case we emit a warning + a less precise source location. 16872 log.warn("unable to load {f}: {s}", .{ 16873 file.path.fmt(zcu.comp), @errorName(err), 16874 }); 16875 break :name null; 16876 }; 16877 const node = src_node.toAbsolute(src_base_node); 16878 const token = tree.nodeMainToken(node); 16879 break :name tree.tokenSlice(token); 16880 }; 16881 16882 const msg = if (name) |some| 16883 try sema.errMsg(src, "'{s}' not accessible outside function scope", .{some}) 16884 else 16885 try sema.errMsg(src, "variable not accessible outside function scope", .{}); 16886 errdefer msg.destroy(sema.gpa); 16887 16888 // TODO add "declared here" note 16889 break :msg msg; 16890 }; 16891 return sema.failWithOwnedErrorMsg(block, msg); 16892 } 16893 16894 if (!block.is_typeof and !block.isComptime() and sema.func_index != .none) { 16895 const msg = msg: { 16896 const name = name: { 16897 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; 16898 const tree = file.getTree(zcu) catch |err| { 16899 // In this case we emit a warning + a less precise source location. 16900 log.warn("unable to load {f}: {s}", .{ 16901 file.path.fmt(zcu.comp), @errorName(err), 16902 }); 16903 break :name null; 16904 }; 16905 const node = src_node.toAbsolute(src_base_node); 16906 const token = tree.nodeMainToken(node); 16907 break :name tree.tokenSlice(token); 16908 }; 16909 16910 const msg = if (name) |some| 16911 try sema.errMsg(src, "'{s}' not accessible from inner function", .{some}) 16912 else 16913 try sema.errMsg(src, "variable not accessible from inner function", .{}); 16914 errdefer msg.destroy(sema.gpa); 16915 16916 try sema.errNote(block.nodeOffset(.zero), msg, "crossed function definition here", .{}); 16917 16918 // TODO add "declared here" note 16919 break :msg msg; 16920 }; 16921 return sema.failWithOwnedErrorMsg(block, msg); 16922 } 16923 16924 assert(block.is_typeof); 16925 // We need a dummy runtime instruction with the correct type. 16926 return block.addTy(.alloc, .fromInterned(capture_ty)); 16927 } 16928 16929 fn zirRetAddr( 16930 sema: *Sema, 16931 block: *Block, 16932 extended: Zir.Inst.Extended.InstData, 16933 ) CompileError!Air.Inst.Ref { 16934 _ = sema; 16935 _ = extended; 16936 if (block.isComptime()) { 16937 // TODO: we could give a meaningful lazy value here. #14938 16938 return .zero_usize; 16939 } else { 16940 return block.addNoOp(.ret_addr); 16941 } 16942 } 16943 16944 fn zirFrameAddress( 16945 sema: *Sema, 16946 block: *Block, 16947 extended: Zir.Inst.Extended.InstData, 16948 ) CompileError!Air.Inst.Ref { 16949 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 16950 const src = block.nodeOffset(src_node); 16951 try sema.requireRuntimeBlock(block, src, null); 16952 return try block.addNoOp(.frame_addr); 16953 } 16954 16955 fn zirBuiltinSrc( 16956 sema: *Sema, 16957 block: *Block, 16958 extended: Zir.Inst.Extended.InstData, 16959 ) CompileError!Air.Inst.Ref { 16960 const tracy = trace(@src()); 16961 defer tracy.end(); 16962 16963 const pt = sema.pt; 16964 const zcu = pt.zcu; 16965 const ip = &zcu.intern_pool; 16966 const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data; 16967 const fn_name = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).name; 16968 const gpa = sema.gpa; 16969 const file_scope = block.getFileScope(zcu); 16970 16971 const func_name_val = v: { 16972 const func_name_len = fn_name.length(ip); 16973 const array_ty = try pt.intern(.{ .array_type = .{ 16974 .len = func_name_len, 16975 .sentinel = .zero_u8, 16976 .child = .u8_type, 16977 } }); 16978 break :v try pt.intern(.{ .slice = .{ 16979 .ty = .slice_const_u8_sentinel_0_type, 16980 .ptr = try pt.intern(.{ .ptr = .{ 16981 .ty = .manyptr_const_u8_sentinel_0_type, 16982 .base_addr = .{ .uav = .{ 16983 .orig_ty = .slice_const_u8_sentinel_0_type, 16984 .val = try pt.intern(.{ .aggregate = .{ 16985 .ty = array_ty, 16986 .storage = .{ .bytes = fn_name.toString() }, 16987 } }), 16988 } }, 16989 .byte_offset = 0, 16990 } }), 16991 .len = (try pt.intValue(.usize, func_name_len)).toIntern(), 16992 } }); 16993 }; 16994 16995 const module_name_val = v: { 16996 const module_name = file_scope.mod.?.fully_qualified_name; 16997 const array_ty = try pt.intern(.{ .array_type = .{ 16998 .len = module_name.len, 16999 .sentinel = .zero_u8, 17000 .child = .u8_type, 17001 } }); 17002 break :v try pt.intern(.{ .slice = .{ 17003 .ty = .slice_const_u8_sentinel_0_type, 17004 .ptr = try pt.intern(.{ .ptr = .{ 17005 .ty = .manyptr_const_u8_sentinel_0_type, 17006 .base_addr = .{ .uav = .{ 17007 .orig_ty = .slice_const_u8_sentinel_0_type, 17008 .val = try pt.intern(.{ .aggregate = .{ 17009 .ty = array_ty, 17010 .storage = .{ 17011 .bytes = try ip.getOrPutString(gpa, pt.tid, module_name, .maybe_embedded_nulls), 17012 }, 17013 } }), 17014 } }, 17015 .byte_offset = 0, 17016 } }), 17017 .len = (try pt.intValue(.usize, module_name.len)).toIntern(), 17018 } }); 17019 }; 17020 17021 const file_name_val = v: { 17022 const file_name = file_scope.sub_file_path; 17023 const array_ty = try pt.intern(.{ .array_type = .{ 17024 .len = file_name.len, 17025 .sentinel = .zero_u8, 17026 .child = .u8_type, 17027 } }); 17028 break :v try pt.intern(.{ .slice = .{ 17029 .ty = .slice_const_u8_sentinel_0_type, 17030 .ptr = try pt.intern(.{ .ptr = .{ 17031 .ty = .manyptr_const_u8_sentinel_0_type, 17032 .base_addr = .{ .uav = .{ 17033 .orig_ty = .slice_const_u8_sentinel_0_type, 17034 .val = try pt.intern(.{ .aggregate = .{ 17035 .ty = array_ty, 17036 .storage = .{ 17037 .bytes = try ip.getOrPutString(gpa, pt.tid, file_name, .maybe_embedded_nulls), 17038 }, 17039 } }), 17040 } }, 17041 .byte_offset = 0, 17042 } }), 17043 .len = (try pt.intValue(.usize, file_name.len)).toIntern(), 17044 } }); 17045 }; 17046 17047 const src_loc_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .SourceLocation); 17048 const fields = .{ 17049 // module: [:0]const u8, 17050 module_name_val, 17051 // file: [:0]const u8, 17052 file_name_val, 17053 // fn_name: [:0]const u8, 17054 func_name_val, 17055 // line: u32, 17056 (try pt.intValue(.u32, extra.line + 1)).toIntern(), 17057 // column: u32, 17058 (try pt.intValue(.u32, extra.column + 1)).toIntern(), 17059 }; 17060 return Air.internedToRef((try pt.aggregateValue(src_loc_ty, &fields)).toIntern()); 17061 } 17062 17063 fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17064 const pt = sema.pt; 17065 const zcu = pt.zcu; 17066 const gpa = sema.gpa; 17067 const ip = &zcu.intern_pool; 17068 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 17069 const src = block.nodeOffset(inst_data.src_node); 17070 const ty = try sema.resolveType(block, src, inst_data.operand); 17071 const type_info_ty = try sema.getBuiltinType(src, .Type); 17072 const type_info_tag_ty = type_info_ty.unionTagType(zcu).?; 17073 17074 if (ty.typeDeclInst(zcu)) |type_decl_inst| { 17075 try sema.declareDependency(.{ .namespace = type_decl_inst }); 17076 } 17077 17078 switch (ty.zigTypeTag(zcu)) { 17079 .type, 17080 .void, 17081 .bool, 17082 .noreturn, 17083 .comptime_float, 17084 .comptime_int, 17085 .undefined, 17086 .null, 17087 .enum_literal, 17088 => |type_info_tag| return unionInitFromEnumTag(sema, block, src, type_info_ty, @intFromEnum(type_info_tag), .void_value), 17089 17090 .@"fn" => { 17091 const fn_info_ty = try sema.getBuiltinType(src, .@"Type.Fn"); 17092 const param_info_ty = try sema.getBuiltinType(src, .@"Type.Fn.Param"); 17093 17094 const func_ty_info = zcu.typeToFunc(ty).?; 17095 const param_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); 17096 for (param_vals, 0..) |*param_val, i| { 17097 const param_ty = func_ty_info.param_types.get(ip)[i]; 17098 const is_generic = param_ty == .generic_poison_type; 17099 const param_ty_val = try pt.intern(.{ .opt = .{ 17100 .ty = try pt.intern(.{ .opt_type = .type_type }), 17101 .val = if (is_generic) .none else param_ty, 17102 } }); 17103 17104 const is_noalias = blk: { 17105 const index = std.math.cast(u5, i) orelse break :blk false; 17106 break :blk @as(u1, @truncate(func_ty_info.noalias_bits >> index)) != 0; 17107 }; 17108 17109 const param_fields = .{ 17110 // is_generic: bool, 17111 Value.makeBool(is_generic).toIntern(), 17112 // is_noalias: bool, 17113 Value.makeBool(is_noalias).toIntern(), 17114 // type: ?type, 17115 param_ty_val, 17116 }; 17117 param_val.* = (try pt.aggregateValue(param_info_ty, ¶m_fields)).toIntern(); 17118 } 17119 17120 const args_val = v: { 17121 const new_decl_ty = try pt.arrayType(.{ 17122 .len = param_vals.len, 17123 .child = param_info_ty.toIntern(), 17124 }); 17125 const new_decl_val = (try pt.aggregateValue(new_decl_ty, param_vals)).toIntern(); 17126 const slice_ty = (try pt.ptrTypeSema(.{ 17127 .child = param_info_ty.toIntern(), 17128 .flags = .{ 17129 .size = .slice, 17130 .is_const = true, 17131 }, 17132 })).toIntern(); 17133 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17134 break :v try pt.intern(.{ .slice = .{ 17135 .ty = slice_ty, 17136 .ptr = try pt.intern(.{ .ptr = .{ 17137 .ty = manyptr_ty, 17138 .base_addr = .{ .uav = .{ 17139 .orig_ty = manyptr_ty, 17140 .val = new_decl_val, 17141 } }, 17142 .byte_offset = 0, 17143 } }), 17144 .len = (try pt.intValue(.usize, param_vals.len)).toIntern(), 17145 } }); 17146 }; 17147 17148 const ret_ty_opt = try pt.intern(.{ .opt = .{ 17149 .ty = try pt.intern(.{ .opt_type = .type_type }), 17150 .val = opt_val: { 17151 const ret_ty: Type = .fromInterned(func_ty_info.return_type); 17152 if (ret_ty.toIntern() == .generic_poison_type) break :opt_val .none; 17153 if (ret_ty.zigTypeTag(zcu) == .error_union) { 17154 if (ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) { 17155 break :opt_val .none; 17156 } 17157 } 17158 break :opt_val ret_ty.toIntern(); 17159 }, 17160 } }); 17161 17162 const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); 17163 const callconv_val = Value.uninterpret(func_ty_info.cc, callconv_ty, pt) catch |err| switch (err) { 17164 error.TypeMismatch => @panic("std.builtin is corrupt"), 17165 error.OutOfMemory => |e| return e, 17166 }; 17167 17168 const field_values: [5]InternPool.Index = .{ 17169 // calling_convention: CallingConvention, 17170 callconv_val.toIntern(), 17171 // is_generic: bool, 17172 Value.makeBool(func_ty_info.is_generic).toIntern(), 17173 // is_var_args: bool, 17174 Value.makeBool(func_ty_info.is_var_args).toIntern(), 17175 // return_type: ?type, 17176 ret_ty_opt, 17177 // args: []const Fn.Param, 17178 args_val, 17179 }; 17180 return Air.internedToRef((try pt.internUnion(.{ 17181 .ty = type_info_ty.toIntern(), 17182 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"fn"))).toIntern(), 17183 .val = (try pt.aggregateValue(fn_info_ty, &field_values)).toIntern(), 17184 }))); 17185 }, 17186 .int => { 17187 const int_info_ty = try sema.getBuiltinType(src, .@"Type.Int"); 17188 const signedness_ty = try sema.getBuiltinType(src, .Signedness); 17189 const info = ty.intInfo(zcu); 17190 const field_values = .{ 17191 // signedness: Signedness, 17192 (try pt.enumValueFieldIndex(signedness_ty, @intFromEnum(info.signedness))).toIntern(), 17193 // bits: u16, 17194 (try pt.intValue(.u16, info.bits)).toIntern(), 17195 }; 17196 return Air.internedToRef((try pt.internUnion(.{ 17197 .ty = type_info_ty.toIntern(), 17198 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.int))).toIntern(), 17199 .val = (try pt.aggregateValue(int_info_ty, &field_values)).toIntern(), 17200 }))); 17201 }, 17202 .float => { 17203 const float_info_ty = try sema.getBuiltinType(src, .@"Type.Float"); 17204 17205 const field_vals = .{ 17206 // bits: u16, 17207 (try pt.intValue(.u16, ty.bitSize(zcu))).toIntern(), 17208 }; 17209 return Air.internedToRef((try pt.internUnion(.{ 17210 .ty = type_info_ty.toIntern(), 17211 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.float))).toIntern(), 17212 .val = (try pt.aggregateValue(float_info_ty, &field_vals)).toIntern(), 17213 }))); 17214 }, 17215 .pointer => { 17216 const info = ty.ptrInfo(zcu); 17217 const alignment = if (info.flags.alignment.toByteUnits()) |alignment| 17218 try pt.intValue(.comptime_int, alignment) 17219 else 17220 try Type.fromInterned(info.child).lazyAbiAlignment(pt); 17221 17222 const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace); 17223 const pointer_ty = try sema.getBuiltinType(src, .@"Type.Pointer"); 17224 const ptr_size_ty = try sema.getBuiltinType(src, .@"Type.Pointer.Size"); 17225 17226 const field_values = .{ 17227 // size: Size, 17228 (try pt.enumValueFieldIndex(ptr_size_ty, @intFromEnum(info.flags.size))).toIntern(), 17229 // is_const: bool, 17230 Value.makeBool(info.flags.is_const).toIntern(), 17231 // is_volatile: bool, 17232 Value.makeBool(info.flags.is_volatile).toIntern(), 17233 // alignment: comptime_int, 17234 alignment.toIntern(), 17235 // address_space: AddressSpace 17236 (try pt.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).toIntern(), 17237 // child: type, 17238 info.child, 17239 // is_allowzero: bool, 17240 Value.makeBool(info.flags.is_allowzero).toIntern(), 17241 // sentinel: ?*const anyopaque, 17242 (try sema.optRefValue(switch (info.sentinel) { 17243 .none => null, 17244 else => Value.fromInterned(info.sentinel), 17245 })).toIntern(), 17246 }; 17247 return Air.internedToRef((try pt.internUnion(.{ 17248 .ty = type_info_ty.toIntern(), 17249 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.pointer))).toIntern(), 17250 .val = (try pt.aggregateValue(pointer_ty, &field_values)).toIntern(), 17251 }))); 17252 }, 17253 .array => { 17254 const array_field_ty = try sema.getBuiltinType(src, .@"Type.Array"); 17255 17256 const info = ty.arrayInfo(zcu); 17257 const field_values = .{ 17258 // len: comptime_int, 17259 (try pt.intValue(.comptime_int, info.len)).toIntern(), 17260 // child: type, 17261 info.elem_type.toIntern(), 17262 // sentinel: ?*const anyopaque, 17263 (try sema.optRefValue(info.sentinel)).toIntern(), 17264 }; 17265 return Air.internedToRef((try pt.internUnion(.{ 17266 .ty = type_info_ty.toIntern(), 17267 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.array))).toIntern(), 17268 .val = (try pt.aggregateValue(array_field_ty, &field_values)).toIntern(), 17269 }))); 17270 }, 17271 .vector => { 17272 const vector_field_ty = try sema.getBuiltinType(src, .@"Type.Vector"); 17273 17274 const info = ty.arrayInfo(zcu); 17275 const field_values = .{ 17276 // len: comptime_int, 17277 (try pt.intValue(.comptime_int, info.len)).toIntern(), 17278 // child: type, 17279 info.elem_type.toIntern(), 17280 }; 17281 return Air.internedToRef((try pt.internUnion(.{ 17282 .ty = type_info_ty.toIntern(), 17283 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.vector))).toIntern(), 17284 .val = (try pt.aggregateValue(vector_field_ty, &field_values)).toIntern(), 17285 }))); 17286 }, 17287 .optional => { 17288 const optional_field_ty = try sema.getBuiltinType(src, .@"Type.Optional"); 17289 17290 const field_values = .{ 17291 // child: type, 17292 ty.optionalChild(zcu).toIntern(), 17293 }; 17294 return Air.internedToRef((try pt.internUnion(.{ 17295 .ty = type_info_ty.toIntern(), 17296 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.optional))).toIntern(), 17297 .val = (try pt.aggregateValue(optional_field_ty, &field_values)).toIntern(), 17298 }))); 17299 }, 17300 .error_set => { 17301 // Get the Error type 17302 const error_field_ty = try sema.getBuiltinType(src, .@"Type.Error"); 17303 17304 // Build our list of Error values 17305 // Optional value is only null if anyerror 17306 // Value can be zero-length slice otherwise 17307 const error_field_vals = switch (try sema.resolveInferredErrorSetTy(block, src, ty.toIntern())) { 17308 .anyerror_type => null, 17309 else => |err_set_ty_index| blk: { 17310 const names = ip.indexToKey(err_set_ty_index).error_set_type.names; 17311 const vals = try sema.arena.alloc(InternPool.Index, names.len); 17312 for (vals, 0..) |*field_val, error_index| { 17313 const error_name = names.get(ip)[error_index]; 17314 const error_name_len = error_name.length(ip); 17315 const error_name_val = v: { 17316 const new_decl_ty = try pt.arrayType(.{ 17317 .len = error_name_len, 17318 .sentinel = .zero_u8, 17319 .child = .u8_type, 17320 }); 17321 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17322 .ty = new_decl_ty.toIntern(), 17323 .storage = .{ .bytes = error_name.toString() }, 17324 } }); 17325 break :v try pt.intern(.{ .slice = .{ 17326 .ty = .slice_const_u8_sentinel_0_type, 17327 .ptr = try pt.intern(.{ .ptr = .{ 17328 .ty = .manyptr_const_u8_sentinel_0_type, 17329 .base_addr = .{ .uav = .{ 17330 .val = new_decl_val, 17331 .orig_ty = .slice_const_u8_sentinel_0_type, 17332 } }, 17333 .byte_offset = 0, 17334 } }), 17335 .len = (try pt.intValue(.usize, error_name_len)).toIntern(), 17336 } }); 17337 }; 17338 17339 const error_field_fields = .{ 17340 // name: [:0]const u8, 17341 error_name_val, 17342 }; 17343 field_val.* = (try pt.aggregateValue(error_field_ty, &error_field_fields)).toIntern(); 17344 } 17345 17346 break :blk vals; 17347 }, 17348 }; 17349 17350 // Build our ?[]const Error value 17351 const slice_errors_ty = try pt.ptrTypeSema(.{ 17352 .child = error_field_ty.toIntern(), 17353 .flags = .{ 17354 .size = .slice, 17355 .is_const = true, 17356 }, 17357 }); 17358 const opt_slice_errors_ty = try pt.optionalType(slice_errors_ty.toIntern()); 17359 const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: { 17360 const array_errors_ty = try pt.arrayType(.{ 17361 .len = vals.len, 17362 .child = error_field_ty.toIntern(), 17363 }); 17364 const new_decl_val = (try pt.aggregateValue(array_errors_ty, vals)).toIntern(); 17365 const manyptr_errors_ty = slice_errors_ty.slicePtrFieldType(zcu).toIntern(); 17366 break :v try pt.intern(.{ .slice = .{ 17367 .ty = slice_errors_ty.toIntern(), 17368 .ptr = try pt.intern(.{ .ptr = .{ 17369 .ty = manyptr_errors_ty, 17370 .base_addr = .{ .uav = .{ 17371 .orig_ty = manyptr_errors_ty, 17372 .val = new_decl_val, 17373 } }, 17374 .byte_offset = 0, 17375 } }), 17376 .len = (try pt.intValue(.usize, vals.len)).toIntern(), 17377 } }); 17378 } else .none; 17379 const errors_val = try pt.intern(.{ .opt = .{ 17380 .ty = opt_slice_errors_ty.toIntern(), 17381 .val = errors_payload_val, 17382 } }); 17383 17384 // Construct Type{ .error_set = errors_val } 17385 return Air.internedToRef((try pt.internUnion(.{ 17386 .ty = type_info_ty.toIntern(), 17387 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.error_set))).toIntern(), 17388 .val = errors_val, 17389 }))); 17390 }, 17391 .error_union => { 17392 const error_union_field_ty = try sema.getBuiltinType(src, .@"Type.ErrorUnion"); 17393 17394 const field_values = .{ 17395 // error_set: type, 17396 ty.errorUnionSet(zcu).toIntern(), 17397 // payload: type, 17398 ty.errorUnionPayload(zcu).toIntern(), 17399 }; 17400 return Air.internedToRef((try pt.internUnion(.{ 17401 .ty = type_info_ty.toIntern(), 17402 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.error_union))).toIntern(), 17403 .val = (try pt.aggregateValue(error_union_field_ty, &field_values)).toIntern(), 17404 }))); 17405 }, 17406 .@"enum" => { 17407 const is_exhaustive = Value.makeBool(ip.loadEnumType(ty.toIntern()).tag_mode != .nonexhaustive); 17408 17409 const enum_field_ty = try sema.getBuiltinType(src, .@"Type.EnumField"); 17410 17411 const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len); 17412 for (enum_field_vals, 0..) |*field_val, tag_index| { 17413 const enum_type = ip.loadEnumType(ty.toIntern()); 17414 const value_val = if (enum_type.values.len > 0) 17415 try ip.getCoercedInts( 17416 zcu.gpa, 17417 pt.tid, 17418 ip.indexToKey(enum_type.values.get(ip)[tag_index]).int, 17419 .comptime_int_type, 17420 ) 17421 else 17422 (try pt.intValue(.comptime_int, tag_index)).toIntern(); 17423 17424 // TODO: write something like getCoercedInts to avoid needing to dupe 17425 const name_val = v: { 17426 const tag_name = enum_type.names.get(ip)[tag_index]; 17427 const tag_name_len = tag_name.length(ip); 17428 const new_decl_ty = try pt.arrayType(.{ 17429 .len = tag_name_len, 17430 .sentinel = .zero_u8, 17431 .child = .u8_type, 17432 }); 17433 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17434 .ty = new_decl_ty.toIntern(), 17435 .storage = .{ .bytes = tag_name.toString() }, 17436 } }); 17437 break :v try pt.intern(.{ .slice = .{ 17438 .ty = .slice_const_u8_sentinel_0_type, 17439 .ptr = try pt.intern(.{ .ptr = .{ 17440 .ty = .manyptr_const_u8_sentinel_0_type, 17441 .base_addr = .{ .uav = .{ 17442 .val = new_decl_val, 17443 .orig_ty = .slice_const_u8_sentinel_0_type, 17444 } }, 17445 .byte_offset = 0, 17446 } }), 17447 .len = (try pt.intValue(.usize, tag_name_len)).toIntern(), 17448 } }); 17449 }; 17450 17451 const enum_field_fields = .{ 17452 // name: [:0]const u8, 17453 name_val, 17454 // value: comptime_int, 17455 value_val, 17456 }; 17457 field_val.* = (try pt.aggregateValue(enum_field_ty, &enum_field_fields)).toIntern(); 17458 } 17459 17460 const fields_val = v: { 17461 const fields_array_ty = try pt.arrayType(.{ 17462 .len = enum_field_vals.len, 17463 .child = enum_field_ty.toIntern(), 17464 }); 17465 const new_decl_val = (try pt.aggregateValue(fields_array_ty, enum_field_vals)).toIntern(); 17466 const slice_ty = (try pt.ptrTypeSema(.{ 17467 .child = enum_field_ty.toIntern(), 17468 .flags = .{ 17469 .size = .slice, 17470 .is_const = true, 17471 }, 17472 })).toIntern(); 17473 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17474 break :v try pt.intern(.{ .slice = .{ 17475 .ty = slice_ty, 17476 .ptr = try pt.intern(.{ .ptr = .{ 17477 .ty = manyptr_ty, 17478 .base_addr = .{ .uav = .{ 17479 .val = new_decl_val, 17480 .orig_ty = manyptr_ty, 17481 } }, 17482 .byte_offset = 0, 17483 } }), 17484 .len = (try pt.intValue(.usize, enum_field_vals.len)).toIntern(), 17485 } }); 17486 }; 17487 17488 const decls_val = try sema.typeInfoDecls(src, ip.loadEnumType(ty.toIntern()).namespace.toOptional()); 17489 17490 const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum"); 17491 17492 const field_values = .{ 17493 // tag_type: type, 17494 ip.loadEnumType(ty.toIntern()).tag_ty, 17495 // fields: []const EnumField, 17496 fields_val, 17497 // decls: []const Declaration, 17498 decls_val, 17499 // is_exhaustive: bool, 17500 is_exhaustive.toIntern(), 17501 }; 17502 return Air.internedToRef((try pt.internUnion(.{ 17503 .ty = type_info_ty.toIntern(), 17504 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"enum"))).toIntern(), 17505 .val = (try pt.aggregateValue(type_enum_ty, &field_values)).toIntern(), 17506 }))); 17507 }, 17508 .@"union" => { 17509 const type_union_ty = try sema.getBuiltinType(src, .@"Type.Union"); 17510 const union_field_ty = try sema.getBuiltinType(src, .@"Type.UnionField"); 17511 17512 try ty.resolveLayout(pt); // Getting alignment requires type layout 17513 const union_obj = zcu.typeToUnion(ty).?; 17514 const tag_type = union_obj.loadTagType(ip); 17515 const layout = union_obj.flagsUnordered(ip).layout; 17516 17517 const union_field_vals = try gpa.alloc(InternPool.Index, tag_type.names.len); 17518 defer gpa.free(union_field_vals); 17519 17520 for (union_field_vals, 0..) |*field_val, field_index| { 17521 const name_val = v: { 17522 const field_name = tag_type.names.get(ip)[field_index]; 17523 const field_name_len = field_name.length(ip); 17524 const new_decl_ty = try pt.arrayType(.{ 17525 .len = field_name_len, 17526 .sentinel = .zero_u8, 17527 .child = .u8_type, 17528 }); 17529 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17530 .ty = new_decl_ty.toIntern(), 17531 .storage = .{ .bytes = field_name.toString() }, 17532 } }); 17533 break :v try pt.intern(.{ .slice = .{ 17534 .ty = .slice_const_u8_sentinel_0_type, 17535 .ptr = try pt.intern(.{ .ptr = .{ 17536 .ty = .manyptr_const_u8_sentinel_0_type, 17537 .base_addr = .{ .uav = .{ 17538 .val = new_decl_val, 17539 .orig_ty = .slice_const_u8_sentinel_0_type, 17540 } }, 17541 .byte_offset = 0, 17542 } }), 17543 .len = (try pt.intValue(.usize, field_name_len)).toIntern(), 17544 } }); 17545 }; 17546 17547 const alignment = switch (layout) { 17548 .auto, .@"extern" => try ty.fieldAlignmentSema(field_index, pt), 17549 .@"packed" => .none, 17550 }; 17551 17552 const field_ty = union_obj.field_types.get(ip)[field_index]; 17553 const union_field_fields = .{ 17554 // name: [:0]const u8, 17555 name_val, 17556 // type: type, 17557 field_ty, 17558 // alignment: comptime_int, 17559 (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(), 17560 }; 17561 field_val.* = (try pt.aggregateValue(union_field_ty, &union_field_fields)).toIntern(); 17562 } 17563 17564 const fields_val = v: { 17565 const array_fields_ty = try pt.arrayType(.{ 17566 .len = union_field_vals.len, 17567 .child = union_field_ty.toIntern(), 17568 }); 17569 const new_decl_val = (try pt.aggregateValue(array_fields_ty, union_field_vals)).toIntern(); 17570 const slice_ty = (try pt.ptrTypeSema(.{ 17571 .child = union_field_ty.toIntern(), 17572 .flags = .{ 17573 .size = .slice, 17574 .is_const = true, 17575 }, 17576 })).toIntern(); 17577 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17578 break :v try pt.intern(.{ .slice = .{ 17579 .ty = slice_ty, 17580 .ptr = try pt.intern(.{ .ptr = .{ 17581 .ty = manyptr_ty, 17582 .base_addr = .{ .uav = .{ 17583 .orig_ty = manyptr_ty, 17584 .val = new_decl_val, 17585 } }, 17586 .byte_offset = 0, 17587 } }), 17588 .len = (try pt.intValue(.usize, union_field_vals.len)).toIntern(), 17589 } }); 17590 }; 17591 17592 const decls_val = try sema.typeInfoDecls(src, ty.getNamespaceIndex(zcu).toOptional()); 17593 17594 const enum_tag_ty_val = try pt.intern(.{ .opt = .{ 17595 .ty = (try pt.optionalType(.type_type)).toIntern(), 17596 .val = if (ty.unionTagType(zcu)) |tag_ty| tag_ty.toIntern() else .none, 17597 } }); 17598 17599 const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout"); 17600 17601 const field_values = .{ 17602 // layout: ContainerLayout, 17603 (try pt.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17604 17605 // tag_type: ?type, 17606 enum_tag_ty_val, 17607 // fields: []const UnionField, 17608 fields_val, 17609 // decls: []const Declaration, 17610 decls_val, 17611 }; 17612 return Air.internedToRef((try pt.internUnion(.{ 17613 .ty = type_info_ty.toIntern(), 17614 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"union"))).toIntern(), 17615 .val = (try pt.aggregateValue(type_union_ty, &field_values)).toIntern(), 17616 }))); 17617 }, 17618 .@"struct" => { 17619 const type_struct_ty = try sema.getBuiltinType(src, .@"Type.Struct"); 17620 const struct_field_ty = try sema.getBuiltinType(src, .@"Type.StructField"); 17621 17622 try ty.resolveLayout(pt); // Getting alignment requires type layout 17623 17624 var struct_field_vals: []InternPool.Index = &.{}; 17625 defer gpa.free(struct_field_vals); 17626 fv: { 17627 const struct_type = switch (ip.indexToKey(ty.toIntern())) { 17628 .tuple_type => |tuple_type| { 17629 struct_field_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len); 17630 for (struct_field_vals, 0..) |*struct_field_val, field_index| { 17631 const field_ty = tuple_type.types.get(ip)[field_index]; 17632 const field_val = tuple_type.values.get(ip)[field_index]; 17633 const name_val = v: { 17634 const field_name = try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); 17635 const field_name_len = field_name.length(ip); 17636 const new_decl_ty = try pt.arrayType(.{ 17637 .len = field_name_len, 17638 .sentinel = .zero_u8, 17639 .child = .u8_type, 17640 }); 17641 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17642 .ty = new_decl_ty.toIntern(), 17643 .storage = .{ .bytes = field_name.toString() }, 17644 } }); 17645 break :v try pt.intern(.{ .slice = .{ 17646 .ty = .slice_const_u8_sentinel_0_type, 17647 .ptr = try pt.intern(.{ .ptr = .{ 17648 .ty = .manyptr_const_u8_sentinel_0_type, 17649 .base_addr = .{ .uav = .{ 17650 .val = new_decl_val, 17651 .orig_ty = .slice_const_u8_sentinel_0_type, 17652 } }, 17653 .byte_offset = 0, 17654 } }), 17655 .len = (try pt.intValue(.usize, field_name_len)).toIntern(), 17656 } }); 17657 }; 17658 17659 try Type.fromInterned(field_ty).resolveLayout(pt); 17660 17661 const is_comptime = field_val != .none; 17662 const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null; 17663 const default_val_ptr = try sema.optRefValue(opt_default_val); 17664 const struct_field_fields = .{ 17665 // name: [:0]const u8, 17666 name_val, 17667 // type: type, 17668 field_ty, 17669 // default_value: ?*const anyopaque, 17670 default_val_ptr.toIntern(), 17671 // is_comptime: bool, 17672 Value.makeBool(is_comptime).toIntern(), 17673 // alignment: comptime_int, 17674 (try pt.intValue(.comptime_int, Type.fromInterned(field_ty).abiAlignment(zcu).toByteUnits() orelse 0)).toIntern(), 17675 }; 17676 struct_field_val.* = (try pt.aggregateValue(struct_field_ty, &struct_field_fields)).toIntern(); 17677 } 17678 break :fv; 17679 }, 17680 .struct_type => ip.loadStructType(ty.toIntern()), 17681 else => unreachable, 17682 }; 17683 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len); 17684 17685 try ty.resolveStructFieldInits(pt); 17686 17687 for (struct_field_vals, 0..) |*field_val, field_index| { 17688 const field_name = if (struct_type.fieldName(ip, field_index).unwrap()) |field_name| 17689 field_name 17690 else 17691 try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); 17692 const field_name_len = field_name.length(ip); 17693 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); 17694 const field_init = struct_type.fieldInit(ip, field_index); 17695 const field_is_comptime = struct_type.fieldIsComptime(ip, field_index); 17696 const name_val = v: { 17697 const new_decl_ty = try pt.arrayType(.{ 17698 .len = field_name_len, 17699 .sentinel = .zero_u8, 17700 .child = .u8_type, 17701 }); 17702 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17703 .ty = new_decl_ty.toIntern(), 17704 .storage = .{ .bytes = field_name.toString() }, 17705 } }); 17706 break :v try pt.intern(.{ .slice = .{ 17707 .ty = .slice_const_u8_sentinel_0_type, 17708 .ptr = try pt.intern(.{ .ptr = .{ 17709 .ty = .manyptr_const_u8_sentinel_0_type, 17710 .base_addr = .{ .uav = .{ 17711 .val = new_decl_val, 17712 .orig_ty = .slice_const_u8_sentinel_0_type, 17713 } }, 17714 .byte_offset = 0, 17715 } }), 17716 .len = (try pt.intValue(.usize, field_name_len)).toIntern(), 17717 } }); 17718 }; 17719 17720 const opt_default_val = if (field_init == .none) null else Value.fromInterned(field_init); 17721 const default_val_ptr = try sema.optRefValue(opt_default_val); 17722 const alignment = switch (struct_type.layout) { 17723 .@"packed" => .none, 17724 else => try field_ty.structFieldAlignmentSema( 17725 struct_type.fieldAlign(ip, field_index), 17726 struct_type.layout, 17727 pt, 17728 ), 17729 }; 17730 17731 const struct_field_fields = .{ 17732 // name: [:0]const u8, 17733 name_val, 17734 // type: type, 17735 field_ty.toIntern(), 17736 // default_value: ?*const anyopaque, 17737 default_val_ptr.toIntern(), 17738 // is_comptime: bool, 17739 Value.makeBool(field_is_comptime).toIntern(), 17740 // alignment: comptime_int, 17741 (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(), 17742 }; 17743 field_val.* = (try pt.aggregateValue(struct_field_ty, &struct_field_fields)).toIntern(); 17744 } 17745 } 17746 17747 const fields_val = v: { 17748 const array_fields_ty = try pt.arrayType(.{ 17749 .len = struct_field_vals.len, 17750 .child = struct_field_ty.toIntern(), 17751 }); 17752 const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_vals)).toIntern(); 17753 const slice_ty = (try pt.ptrTypeSema(.{ 17754 .child = struct_field_ty.toIntern(), 17755 .flags = .{ 17756 .size = .slice, 17757 .is_const = true, 17758 }, 17759 })).toIntern(); 17760 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17761 break :v try pt.intern(.{ .slice = .{ 17762 .ty = slice_ty, 17763 .ptr = try pt.intern(.{ .ptr = .{ 17764 .ty = manyptr_ty, 17765 .base_addr = .{ .uav = .{ 17766 .orig_ty = manyptr_ty, 17767 .val = new_decl_val, 17768 } }, 17769 .byte_offset = 0, 17770 } }), 17771 .len = (try pt.intValue(.usize, struct_field_vals.len)).toIntern(), 17772 } }); 17773 }; 17774 17775 const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu)); 17776 17777 const backing_integer_val = try pt.intern(.{ .opt = .{ 17778 .ty = (try pt.optionalType(.type_type)).toIntern(), 17779 .val = if (zcu.typeToPackedStruct(ty)) |packed_struct| val: { 17780 assert(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)).isInt(zcu)); 17781 break :val packed_struct.backingIntTypeUnordered(ip); 17782 } else .none, 17783 } }); 17784 17785 const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout"); 17786 17787 const layout = ty.containerLayout(zcu); 17788 17789 const field_values = [_]InternPool.Index{ 17790 // layout: ContainerLayout, 17791 (try pt.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17792 // backing_integer: ?type, 17793 backing_integer_val, 17794 // fields: []const StructField, 17795 fields_val, 17796 // decls: []const Declaration, 17797 decls_val, 17798 // is_tuple: bool, 17799 Value.makeBool(ty.isTuple(zcu)).toIntern(), 17800 }; 17801 return Air.internedToRef((try pt.internUnion(.{ 17802 .ty = type_info_ty.toIntern(), 17803 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"struct"))).toIntern(), 17804 .val = (try pt.aggregateValue(type_struct_ty, &field_values)).toIntern(), 17805 }))); 17806 }, 17807 .@"opaque" => { 17808 const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque"); 17809 17810 try ty.resolveFields(pt); 17811 const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu)); 17812 17813 const field_values = .{ 17814 // decls: []const Declaration, 17815 decls_val, 17816 }; 17817 return Air.internedToRef((try pt.internUnion(.{ 17818 .ty = type_info_ty.toIntern(), 17819 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"opaque"))).toIntern(), 17820 .val = (try pt.aggregateValue(type_opaque_ty, &field_values)).toIntern(), 17821 }))); 17822 }, 17823 .frame => return sema.failWithUseOfAsync(block, src), 17824 .@"anyframe" => return sema.failWithUseOfAsync(block, src), 17825 } 17826 } 17827 17828 fn typeInfoDecls( 17829 sema: *Sema, 17830 src: LazySrcLoc, 17831 opt_namespace: InternPool.OptionalNamespaceIndex, 17832 ) CompileError!InternPool.Index { 17833 const pt = sema.pt; 17834 const zcu = pt.zcu; 17835 const gpa = sema.gpa; 17836 17837 const declaration_ty = try sema.getBuiltinType(src, .@"Type.Declaration"); 17838 17839 var decl_vals = std.array_list.Managed(InternPool.Index).init(gpa); 17840 defer decl_vals.deinit(); 17841 17842 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); 17843 defer seen_namespaces.deinit(); 17844 17845 try sema.typeInfoNamespaceDecls(opt_namespace, declaration_ty, &decl_vals, &seen_namespaces); 17846 17847 const array_decl_ty = try pt.arrayType(.{ 17848 .len = decl_vals.items.len, 17849 .child = declaration_ty.toIntern(), 17850 }); 17851 const new_decl_val = (try pt.aggregateValue(array_decl_ty, decl_vals.items)).toIntern(); 17852 const slice_ty = (try pt.ptrTypeSema(.{ 17853 .child = declaration_ty.toIntern(), 17854 .flags = .{ 17855 .size = .slice, 17856 .is_const = true, 17857 }, 17858 })).toIntern(); 17859 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17860 return try pt.intern(.{ .slice = .{ 17861 .ty = slice_ty, 17862 .ptr = try pt.intern(.{ .ptr = .{ 17863 .ty = manyptr_ty, 17864 .base_addr = .{ .uav = .{ 17865 .orig_ty = manyptr_ty, 17866 .val = new_decl_val, 17867 } }, 17868 .byte_offset = 0, 17869 } }), 17870 .len = (try pt.intValue(.usize, decl_vals.items.len)).toIntern(), 17871 } }); 17872 } 17873 17874 fn typeInfoNamespaceDecls( 17875 sema: *Sema, 17876 opt_namespace_index: InternPool.OptionalNamespaceIndex, 17877 declaration_ty: Type, 17878 decl_vals: *std.array_list.Managed(InternPool.Index), 17879 seen_namespaces: *std.AutoHashMap(*Namespace, void), 17880 ) !void { 17881 const pt = sema.pt; 17882 const zcu = pt.zcu; 17883 const ip = &zcu.intern_pool; 17884 17885 const namespace_index = opt_namespace_index.unwrap() orelse return; 17886 try pt.ensureNamespaceUpToDate(namespace_index); 17887 const namespace = zcu.namespacePtr(namespace_index); 17888 17889 const gop = try seen_namespaces.getOrPut(namespace); 17890 if (gop.found_existing) return; 17891 17892 for (namespace.pub_decls.keys()) |nav| { 17893 const name = ip.getNav(nav).name; 17894 const name_val = name_val: { 17895 const name_len = name.length(ip); 17896 const array_ty = try pt.arrayType(.{ 17897 .len = name_len, 17898 .sentinel = .zero_u8, 17899 .child = .u8_type, 17900 }); 17901 const array_val = try pt.intern(.{ .aggregate = .{ 17902 .ty = array_ty.toIntern(), 17903 .storage = .{ .bytes = name.toString() }, 17904 } }); 17905 break :name_val try pt.intern(.{ 17906 .slice = .{ 17907 .ty = .slice_const_u8_sentinel_0_type, // [:0]const u8 17908 .ptr = try pt.intern(.{ 17909 .ptr = .{ 17910 .ty = .manyptr_const_u8_sentinel_0_type, // [*:0]const u8 17911 .base_addr = .{ .uav = .{ 17912 .orig_ty = .slice_const_u8_sentinel_0_type, 17913 .val = array_val, 17914 } }, 17915 .byte_offset = 0, 17916 }, 17917 }), 17918 .len = (try pt.intValue(.usize, name_len)).toIntern(), 17919 }, 17920 }); 17921 }; 17922 const fields = [_]InternPool.Index{ 17923 // name: [:0]const u8, 17924 name_val, 17925 }; 17926 try decl_vals.append((try pt.aggregateValue(declaration_ty, &fields)).toIntern()); 17927 } 17928 } 17929 17930 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17931 _ = block; 17932 const zir_datas = sema.code.instructions.items(.data); 17933 const inst_data = zir_datas[@intFromEnum(inst)].un_node; 17934 const operand = try sema.resolveInst(inst_data.operand); 17935 const operand_ty = sema.typeOf(operand); 17936 return Air.internedToRef(operand_ty.toIntern()); 17937 } 17938 17939 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17940 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 17941 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 17942 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 17943 17944 var child_block: Block = .{ 17945 .parent = block, 17946 .sema = sema, 17947 .namespace = block.namespace, 17948 .instructions = .{}, 17949 .inlining = block.inlining, 17950 .comptime_reason = null, 17951 .is_typeof = true, 17952 .want_safety = false, 17953 .error_return_trace_index = block.error_return_trace_index, 17954 .src_base_inst = block.src_base_inst, 17955 .type_name_ctx = block.type_name_ctx, 17956 }; 17957 defer child_block.instructions.deinit(sema.gpa); 17958 17959 const operand = try sema.resolveInlineBody(&child_block, body, inst); 17960 return Air.internedToRef(sema.typeOf(operand).toIntern()); 17961 } 17962 17963 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17964 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 17965 const src = block.nodeOffset(inst_data.src_node); 17966 const operand = try sema.resolveInst(inst_data.operand); 17967 const operand_ty = sema.typeOf(operand); 17968 const res_ty = try sema.log2IntType(block, operand_ty, src); 17969 return Air.internedToRef(res_ty.toIntern()); 17970 } 17971 17972 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type { 17973 const pt = sema.pt; 17974 const zcu = pt.zcu; 17975 switch (operand.zigTypeTag(zcu)) { 17976 .comptime_int => return .comptime_int, 17977 .int => { 17978 const bits = operand.bitSize(zcu); 17979 const count = if (bits == 0) 17980 0 17981 else blk: { 17982 var count: u16 = 0; 17983 var s = bits - 1; 17984 while (s != 0) : (s >>= 1) { 17985 count += 1; 17986 } 17987 break :blk count; 17988 }; 17989 return pt.intType(.unsigned, count); 17990 }, 17991 .vector => { 17992 const elem_ty = operand.elemType2(zcu); 17993 const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); 17994 return pt.vectorType(.{ 17995 .len = operand.vectorLen(zcu), 17996 .child = log2_elem_ty.toIntern(), 17997 }); 17998 }, 17999 else => {}, 18000 } 18001 return sema.fail( 18002 block, 18003 src, 18004 "bit shifting operation expected integer type, found '{f}'", 18005 .{operand.fmt(pt)}, 18006 ); 18007 } 18008 18009 fn zirTypeofPeer( 18010 sema: *Sema, 18011 block: *Block, 18012 extended: Zir.Inst.Extended.InstData, 18013 inst: Zir.Inst.Index, 18014 ) CompileError!Air.Inst.Ref { 18015 const tracy = trace(@src()); 18016 defer tracy.end(); 18017 18018 const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); 18019 const src = block.nodeOffset(extra.data.src_node); 18020 const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len); 18021 18022 var child_block: Block = .{ 18023 .parent = block, 18024 .sema = sema, 18025 .namespace = block.namespace, 18026 .instructions = .{}, 18027 .inlining = block.inlining, 18028 .comptime_reason = null, 18029 .is_typeof = true, 18030 .runtime_cond = block.runtime_cond, 18031 .runtime_loop = block.runtime_loop, 18032 .runtime_index = block.runtime_index, 18033 .src_base_inst = block.src_base_inst, 18034 .type_name_ctx = block.type_name_ctx, 18035 }; 18036 defer child_block.instructions.deinit(sema.gpa); 18037 // Ignore the result, we only care about the instructions in `args`. 18038 _ = try sema.analyzeInlineBody(&child_block, body, inst); 18039 18040 const args = sema.code.refSlice(extra.end, extended.small); 18041 18042 const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len); 18043 defer sema.gpa.free(inst_list); 18044 18045 for (args, 0..) |arg_ref, i| { 18046 inst_list[i] = try sema.resolveInst(arg_ref); 18047 } 18048 18049 const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); 18050 return Air.internedToRef(result_type.toIntern()); 18051 } 18052 18053 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18054 const pt = sema.pt; 18055 const zcu = pt.zcu; 18056 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18057 const src = block.nodeOffset(inst_data.src_node); 18058 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 18059 const uncasted_operand = try sema.resolveInst(inst_data.operand); 18060 const uncasted_ty = sema.typeOf(uncasted_operand); 18061 if (uncasted_ty.isVector(zcu)) { 18062 if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) { 18063 return sema.fail(block, operand_src, "boolean not operation on type '{f}'", .{ 18064 uncasted_ty.fmt(pt), 18065 }); 18066 } 18067 return analyzeBitNot(sema, block, uncasted_operand, src); 18068 } 18069 const operand = try sema.coerce(block, .bool, uncasted_operand, operand_src); 18070 if (try sema.resolveValue(operand)) |val| { 18071 return if (val.isUndef(zcu)) .undef_bool else if (val.toBool()) .bool_false else .bool_true; 18072 } 18073 try sema.requireRuntimeBlock(block, src, null); 18074 return block.addTyOp(.not, .bool, operand); 18075 } 18076 18077 fn zirBoolBr( 18078 sema: *Sema, 18079 parent_block: *Block, 18080 inst: Zir.Inst.Index, 18081 is_bool_or: bool, 18082 ) CompileError!Air.Inst.Ref { 18083 const tracy = trace(@src()); 18084 defer tracy.end(); 18085 18086 const pt = sema.pt; 18087 const zcu = pt.zcu; 18088 const gpa = sema.gpa; 18089 18090 const datas = sema.code.instructions.items(.data); 18091 const inst_data = datas[@intFromEnum(inst)].pl_node; 18092 const extra = sema.code.extraData(Zir.Inst.BoolBr, inst_data.payload_index); 18093 18094 const uncoerced_lhs = try sema.resolveInst(extra.data.lhs); 18095 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18096 const lhs_src = parent_block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 18097 const rhs_src = parent_block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 18098 18099 const lhs = try sema.coerce(parent_block, .bool, uncoerced_lhs, lhs_src); 18100 18101 if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| { 18102 if (is_bool_or and lhs_val.toBool()) { 18103 return .bool_true; 18104 } else if (!is_bool_or and !lhs_val.toBool()) { 18105 return .bool_false; 18106 } 18107 // comptime-known left-hand side. No need for a block here; the result 18108 // is simply the rhs expression. Here we rely on there only being 1 18109 // break instruction (`break_inline`). 18110 const rhs_result = try sema.resolveInlineBody(parent_block, body, inst); 18111 if (sema.typeOf(rhs_result).isNoReturn(zcu)) { 18112 return rhs_result; 18113 } 18114 return sema.coerce(parent_block, .bool, rhs_result, rhs_src); 18115 } 18116 18117 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 18118 try sema.air_instructions.append(gpa, .{ 18119 .tag = .block, 18120 .data = .{ .ty_pl = .{ 18121 .ty = .bool_type, 18122 .payload = undefined, 18123 } }, 18124 }); 18125 18126 var child_block = parent_block.makeSubBlock(); 18127 child_block.runtime_loop = null; 18128 child_block.runtime_cond = lhs_src; 18129 child_block.runtime_index.increment(); 18130 defer child_block.instructions.deinit(gpa); 18131 18132 var then_block = child_block.makeSubBlock(); 18133 defer then_block.instructions.deinit(gpa); 18134 18135 var else_block = child_block.makeSubBlock(); 18136 defer else_block.instructions.deinit(gpa); 18137 18138 const lhs_block = if (is_bool_or) &then_block else &else_block; 18139 const rhs_block = if (is_bool_or) &else_block else &then_block; 18140 18141 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false; 18142 _ = try lhs_block.addBr(block_inst, lhs_result); 18143 18144 const parent_hint = sema.branch_hint; 18145 defer sema.branch_hint = parent_hint; 18146 sema.branch_hint = null; 18147 18148 const rhs_result = try sema.resolveInlineBody(rhs_block, body, inst); 18149 const rhs_noret = sema.typeOf(rhs_result).isNoReturn(zcu); 18150 const coerced_rhs_result = if (!rhs_noret) rhs: { 18151 const coerced_result = try sema.coerce(rhs_block, .bool, rhs_result, rhs_src); 18152 _ = try rhs_block.addBr(block_inst, coerced_result); 18153 break :rhs coerced_result; 18154 } else rhs_result; 18155 18156 const rhs_hint = sema.branch_hint orelse .none; 18157 18158 const result = try sema.finishCondBr( 18159 parent_block, 18160 &child_block, 18161 &then_block, 18162 &else_block, 18163 lhs, 18164 block_inst, 18165 if (is_bool_or) .{ 18166 .true = .none, 18167 .false = rhs_hint, 18168 .then_cov = .poi, 18169 .else_cov = .poi, 18170 } else .{ 18171 .true = rhs_hint, 18172 .false = .none, 18173 .then_cov = .poi, 18174 .else_cov = .poi, 18175 }, 18176 ); 18177 if (!rhs_noret) { 18178 if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| { 18179 if (is_bool_or and rhs_val.toBool()) { 18180 return .bool_true; 18181 } else if (!is_bool_or and !rhs_val.toBool()) { 18182 return .bool_false; 18183 } 18184 } 18185 } 18186 18187 return result; 18188 } 18189 18190 fn finishCondBr( 18191 sema: *Sema, 18192 parent_block: *Block, 18193 child_block: *Block, 18194 then_block: *Block, 18195 else_block: *Block, 18196 cond: Air.Inst.Ref, 18197 block_inst: Air.Inst.Index, 18198 branch_hints: Air.CondBr.BranchHints, 18199 ) !Air.Inst.Ref { 18200 const gpa = sema.gpa; 18201 18202 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 18203 then_block.instructions.items.len + else_block.instructions.items.len + 18204 @typeInfo(Air.Block).@"struct".fields.len + child_block.instructions.items.len + 1); 18205 18206 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18207 .then_body_len = @intCast(then_block.instructions.items.len), 18208 .else_body_len = @intCast(else_block.instructions.items.len), 18209 .branch_hints = branch_hints, 18210 }); 18211 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 18212 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 18213 18214 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 18215 .operand = cond, 18216 .payload = cond_br_payload, 18217 } } }); 18218 18219 sema.air_instructions.items(.data)[@intFromEnum(block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity( 18220 Air.Block{ .body_len = @intCast(child_block.instructions.items.len) }, 18221 ); 18222 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 18223 18224 try parent_block.instructions.append(gpa, block_inst); 18225 return block_inst.toRef(); 18226 } 18227 18228 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 18229 const pt = sema.pt; 18230 const zcu = pt.zcu; 18231 switch (ty.zigTypeTag(zcu)) { 18232 .optional, .null, .undefined => return, 18233 .pointer => if (ty.isPtrLikeOptional(zcu)) return, 18234 else => {}, 18235 } 18236 return sema.failWithExpectedOptionalType(block, src, ty); 18237 } 18238 18239 fn checkSentinelType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 18240 const pt = sema.pt; 18241 const zcu = pt.zcu; 18242 if (!ty.isSelfComparable(zcu, true)) { 18243 return sema.fail(block, src, "non-scalar sentinel type '{f}'", .{ty.fmt(pt)}); 18244 } 18245 } 18246 18247 fn zirIsNonNull( 18248 sema: *Sema, 18249 block: *Block, 18250 inst: Zir.Inst.Index, 18251 ) CompileError!Air.Inst.Ref { 18252 const tracy = trace(@src()); 18253 defer tracy.end(); 18254 18255 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18256 const src = block.nodeOffset(inst_data.src_node); 18257 const operand = try sema.resolveInst(inst_data.operand); 18258 try sema.checkNullableType(block, src, sema.typeOf(operand)); 18259 return sema.analyzeIsNull(block, operand, true); 18260 } 18261 18262 fn zirIsNonNullPtr( 18263 sema: *Sema, 18264 block: *Block, 18265 inst: Zir.Inst.Index, 18266 ) CompileError!Air.Inst.Ref { 18267 const tracy = trace(@src()); 18268 defer tracy.end(); 18269 18270 const pt = sema.pt; 18271 const zcu = pt.zcu; 18272 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18273 const src = block.nodeOffset(inst_data.src_node); 18274 const ptr = try sema.resolveInst(inst_data.operand); 18275 const ptr_ty = sema.typeOf(ptr); 18276 try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(zcu)); 18277 if (try sema.resolveValue(ptr)) |ptr_val| { 18278 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |loaded_val| { 18279 return sema.analyzeIsNull(block, Air.internedToRef(loaded_val.toIntern()), true); 18280 } 18281 } 18282 if (ptr_ty.childType(zcu).isNullFromType(zcu)) |is_null| { 18283 return if (is_null) .bool_false else .bool_true; 18284 } 18285 return block.addUnOp(.is_non_null_ptr, ptr); 18286 } 18287 18288 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 18289 const pt = sema.pt; 18290 const zcu = pt.zcu; 18291 switch (ty.zigTypeTag(zcu)) { 18292 .error_set, .error_union, .undefined => return, 18293 else => return sema.fail(block, src, "expected error union type, found '{f}'", .{ 18294 ty.fmt(pt), 18295 }), 18296 } 18297 } 18298 18299 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18300 const tracy = trace(@src()); 18301 defer tracy.end(); 18302 18303 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18304 const src = block.nodeOffset(inst_data.src_node); 18305 const operand = try sema.resolveInst(inst_data.operand); 18306 try sema.checkErrorType(block, src, sema.typeOf(operand)); 18307 return sema.analyzeIsNonErr(block, src, operand); 18308 } 18309 18310 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18311 const tracy = trace(@src()); 18312 defer tracy.end(); 18313 18314 const pt = sema.pt; 18315 const zcu = pt.zcu; 18316 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18317 const src = block.nodeOffset(inst_data.src_node); 18318 const ptr = try sema.resolveInst(inst_data.operand); 18319 try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(zcu)); 18320 const loaded = try sema.analyzeLoad(block, src, ptr, src); 18321 return sema.analyzeIsNonErr(block, src, loaded); 18322 } 18323 18324 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18325 const tracy = trace(@src()); 18326 defer tracy.end(); 18327 18328 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18329 const src = block.nodeOffset(inst_data.src_node); 18330 const operand = try sema.resolveInst(inst_data.operand); 18331 return sema.analyzeIsNonErr(block, src, operand); 18332 } 18333 18334 fn zirCondbr( 18335 sema: *Sema, 18336 parent_block: *Block, 18337 inst: Zir.Inst.Index, 18338 ) CompileError!void { 18339 const tracy = trace(@src()); 18340 defer tracy.end(); 18341 18342 const pt = sema.pt; 18343 const zcu = pt.zcu; 18344 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18345 const cond_src = parent_block.src(.{ .node_offset_if_cond = inst_data.src_node }); 18346 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 18347 18348 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); 18349 const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len); 18350 18351 const uncasted_cond = try sema.resolveInst(extra.data.condition); 18352 const cond = try sema.coerce(parent_block, .bool, uncasted_cond, cond_src); 18353 18354 if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| { 18355 const body = if (cond_val.toBool()) then_body else else_body; 18356 18357 // We can propagate `.cold` hints from this branch since it's comptime-known 18358 // to be taken from the parent branch. 18359 const parent_hint = sema.branch_hint; 18360 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; 18361 18362 try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src); 18363 // We use `analyzeBodyInner` since we want to propagate any comptime control flow to the caller. 18364 return sema.analyzeBodyInner(parent_block, body); 18365 } 18366 18367 const gpa = sema.gpa; 18368 18369 // We'll re-use the sub block to save on memory bandwidth, and yank out the 18370 // instructions array in between using it for the then block and else block. 18371 var sub_block = parent_block.makeSubBlock(); 18372 sub_block.runtime_loop = null; 18373 sub_block.runtime_cond = cond_src; 18374 sub_block.runtime_index.increment(); 18375 sub_block.need_debug_scope = null; // this body is emitted regardless 18376 defer sub_block.instructions.deinit(gpa); 18377 18378 const true_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, then_body); 18379 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); 18380 defer gpa.free(true_instructions); 18381 18382 const err_cond = blk: { 18383 const index = extra.data.condition.toIndex() orelse break :blk null; 18384 if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) break :blk null; 18385 18386 const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node; 18387 const err_operand = try sema.resolveInst(err_inst_data.operand); 18388 const operand_ty = sema.typeOf(err_operand); 18389 assert(operand_ty.zigTypeTag(zcu) == .error_union); 18390 const result_ty = operand_ty.errorUnionSet(zcu); 18391 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); 18392 }; 18393 18394 const false_hint: std.builtin.BranchHint = if (err_cond != null and 18395 try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false)) 18396 h: { 18397 // nothing to do here. weight against error branch 18398 break :h .unlikely; 18399 } else try sema.analyzeBodyRuntimeBreak(&sub_block, else_body); 18400 18401 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 18402 true_instructions.len + sub_block.instructions.items.len); 18403 _ = try parent_block.addInst(.{ 18404 .tag = .cond_br, 18405 .data = .{ 18406 .pl_op = .{ 18407 .operand = cond, 18408 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18409 .then_body_len = @intCast(true_instructions.len), 18410 .else_body_len = @intCast(sub_block.instructions.items.len), 18411 .branch_hints = .{ 18412 .true = true_hint, 18413 .false = false_hint, 18414 // Code coverage is desired for error handling. 18415 .then_cov = .poi, 18416 .else_cov = .poi, 18417 }, 18418 }), 18419 }, 18420 }, 18421 }); 18422 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); 18423 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18424 } 18425 18426 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18427 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18428 const src = parent_block.nodeOffset(inst_data.src_node); 18429 const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node }); 18430 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18431 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18432 const err_union = try sema.resolveInst(extra.data.operand); 18433 const err_union_ty = sema.typeOf(err_union); 18434 const pt = sema.pt; 18435 const zcu = pt.zcu; 18436 if (err_union_ty.zigTypeTag(zcu) != .error_union) { 18437 return sema.failWithOwnedErrorMsg(parent_block, msg: { 18438 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); 18439 errdefer msg.destroy(sema.gpa); 18440 try sema.addDeclaredHereNote(msg, err_union_ty); 18441 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); 18442 break :msg msg; 18443 }); 18444 } 18445 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18446 if (is_non_err != .none) { 18447 // We can propagate `.cold` hints from this branch since it's comptime-known 18448 // to be taken from the parent branch. 18449 const parent_hint = sema.branch_hint; 18450 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; 18451 18452 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18453 if (is_non_err_val.toBool()) { 18454 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); 18455 } 18456 // We can analyze the body directly in the parent block because we know there are 18457 // no breaks from the body possible, and that the body is noreturn. 18458 try sema.analyzeBodyInner(parent_block, body); 18459 return .unreachable_value; 18460 } 18461 18462 var sub_block = parent_block.makeSubBlock(); 18463 defer sub_block.instructions.deinit(sema.gpa); 18464 18465 const parent_hint = sema.branch_hint; 18466 defer sema.branch_hint = parent_hint; 18467 18468 // This body is guaranteed to end with noreturn and has no breaks. 18469 try sema.analyzeBodyInner(&sub_block, body); 18470 18471 // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`. 18472 const is_cold = sema.branch_hint == .cold; 18473 18474 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).@"struct".fields.len + 18475 sub_block.instructions.items.len); 18476 const try_inst = try parent_block.addInst(.{ 18477 .tag = if (is_cold) .try_cold else .@"try", 18478 .data = .{ .pl_op = .{ 18479 .operand = err_union, 18480 .payload = sema.addExtraAssumeCapacity(Air.Try{ 18481 .body_len = @intCast(sub_block.instructions.items.len), 18482 }), 18483 } }, 18484 }); 18485 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18486 return try_inst; 18487 } 18488 18489 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18490 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18491 const src = parent_block.nodeOffset(inst_data.src_node); 18492 const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node }); 18493 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18494 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18495 const operand = try sema.resolveInst(extra.data.operand); 18496 const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); 18497 const err_union_ty = sema.typeOf(err_union); 18498 const pt = sema.pt; 18499 const zcu = pt.zcu; 18500 if (err_union_ty.zigTypeTag(zcu) != .error_union) { 18501 return sema.failWithOwnedErrorMsg(parent_block, msg: { 18502 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); 18503 errdefer msg.destroy(sema.gpa); 18504 try sema.addDeclaredHereNote(msg, err_union_ty); 18505 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); 18506 break :msg msg; 18507 }); 18508 } 18509 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18510 if (is_non_err != .none) { 18511 // We can propagate `.cold` hints from this branch since it's comptime-known 18512 // to be taken from the parent branch. 18513 const parent_hint = sema.branch_hint; 18514 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; 18515 18516 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18517 if (is_non_err_val.toBool()) { 18518 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); 18519 } 18520 // We can analyze the body directly in the parent block because we know there are 18521 // no breaks from the body possible, and that the body is noreturn. 18522 try sema.analyzeBodyInner(parent_block, body); 18523 return .unreachable_value; 18524 } 18525 18526 var sub_block = parent_block.makeSubBlock(); 18527 defer sub_block.instructions.deinit(sema.gpa); 18528 18529 const parent_hint = sema.branch_hint; 18530 defer sema.branch_hint = parent_hint; 18531 18532 // This body is guaranteed to end with noreturn and has no breaks. 18533 try sema.analyzeBodyInner(&sub_block, body); 18534 18535 // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`. 18536 const is_cold = sema.branch_hint == .cold; 18537 18538 const operand_ty = sema.typeOf(operand); 18539 const ptr_info = operand_ty.ptrInfo(zcu); 18540 const res_ty = try pt.ptrTypeSema(.{ 18541 .child = err_union_ty.errorUnionPayload(zcu).toIntern(), 18542 .flags = .{ 18543 .is_const = ptr_info.flags.is_const, 18544 .is_volatile = ptr_info.flags.is_volatile, 18545 .is_allowzero = ptr_info.flags.is_allowzero, 18546 .address_space = ptr_info.flags.address_space, 18547 }, 18548 }); 18549 const res_ty_ref = Air.internedToRef(res_ty.toIntern()); 18550 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).@"struct".fields.len + 18551 sub_block.instructions.items.len); 18552 const try_inst = try parent_block.addInst(.{ 18553 .tag = if (is_cold) .try_ptr_cold else .try_ptr, 18554 .data = .{ .ty_pl = .{ 18555 .ty = res_ty_ref, 18556 .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ 18557 .ptr = operand, 18558 .body_len = @intCast(sub_block.instructions.items.len), 18559 }), 18560 } }, 18561 }); 18562 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18563 return try_inst; 18564 } 18565 18566 fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*LabeledBlock { 18567 const gop = sema.inst_map.getOrPutAssumeCapacity(dest_block); 18568 if (gop.found_existing) existing: { 18569 // This may be a *result* from an earlier iteration of an inline loop. 18570 // In this case, there will not be a post-hoc block entry, and we can 18571 // continue with the logic below. 18572 const new_block_inst = gop.value_ptr.*.toIndex() orelse break :existing; 18573 return sema.post_hoc_blocks.get(new_block_inst) orelse break :existing; 18574 } 18575 18576 try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); 18577 18578 const new_block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 18579 gop.value_ptr.* = new_block_inst.toRef(); 18580 try sema.air_instructions.append(sema.gpa, .{ 18581 .tag = .block, 18582 .data = undefined, 18583 }); 18584 const labeled_block = try sema.gpa.create(LabeledBlock); 18585 labeled_block.* = .{ 18586 .label = .{ 18587 .zir_block = dest_block, 18588 .merges = .{ 18589 .src_locs = .{}, 18590 .results = .{}, 18591 .br_list = .{}, 18592 .block_inst = new_block_inst, 18593 }, 18594 }, 18595 .block = .{ 18596 .parent = block, 18597 .sema = sema, 18598 .namespace = block.namespace, 18599 .instructions = .{}, 18600 .label = &labeled_block.label, 18601 .inlining = block.inlining, 18602 .comptime_reason = block.comptime_reason, 18603 .src_base_inst = block.src_base_inst, 18604 .type_name_ctx = block.type_name_ctx, 18605 }, 18606 }; 18607 sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); 18608 return labeled_block; 18609 } 18610 18611 /// A `break` statement is inside a runtime condition, but trying to 18612 /// break from an inline loop. In such case we must convert it to 18613 /// a runtime break. 18614 fn addRuntimeBreak(sema: *Sema, child_block: *Block, block_inst: Zir.Inst.Index, break_operand: Zir.Inst.Ref) !void { 18615 const labeled_block = try sema.ensurePostHoc(child_block, block_inst); 18616 18617 const operand = try sema.resolveInst(break_operand); 18618 const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); 18619 18620 try labeled_block.label.merges.results.append(sema.gpa, operand); 18621 try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); 18622 try labeled_block.label.merges.src_locs.append(sema.gpa, null); 18623 18624 labeled_block.block.runtime_index.increment(); 18625 if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { 18626 labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; 18627 labeled_block.block.runtime_loop = child_block.runtime_loop; 18628 } 18629 } 18630 18631 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18632 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable"; 18633 const src = block.nodeOffset(inst_data.src_node); 18634 18635 if (block.isComptime()) { 18636 return sema.fail(block, src, "reached unreachable code", .{}); 18637 } 18638 // TODO Add compile error for @optimizeFor occurring too late in a scope. 18639 sema.analyzeUnreachable(block, src, true) catch |err| switch (err) { 18640 error.AnalysisFail => { 18641 const msg = sema.err orelse return err; 18642 if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err; 18643 try sema.errNote(src, msg, "the end of a naked function is implicitly unreachable", .{}); 18644 return err; 18645 }, 18646 else => |e| return e, 18647 }; 18648 } 18649 18650 fn zirRetErrValue( 18651 sema: *Sema, 18652 block: *Block, 18653 inst: Zir.Inst.Index, 18654 ) CompileError!void { 18655 const pt = sema.pt; 18656 const zcu = pt.zcu; 18657 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 18658 const src = block.tokenOffset(inst_data.src_tok); 18659 const err_name = try zcu.intern_pool.getOrPutString( 18660 sema.gpa, 18661 pt.tid, 18662 inst_data.get(sema.code), 18663 .no_embedded_nulls, 18664 ); 18665 _ = try pt.getErrorValue(err_name); 18666 // Return the error code from the function. 18667 const error_set_type = try pt.singleErrorSetType(err_name); 18668 const result_inst = Air.internedToRef((try pt.intern(.{ .err = .{ 18669 .ty = error_set_type.toIntern(), 18670 .name = err_name, 18671 } }))); 18672 return sema.analyzeRet(block, result_inst, src, src); 18673 } 18674 18675 fn zirRetImplicit( 18676 sema: *Sema, 18677 block: *Block, 18678 inst: Zir.Inst.Index, 18679 ) CompileError!void { 18680 const tracy = trace(@src()); 18681 defer tracy.end(); 18682 18683 const pt = sema.pt; 18684 const zcu = pt.zcu; 18685 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 18686 const r_brace_src = block.tokenOffset(inst_data.src_tok); 18687 if (block.inlining == null and sema.func_is_naked) { 18688 assert(!block.isComptime()); 18689 if (block.wantSafety()) { 18690 // Calling a safety function from a naked function would not be legal. 18691 _ = try block.addNoOp(.trap); 18692 } else { 18693 try sema.analyzeUnreachable(block, r_brace_src, false); 18694 } 18695 return; 18696 } 18697 18698 const operand = try sema.resolveInst(inst_data.operand); 18699 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = .zero }); 18700 const base_tag = sema.fn_ret_ty.baseZigTypeTag(zcu); 18701 if (base_tag == .noreturn) { 18702 const msg = msg: { 18703 const msg = try sema.errMsg(ret_ty_src, "function declared '{f}' implicitly returns", .{ 18704 sema.fn_ret_ty.fmt(pt), 18705 }); 18706 errdefer msg.destroy(sema.gpa); 18707 try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{}); 18708 break :msg msg; 18709 }; 18710 return sema.failWithOwnedErrorMsg(block, msg); 18711 } else if (base_tag != .void) { 18712 const msg = msg: { 18713 const msg = try sema.errMsg(ret_ty_src, "function with non-void return type '{f}' implicitly returns", .{ 18714 sema.fn_ret_ty.fmt(pt), 18715 }); 18716 errdefer msg.destroy(sema.gpa); 18717 try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{}); 18718 break :msg msg; 18719 }; 18720 return sema.failWithOwnedErrorMsg(block, msg); 18721 } 18722 18723 return sema.analyzeRet(block, operand, r_brace_src, r_brace_src); 18724 } 18725 18726 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18727 const tracy = trace(@src()); 18728 defer tracy.end(); 18729 18730 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18731 const operand = try sema.resolveInst(inst_data.operand); 18732 const src = block.nodeOffset(inst_data.src_node); 18733 18734 return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node })); 18735 } 18736 18737 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18738 const tracy = trace(@src()); 18739 defer tracy.end(); 18740 18741 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18742 const src = block.nodeOffset(inst_data.src_node); 18743 const ret_ptr = try sema.resolveInst(inst_data.operand); 18744 18745 if (block.isComptime() or block.inlining != null or sema.func_is_naked) { 18746 const operand = try sema.analyzeLoad(block, src, ret_ptr, src); 18747 return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node })); 18748 } 18749 18750 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18751 const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); 18752 return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr); 18753 } 18754 18755 _ = try block.addUnOp(.ret_load, ret_ptr); 18756 } 18757 18758 fn retWithErrTracing( 18759 sema: *Sema, 18760 block: *Block, 18761 src: LazySrcLoc, 18762 is_non_err: Air.Inst.Ref, 18763 ret_tag: Air.Inst.Tag, 18764 operand: Air.Inst.Ref, 18765 ) CompileError!void { 18766 const pt = sema.pt; 18767 const need_check = switch (is_non_err) { 18768 .bool_true => { 18769 _ = try block.addUnOp(ret_tag, operand); 18770 return; 18771 }, 18772 .bool_false => false, 18773 else => true, 18774 }; 18775 18776 // This means we're returning something that might be an error! 18777 // This should only be possible with the `auto` cc, so we definitely have an error trace. 18778 assert(pt.zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace); 18779 18780 const gpa = sema.gpa; 18781 const return_err_fn = Air.internedToRef(try sema.getBuiltin(src, .returnError)); 18782 18783 if (!need_check) { 18784 try sema.callBuiltin(block, src, return_err_fn, .never_tail, &.{}, .@"error return"); 18785 _ = try block.addUnOp(ret_tag, operand); 18786 return; 18787 } 18788 18789 var then_block = block.makeSubBlock(); 18790 defer then_block.instructions.deinit(gpa); 18791 _ = try then_block.addUnOp(ret_tag, operand); 18792 18793 var else_block = block.makeSubBlock(); 18794 defer else_block.instructions.deinit(gpa); 18795 try sema.callBuiltin(&else_block, src, return_err_fn, .never_tail, &.{}, .@"error return"); 18796 _ = try else_block.addUnOp(ret_tag, operand); 18797 18798 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 18799 then_block.instructions.items.len + else_block.instructions.items.len + 18800 @typeInfo(Air.Block).@"struct".fields.len + 1); 18801 18802 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18803 .then_body_len = @intCast(then_block.instructions.items.len), 18804 .else_body_len = @intCast(else_block.instructions.items.len), 18805 .branch_hints = .{ 18806 // Weight against error branch. 18807 .true = .likely, 18808 .false = .unlikely, 18809 // Code coverage is not valuable on either branch. 18810 .then_cov = .none, 18811 .else_cov = .none, 18812 }, 18813 }); 18814 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 18815 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 18816 18817 _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 18818 .operand = is_non_err, 18819 .payload = cond_br_payload, 18820 } } }); 18821 } 18822 18823 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { 18824 const pt = sema.pt; 18825 const zcu = pt.zcu; 18826 return fn_ret_ty.isError(zcu) and zcu.comp.config.any_error_tracing; 18827 } 18828 18829 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18830 const pt = sema.pt; 18831 const zcu = pt.zcu; 18832 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index; 18833 18834 if (!block.ownerModule().error_tracing) return; 18835 18836 // This is only relevant at runtime. 18837 if (block.isComptime() or block.is_typeof) return; 18838 18839 const save_index = inst_data.operand == .none or b: { 18840 const operand = try sema.resolveInst(inst_data.operand); 18841 const operand_ty = sema.typeOf(operand); 18842 break :b operand_ty.isError(zcu); 18843 }; 18844 18845 if (save_index) 18846 block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block); 18847 } 18848 18849 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 18850 const extra = sema.code.extraData(Zir.Inst.RestoreErrRetIndex, extended.operand).data; 18851 return sema.restoreErrRetIndex(start_block, start_block.nodeOffset(extra.src_node), extra.block, extra.operand); 18852 } 18853 18854 /// If `operand` is non-error (or is `none`), restores the error return trace to 18855 /// its state at the point `block` was reached (or, if `block` is `none`, the 18856 /// point this function began execution). 18857 fn restoreErrRetIndex(sema: *Sema, start_block: *Block, src: LazySrcLoc, target_block: Zir.Inst.Ref, operand_zir: Zir.Inst.Ref) CompileError!void { 18858 const tracy = trace(@src()); 18859 defer tracy.end(); 18860 18861 const pt = sema.pt; 18862 const zcu = pt.zcu; 18863 18864 const saved_index = if (target_block.toIndexAllowNone()) |zir_block| b: { 18865 var block = start_block; 18866 while (true) { 18867 if (block.label) |label| { 18868 if (label.zir_block == zir_block) { 18869 const target_trace_index = if (block.parent) |parent_block| tgt: { 18870 break :tgt parent_block.error_return_trace_index; 18871 } else sema.error_return_trace_index_on_fn_entry; 18872 18873 if (start_block.error_return_trace_index != target_trace_index) 18874 break :b target_trace_index; 18875 18876 return; // No need to restore 18877 } 18878 } 18879 block = block.parent.?; 18880 } 18881 } else b: { 18882 if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry) 18883 break :b sema.error_return_trace_index_on_fn_entry; 18884 18885 return; // No need to restore 18886 }; 18887 18888 const operand = try sema.resolveInstAllowNone(operand_zir); 18889 18890 if (start_block.isComptime() or start_block.is_typeof) { 18891 const is_non_error = if (operand != .none) blk: { 18892 const is_non_error_inst = try sema.analyzeIsNonErr(start_block, src, operand); 18893 const cond_val = try sema.resolveDefinedValue(start_block, src, is_non_error_inst); 18894 break :blk cond_val.?.toBool(); 18895 } else true; // no operand means pop unconditionally 18896 18897 if (is_non_error) return; 18898 18899 const saved_index_val = try sema.resolveDefinedValue(start_block, src, saved_index); 18900 const saved_index_int = saved_index_val.?.toUnsignedInt(zcu); 18901 assert(saved_index_int <= sema.comptime_err_ret_trace.items.len); 18902 sema.comptime_err_ret_trace.items.len = @intCast(saved_index_int); 18903 return; 18904 } 18905 18906 if (!zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace) return; 18907 if (!start_block.ownerModule().error_tracing) return; 18908 18909 assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere 18910 18911 return sema.popErrorReturnTrace(start_block, src, operand, saved_index); 18912 } 18913 18914 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { 18915 const pt = sema.pt; 18916 const zcu = pt.zcu; 18917 const ip = &zcu.intern_pool; 18918 assert(sema.fn_ret_ty.zigTypeTag(zcu) == .error_union); 18919 const err_set_ty = sema.fn_ret_ty.errorUnionSet(zcu).toIntern(); 18920 switch (err_set_ty) { 18921 .adhoc_inferred_error_set_type => { 18922 const ies = sema.fn_ret_ty_ies.?; 18923 assert(ies.func == .none); 18924 try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand)); 18925 }, 18926 else => if (ip.isInferredErrorSetType(err_set_ty)) { 18927 const ies = sema.fn_ret_ty_ies.?; 18928 assert(ies.func == sema.owner.unwrap().func); 18929 try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand)); 18930 }, 18931 } 18932 } 18933 18934 fn addToInferredErrorSetPtr(sema: *Sema, ies: *InferredErrorSet, op_ty: Type) !void { 18935 const arena = sema.arena; 18936 const pt = sema.pt; 18937 const zcu = pt.zcu; 18938 const ip = &zcu.intern_pool; 18939 switch (op_ty.zigTypeTag(zcu)) { 18940 .error_set => try ies.addErrorSet(op_ty, ip, arena), 18941 .error_union => try ies.addErrorSet(op_ty.errorUnionSet(zcu), ip, arena), 18942 else => {}, 18943 } 18944 } 18945 18946 fn analyzeRet( 18947 sema: *Sema, 18948 block: *Block, 18949 uncasted_operand: Air.Inst.Ref, 18950 src: LazySrcLoc, 18951 operand_src: LazySrcLoc, 18952 ) CompileError!void { 18953 // Special case for returning an error to an inferred error set; we need to 18954 // add the error tag to the inferred error set of the in-scope function, so 18955 // that the coercion below works correctly. 18956 const pt = sema.pt; 18957 const zcu = pt.zcu; 18958 if (sema.fn_ret_ty_ies != null and sema.fn_ret_ty.zigTypeTag(zcu) == .error_union) { 18959 try sema.addToInferredErrorSet(uncasted_operand); 18960 } 18961 const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, operand_src, .{ .is_ret = true }) catch |err| switch (err) { 18962 error.NotCoercible => unreachable, 18963 else => |e| return e, 18964 }; 18965 18966 if (block.inlining) |inlining| { 18967 assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr 18968 if (block.isComptime()) { 18969 const ret_val = try sema.resolveConstValue(block, operand_src, operand, null); 18970 inlining.comptime_result = operand; 18971 18972 if (sema.fn_ret_ty.isError(zcu) and ret_val.getErrorName(zcu) != .none) { 18973 try sema.comptime_err_ret_trace.append(src); 18974 } 18975 return error.ComptimeReturn; 18976 } 18977 // We are inlining a function call; rewrite the `ret` as a `break`. 18978 const br_inst = try block.addBr(inlining.merges.block_inst, operand); 18979 try inlining.merges.results.append(sema.gpa, operand); 18980 try inlining.merges.br_list.append(sema.gpa, br_inst.toIndex().?); 18981 try inlining.merges.src_locs.append(sema.gpa, operand_src); 18982 return; 18983 } else if (block.isComptime()) { 18984 return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{}); 18985 } else if (sema.func_is_naked) { 18986 const msg = msg: { 18987 const msg = try sema.errMsg(src, "cannot return from naked function", .{}); 18988 errdefer msg.destroy(sema.gpa); 18989 18990 try sema.errNote(src, msg, "can only return using assembly", .{}); 18991 break :msg msg; 18992 }; 18993 return sema.failWithOwnedErrorMsg(block, msg); 18994 } 18995 18996 try sema.fn_ret_ty.resolveLayout(pt); 18997 18998 try sema.validateRuntimeValue(block, operand_src, operand); 18999 19000 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret; 19001 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 19002 // Avoid adding a frame to the error return trace in case the value is comptime-known 19003 // to be not an error. 19004 const is_non_err = try sema.analyzeIsNonErr(block, operand_src, operand); 19005 return sema.retWithErrTracing(block, src, is_non_err, air_tag, operand); 19006 } 19007 19008 _ = try block.addUnOp(air_tag, operand); 19009 } 19010 19011 fn floatOpAllowed(tag: Zir.Inst.Tag) bool { 19012 // extend this swich as additional operators are implemented 19013 return switch (tag) { 19014 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true, 19015 else => false, 19016 }; 19017 } 19018 19019 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19020 const tracy = trace(@src()); 19021 defer tracy.end(); 19022 19023 const pt = sema.pt; 19024 const zcu = pt.zcu; 19025 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type; 19026 const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); 19027 const elem_ty_src = block.src(.{ .node_offset_ptr_elem = extra.data.src_node }); 19028 const sentinel_src = block.src(.{ .node_offset_ptr_sentinel = extra.data.src_node }); 19029 const align_src = block.src(.{ .node_offset_ptr_align = extra.data.src_node }); 19030 const addrspace_src = block.src(.{ .node_offset_ptr_addrspace = extra.data.src_node }); 19031 const bitoffset_src = block.src(.{ .node_offset_ptr_bitoffset = extra.data.src_node }); 19032 const hostsize_src = block.src(.{ .node_offset_ptr_hostsize = extra.data.src_node }); 19033 19034 const elem_ty = blk: { 19035 const air_inst = try sema.resolveInst(extra.data.elem_type); 19036 const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| { 19037 if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(zcu)) { 19038 try sema.errNote(elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{}); 19039 } 19040 return err; 19041 }; 19042 assert(!ty.isGenericPoison()); 19043 break :blk ty; 19044 }; 19045 19046 if (elem_ty.zigTypeTag(zcu) == .noreturn) 19047 return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); 19048 19049 const target = zcu.getTarget(); 19050 19051 var extra_i = extra.end; 19052 19053 const sentinel = if (inst_data.flags.has_sentinel) blk: { 19054 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 19055 extra_i += 1; 19056 const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src); 19057 const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ .simple = .pointer_sentinel }); 19058 try checkSentinelType(sema, block, sentinel_src, elem_ty); 19059 break :blk val.toIntern(); 19060 } else .none; 19061 19062 const abi_align: Alignment = if (inst_data.flags.has_align) blk: { 19063 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 19064 extra_i += 1; 19065 const coerced = try sema.coerce(block, align_ty, try sema.resolveInst(ref), align_src); 19066 const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" }); 19067 // Check if this happens to be the lazy alignment of our element type, in 19068 // which case we can make this 0 without resolving it. 19069 switch (zcu.intern_pool.indexToKey(val.toIntern())) { 19070 .int => |int| switch (int.storage) { 19071 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none, 19072 else => {}, 19073 }, 19074 else => {}, 19075 } 19076 const align_bytes = (try val.getUnsignedIntSema(pt)).?; 19077 break :blk try sema.validateAlign(block, align_src, align_bytes); 19078 } else .none; 19079 19080 const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: { 19081 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 19082 extra_i += 1; 19083 break :blk try sema.resolveAddressSpace(block, addrspace_src, ref, .pointer); 19084 } else if (elem_ty.zigTypeTag(zcu) == .@"fn" and target.cpu.arch == .avr) .flash else .generic; 19085 19086 const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: { 19087 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 19088 extra_i += 1; 19089 const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, .u16, .{ .simple = .type }); 19090 break :blk @intCast(bit_offset); 19091 } else 0; 19092 19093 const host_size: u16 = if (inst_data.flags.has_bit_range) blk: { 19094 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 19095 extra_i += 1; 19096 const host_size = try sema.resolveInt(block, hostsize_src, ref, .u16, .{ .simple = .type }); 19097 break :blk @intCast(host_size); 19098 } else 0; 19099 19100 if (host_size != 0) { 19101 if (bit_offset >= host_size * 8) { 19102 return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} starts {d} bits after the end of a {d} byte host integer", .{ 19103 elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size, 19104 }); 19105 } 19106 const elem_bit_size = try elem_ty.bitSizeSema(pt); 19107 if (elem_bit_size > host_size * 8 - bit_offset) { 19108 return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{ 19109 elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size, 19110 }); 19111 } 19112 } 19113 19114 if (elem_ty.zigTypeTag(zcu) == .@"fn") { 19115 if (inst_data.size != .one) { 19116 return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); 19117 } 19118 } else if (inst_data.size == .many and elem_ty.zigTypeTag(zcu) == .@"opaque") { 19119 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); 19120 } else if (inst_data.size == .c) { 19121 if (!try sema.validateExternType(elem_ty, .other)) { 19122 const msg = msg: { 19123 const msg = try sema.errMsg(elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)}); 19124 errdefer msg.destroy(sema.gpa); 19125 19126 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other); 19127 19128 try sema.addDeclaredHereNote(msg, elem_ty); 19129 break :msg msg; 19130 }; 19131 return sema.failWithOwnedErrorMsg(block, msg); 19132 } 19133 if (elem_ty.zigTypeTag(zcu) == .@"opaque") { 19134 return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{}); 19135 } 19136 } 19137 19138 if (host_size != 0 and !try sema.validatePackedType(elem_ty)) { 19139 return sema.failWithOwnedErrorMsg(block, msg: { 19140 const msg = try sema.errMsg(elem_ty_src, "bit-pointer cannot refer to value of type '{f}'", .{elem_ty.fmt(pt)}); 19141 errdefer msg.destroy(sema.gpa); 19142 try sema.explainWhyTypeIsNotPacked(msg, elem_ty_src, elem_ty); 19143 break :msg msg; 19144 }); 19145 } 19146 19147 const ty = try pt.ptrTypeSema(.{ 19148 .child = elem_ty.toIntern(), 19149 .sentinel = sentinel, 19150 .flags = .{ 19151 .alignment = abi_align, 19152 .address_space = address_space, 19153 .is_const = !inst_data.flags.is_mutable, 19154 .is_allowzero = inst_data.flags.is_allowzero, 19155 .is_volatile = inst_data.flags.is_volatile, 19156 .size = inst_data.size, 19157 }, 19158 .packed_offset = .{ 19159 .bit_offset = bit_offset, 19160 .host_size = host_size, 19161 }, 19162 }); 19163 return Air.internedToRef(ty.toIntern()); 19164 } 19165 19166 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19167 const tracy = trace(@src()); 19168 defer tracy.end(); 19169 19170 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 19171 const src = block.nodeOffset(inst_data.src_node); 19172 const ty_src = block.src(.{ .node_offset_init_ty = inst_data.src_node }); 19173 const obj_ty = try sema.resolveType(block, ty_src, inst_data.operand); 19174 const pt = sema.pt; 19175 const zcu = pt.zcu; 19176 19177 switch (obj_ty.zigTypeTag(zcu)) { 19178 .@"struct" => return sema.structInitEmpty(block, obj_ty, src, src), 19179 .array, .vector => return sema.arrayInitEmpty(block, src, obj_ty), 19180 .void => return Air.internedToRef(Value.void.toIntern()), 19181 .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}), 19182 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), 19183 } 19184 } 19185 19186 fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref { 19187 const tracy = trace(@src()); 19188 defer tracy.end(); 19189 19190 const pt = sema.pt; 19191 const zcu = pt.zcu; 19192 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 19193 const src = block.nodeOffset(inst_data.src_node); 19194 19195 // Generic poison means this is an untyped anonymous empty struct/array init 19196 const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse { 19197 if (is_byref) { 19198 return sema.uavRef(.empty_tuple); 19199 } else { 19200 return .empty_tuple; 19201 } 19202 }; 19203 19204 const init_ty = if (is_byref) ty: { 19205 const ptr_ty = ty_operand.optEuBaseType(zcu); 19206 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction 19207 switch (ptr_ty.ptrSize(zcu)) { 19208 // Use a zero-length array for a slice or many-ptr result 19209 .slice, .many => break :ty try pt.arrayType(.{ 19210 .len = 0, 19211 .child = ptr_ty.childType(zcu).toIntern(), 19212 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 19213 }), 19214 // Just use the child type for a single-pointer or C-pointer result 19215 .one, .c => { 19216 const child = ptr_ty.childType(zcu); 19217 if (child.toIntern() == .anyopaque_type) { 19218 // ...unless that child is anyopaque, in which case this is equivalent to an untyped init. 19219 // `.{}` is an empty tuple. 19220 if (is_byref) { 19221 return sema.uavRef(.empty_tuple); 19222 } else { 19223 return .empty_tuple; 19224 } 19225 } 19226 break :ty child; 19227 }, 19228 } 19229 if (!ptr_ty.isSlice(zcu)) { 19230 break :ty ptr_ty.childType(zcu); 19231 } 19232 // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`. 19233 break :ty try pt.arrayType(.{ 19234 .len = 0, 19235 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 19236 .child = ptr_ty.childType(zcu).toIntern(), 19237 }); 19238 } else ty_operand; 19239 const obj_ty = init_ty.optEuBaseType(zcu); 19240 19241 const empty_ref = switch (obj_ty.zigTypeTag(zcu)) { 19242 .@"struct" => try sema.structInitEmpty(block, obj_ty, src, src), 19243 .array, .vector => try sema.arrayInitEmpty(block, src, obj_ty), 19244 .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}), 19245 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), 19246 }; 19247 const init_ref = try sema.coerce(block, init_ty, empty_ref, src); 19248 19249 if (is_byref) { 19250 const init_val = (try sema.resolveValue(init_ref)).?; 19251 return sema.uavRef(init_val.toIntern()); 19252 } else { 19253 return init_ref; 19254 } 19255 } 19256 19257 fn structInitEmpty( 19258 sema: *Sema, 19259 block: *Block, 19260 struct_ty: Type, 19261 dest_src: LazySrcLoc, 19262 init_src: LazySrcLoc, 19263 ) CompileError!Air.Inst.Ref { 19264 const pt = sema.pt; 19265 const zcu = pt.zcu; 19266 const gpa = sema.gpa; 19267 // This logic must be synchronized with that in `zirStructInit`. 19268 try struct_ty.resolveFields(pt); 19269 19270 // The init values to use for the struct instance. 19271 const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(zcu)); 19272 defer gpa.free(field_inits); 19273 @memset(field_inits, .none); 19274 19275 // Maps field index in the struct declaration to the field index in the initialization expression. 19276 const field_assign_idxs = try gpa.alloc(?usize, struct_ty.structFieldCount(zcu)); 19277 defer gpa.free(field_assign_idxs); 19278 @memset(field_assign_idxs, null); 19279 19280 return sema.finishStructInit(block, init_src, dest_src, field_inits, field_assign_idxs, struct_ty, struct_ty, false); 19281 } 19282 19283 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { 19284 const pt = sema.pt; 19285 const zcu = pt.zcu; 19286 const arr_len = obj_ty.arrayLen(zcu); 19287 if (arr_len != 0) { 19288 if (obj_ty.zigTypeTag(zcu) == .array) { 19289 return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len}); 19290 } else { 19291 return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len}); 19292 } 19293 } 19294 return .fromValue(try pt.aggregateValue(obj_ty, &.{})); 19295 } 19296 19297 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19298 const pt = sema.pt; 19299 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19300 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 19301 const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); 19302 const init_src = block.builtinCallArgSrc(inst_data.src_node, 2); 19303 const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; 19304 const union_ty = try sema.resolveType(block, ty_src, extra.union_type); 19305 if (union_ty.zigTypeTag(pt.zcu) != .@"union") { 19306 return sema.fail(block, ty_src, "expected union type, found '{f}'", .{union_ty.fmt(pt)}); 19307 } 19308 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_name }); 19309 const init = try sema.resolveInst(extra.init); 19310 return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); 19311 } 19312 19313 fn unionInit( 19314 sema: *Sema, 19315 block: *Block, 19316 uncasted_init: Air.Inst.Ref, 19317 init_src: LazySrcLoc, 19318 union_ty: Type, 19319 union_ty_src: LazySrcLoc, 19320 field_name: InternPool.NullTerminatedString, 19321 field_src: LazySrcLoc, 19322 ) CompileError!Air.Inst.Ref { 19323 const pt = sema.pt; 19324 const zcu = pt.zcu; 19325 const ip = &zcu.intern_pool; 19326 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); 19327 const field_ty: Type = .fromInterned(zcu.typeToUnion(union_ty).?.field_types.get(ip)[field_index]); 19328 const init = try sema.coerce(block, field_ty, uncasted_init, init_src); 19329 _ = union_ty_src; 19330 return unionInitFromEnumTag(sema, block, init_src, union_ty, field_index, init); 19331 } 19332 19333 fn unionInitFromEnumTag( 19334 sema: *Sema, 19335 block: *Block, 19336 init_src: LazySrcLoc, 19337 union_ty: Type, 19338 field_index: u32, 19339 init: Air.Inst.Ref, 19340 ) !Air.Inst.Ref { 19341 const pt = sema.pt; 19342 const zcu = pt.zcu; 19343 19344 if (try sema.resolveValue(init)) |init_val| { 19345 const tag_ty = union_ty.unionTagTypeHypothetical(zcu); 19346 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); 19347 return Air.internedToRef((try pt.internUnion(.{ 19348 .ty = union_ty.toIntern(), 19349 .tag = tag_val.toIntern(), 19350 .val = init_val.toIntern(), 19351 }))); 19352 } 19353 19354 try sema.requireRuntimeBlock(block, init_src, null); 19355 return block.addUnionInit(union_ty, field_index, init); 19356 } 19357 19358 fn zirStructInit( 19359 sema: *Sema, 19360 block: *Block, 19361 inst: Zir.Inst.Index, 19362 is_ref: bool, 19363 ) CompileError!Air.Inst.Ref { 19364 const gpa = sema.gpa; 19365 const zir_datas = sema.code.instructions.items(.data); 19366 const inst_data = zir_datas[@intFromEnum(inst)].pl_node; 19367 const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); 19368 const src = block.nodeOffset(inst_data.src_node); 19369 19370 const pt = sema.pt; 19371 const zcu = pt.zcu; 19372 const ip = &zcu.intern_pool; 19373 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; 19374 const first_field_type_data = zir_datas[@intFromEnum(first_item.field_type)].pl_node; 19375 const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; 19376 const result_ty = try sema.resolveTypeOrPoison(block, src, first_field_type_extra.container_type) orelse { 19377 // The type wasn't actually known, so treat this as an anon struct init. 19378 return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref); 19379 }; 19380 const resolved_ty = result_ty.optEuBaseType(zcu); 19381 try resolved_ty.resolveLayout(pt); 19382 19383 if (resolved_ty.zigTypeTag(zcu) == .@"struct") { 19384 // This logic must be synchronized with that in `zirStructInitEmpty`. 19385 19386 // Maps field index to field_type index of where it was already initialized. 19387 // For making sure all fields are accounted for and no fields are duplicated. 19388 const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(zcu)); 19389 defer gpa.free(found_fields); 19390 19391 // The init values to use for the struct instance. 19392 const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(zcu)); 19393 defer gpa.free(field_inits); 19394 @memset(field_inits, .none); 19395 19396 // Maps field index in the struct declaration to the field index in the initialization expression. 19397 const field_assign_idxs = try gpa.alloc(?usize, resolved_ty.structFieldCount(zcu)); 19398 defer gpa.free(field_assign_idxs); 19399 @memset(field_assign_idxs, null); 19400 19401 var field_i: u32 = 0; 19402 var extra_index = extra.end; 19403 19404 const is_packed = resolved_ty.containerLayout(zcu) == .@"packed"; 19405 while (field_i < extra.data.fields_len) : (field_i += 1) { 19406 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); 19407 extra_index = item.end; 19408 19409 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19410 const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node }); 19411 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 19412 const field_name = try ip.getOrPutString( 19413 gpa, 19414 pt.tid, 19415 sema.code.nullTerminatedString(field_type_extra.name_start), 19416 .no_embedded_nulls, 19417 ); 19418 const field_index = if (resolved_ty.isTuple(zcu)) 19419 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) 19420 else 19421 try sema.structFieldIndex(block, resolved_ty, field_name, field_src); 19422 assert(field_inits[field_index] == .none); 19423 field_assign_idxs[field_index] = field_i; 19424 found_fields[field_index] = item.data.field_type; 19425 const uncoerced_init = try sema.resolveInst(item.data.init); 19426 const field_ty = resolved_ty.fieldType(field_index, zcu); 19427 field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src); 19428 if (!is_packed) { 19429 try resolved_ty.resolveStructFieldInits(pt); 19430 if (try resolved_ty.structFieldValueComptime(pt, field_index)) |default_value| { 19431 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse { 19432 return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); 19433 }; 19434 19435 if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) { 19436 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); 19437 } 19438 } 19439 } 19440 } 19441 19442 return sema.finishStructInit(block, src, src, field_inits, field_assign_idxs, resolved_ty, result_ty, is_ref); 19443 } else if (resolved_ty.zigTypeTag(zcu) == .@"union") { 19444 if (extra.data.fields_len != 1) { 19445 return sema.fail(block, src, "union initialization expects exactly one field", .{}); 19446 } 19447 19448 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end); 19449 19450 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19451 const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node }); 19452 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 19453 const field_name = try ip.getOrPutString( 19454 gpa, 19455 pt.tid, 19456 sema.code.nullTerminatedString(field_type_extra.name_start), 19457 .no_embedded_nulls, 19458 ); 19459 const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); 19460 const tag_ty = resolved_ty.unionTagTypeHypothetical(zcu); 19461 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); 19462 const field_ty: Type = .fromInterned(zcu.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]); 19463 19464 if (field_ty.zigTypeTag(zcu) == .noreturn) { 19465 return sema.failWithOwnedErrorMsg(block, msg: { 19466 const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{}); 19467 errdefer msg.destroy(sema.gpa); 19468 19469 try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{f}' declared here", .{ 19470 field_name.fmt(ip), 19471 }); 19472 try sema.addDeclaredHereNote(msg, resolved_ty); 19473 break :msg msg; 19474 }); 19475 } 19476 19477 const uncoerced_init_inst = try sema.resolveInst(item.data.init); 19478 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src); 19479 19480 if (try sema.resolveValue(init_inst)) |val| { 19481 const struct_val = Value.fromInterned(try pt.internUnion(.{ 19482 .ty = resolved_ty.toIntern(), 19483 .tag = tag_val.toIntern(), 19484 .val = val.toIntern(), 19485 })); 19486 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src); 19487 const final_val = (try sema.resolveValue(final_val_inst)).?; 19488 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref); 19489 } 19490 19491 if (try resolved_ty.comptimeOnlySema(pt)) { 19492 return sema.failWithNeededComptime(block, field_src, .{ .comptime_only = .{ 19493 .ty = resolved_ty, 19494 .msg = .union_init, 19495 } }); 19496 } 19497 19498 try sema.validateRuntimeValue(block, field_src, init_inst); 19499 19500 if (is_ref) { 19501 const target = zcu.getTarget(); 19502 const alloc_ty = try pt.ptrTypeSema(.{ 19503 .child = result_ty.toIntern(), 19504 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19505 }); 19506 const alloc = try block.addTy(.alloc, alloc_ty); 19507 const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); 19508 const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true); 19509 try sema.storePtr(block, src, field_ptr, init_inst); 19510 if ((try sema.typeHasOnePossibleValue(tag_ty)) == null) { 19511 const new_tag = Air.internedToRef(tag_val.toIntern()); 19512 _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag); 19513 } 19514 return sema.makePtrConst(block, alloc); 19515 } 19516 19517 try sema.requireRuntimeBlock(block, src, null); 19518 const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst); 19519 return sema.coerce(block, result_ty, union_val, src); 19520 } 19521 unreachable; 19522 } 19523 19524 fn finishStructInit( 19525 sema: *Sema, 19526 block: *Block, 19527 init_src: LazySrcLoc, 19528 dest_src: LazySrcLoc, 19529 field_inits: []Air.Inst.Ref, 19530 field_assign_idxs: []?usize, 19531 struct_ty: Type, 19532 result_ty: Type, 19533 is_ref: bool, 19534 ) CompileError!Air.Inst.Ref { 19535 const pt = sema.pt; 19536 const zcu = pt.zcu; 19537 const ip = &zcu.intern_pool; 19538 19539 var root_msg: ?*Zcu.ErrorMsg = null; 19540 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 19541 19542 switch (ip.indexToKey(struct_ty.toIntern())) { 19543 .tuple_type => |tuple| { 19544 // We can't get the slices, as the coercion may invalidate them. 19545 for (0..tuple.types.len) |i| { 19546 if (field_inits[i] != .none) { 19547 // Coerce the init value to the field type. 19548 const field_src = block.src(.{ .init_elem = .{ 19549 .init_node_offset = init_src.offset.node_offset.x, 19550 .elem_index = @intCast(i), 19551 } }); 19552 const field_ty: Type = .fromInterned(tuple.types.get(ip)[i]); 19553 field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src); 19554 continue; 19555 } 19556 19557 const default_val = tuple.values.get(ip)[i]; 19558 19559 if (default_val == .none) { 19560 const template = "missing tuple field with index {d}"; 19561 if (root_msg) |msg| { 19562 try sema.errNote(init_src, msg, template, .{i}); 19563 } else { 19564 root_msg = try sema.errMsg(init_src, template, .{i}); 19565 } 19566 } else { 19567 field_inits[i] = Air.internedToRef(default_val); 19568 } 19569 } 19570 }, 19571 .struct_type => { 19572 const struct_type = ip.loadStructType(struct_ty.toIntern()); 19573 for (0..struct_type.field_types.len) |i| { 19574 if (field_inits[i] != .none) { 19575 // Coerce the init value to the field type. 19576 const field_src = block.src(.{ .init_elem = .{ 19577 .init_node_offset = init_src.offset.node_offset.x, 19578 .elem_index = @intCast(i), 19579 } }); 19580 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 19581 field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src); 19582 continue; 19583 } 19584 19585 try struct_ty.resolveStructFieldInits(pt); 19586 19587 const field_init = struct_type.fieldInit(ip, i); 19588 if (field_init == .none) { 19589 const field_name = struct_type.field_names.get(ip)[i]; 19590 const template = "missing struct field: {f}"; 19591 const args = .{field_name.fmt(ip)}; 19592 if (root_msg) |msg| { 19593 try sema.errNote(init_src, msg, template, args); 19594 } else { 19595 root_msg = try sema.errMsg(init_src, template, args); 19596 } 19597 } else { 19598 field_inits[i] = Air.internedToRef(field_init); 19599 } 19600 } 19601 }, 19602 else => unreachable, 19603 } 19604 19605 if (root_msg) |msg| { 19606 try sema.addDeclaredHereNote(msg, struct_ty); 19607 root_msg = null; 19608 return sema.failWithOwnedErrorMsg(block, msg); 19609 } 19610 19611 // Find which field forces the expression to be runtime, if any. 19612 const opt_runtime_index = for (field_inits, field_assign_idxs) |field_init, field_assign| { 19613 if (!(try sema.isComptimeKnown(field_init))) { 19614 break field_assign; 19615 } 19616 } else null; 19617 19618 const runtime_index = opt_runtime_index orelse { 19619 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); 19620 for (elems, field_inits) |*elem, field_init| { 19621 elem.* = (sema.resolveValue(field_init) catch unreachable).?.toIntern(); 19622 } 19623 const struct_val = try pt.aggregateValue(struct_ty, elems); 19624 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), init_src); 19625 const final_val = (try sema.resolveValue(final_val_inst)).?; 19626 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref); 19627 }; 19628 19629 if (try struct_ty.comptimeOnlySema(pt)) { 19630 return sema.failWithNeededComptime(block, block.src(.{ .init_elem = .{ 19631 .init_node_offset = init_src.offset.node_offset.x, 19632 .elem_index = @intCast(runtime_index), 19633 } }), .{ .comptime_only = .{ 19634 .ty = struct_ty, 19635 .msg = .struct_init, 19636 } }); 19637 } 19638 19639 for (field_inits) |field_init| { 19640 try sema.validateRuntimeValue(block, dest_src, field_init); 19641 } 19642 19643 if (is_ref) { 19644 try struct_ty.resolveLayout(pt); 19645 const target = zcu.getTarget(); 19646 const alloc_ty = try pt.ptrTypeSema(.{ 19647 .child = result_ty.toIntern(), 19648 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19649 }); 19650 const alloc = try block.addTy(.alloc, alloc_ty); 19651 const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src); 19652 for (field_inits, 0..) |field_init, i_usize| { 19653 const i: u32 = @intCast(i_usize); 19654 const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, struct_ty); 19655 try sema.storePtr(block, dest_src, field_ptr, field_init); 19656 } 19657 19658 return sema.makePtrConst(block, alloc); 19659 } 19660 19661 try sema.requireRuntimeBlock(block, dest_src, block.src(.{ .init_elem = .{ 19662 .init_node_offset = init_src.offset.node_offset.x, 19663 .elem_index = @intCast(runtime_index), 19664 } })); 19665 try struct_ty.resolveStructFieldInits(pt); 19666 const struct_val = try block.addAggregateInit(struct_ty, field_inits); 19667 return sema.coerce(block, result_ty, struct_val, init_src); 19668 } 19669 19670 fn zirStructInitAnon( 19671 sema: *Sema, 19672 block: *Block, 19673 inst: Zir.Inst.Index, 19674 ) CompileError!Air.Inst.Ref { 19675 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19676 const src = block.nodeOffset(inst_data.src_node); 19677 const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); 19678 return sema.structInitAnon(block, src, inst, .anon_init, extra.data, extra.end, false); 19679 } 19680 19681 fn structInitAnon( 19682 sema: *Sema, 19683 block: *Block, 19684 src: LazySrcLoc, 19685 inst: Zir.Inst.Index, 19686 /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a 19687 /// generic poison type. In this case, we need to know to interpret the extra data differently. 19688 comptime kind: enum { anon_init, typed_init }, 19689 extra_data: switch (kind) { 19690 .anon_init => Zir.Inst.StructInitAnon, 19691 .typed_init => Zir.Inst.StructInit, 19692 }, 19693 extra_end: usize, 19694 is_ref: bool, 19695 ) CompileError!Air.Inst.Ref { 19696 const pt = sema.pt; 19697 const zcu = pt.zcu; 19698 const gpa = sema.gpa; 19699 const ip = &zcu.intern_pool; 19700 const zir_datas = sema.code.instructions.items(.data); 19701 19702 const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len); 19703 const values = try sema.arena.alloc(InternPool.Index, types.len); 19704 const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len); 19705 19706 var any_values = false; 19707 19708 // Find which field forces the expression to be runtime, if any. 19709 const opt_runtime_index = rs: { 19710 var runtime_index: ?usize = null; 19711 var extra_index = extra_end; 19712 for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| { 19713 const item = switch (kind) { 19714 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19715 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19716 }; 19717 extra_index = item.end; 19718 19719 const name = switch (kind) { 19720 .anon_init => sema.code.nullTerminatedString(item.data.field_name), 19721 .typed_init => name: { 19722 // `item.data.field_type` references a `field_type` instruction 19723 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19724 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index); 19725 break :name sema.code.nullTerminatedString(field_type_extra.data.name_start); 19726 }, 19727 }; 19728 19729 field_name.* = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); 19730 19731 const init = try sema.resolveInst(item.data.init); 19732 field_ty.* = sema.typeOf(init).toIntern(); 19733 if (Type.fromInterned(field_ty.*).zigTypeTag(zcu) == .@"opaque") { 19734 const msg = msg: { 19735 const field_src = block.src(.{ .init_elem = .{ 19736 .init_node_offset = src.offset.node_offset.x, 19737 .elem_index = @intCast(i_usize), 19738 } }); 19739 const msg = try sema.errMsg(field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19740 errdefer msg.destroy(sema.gpa); 19741 19742 try sema.addDeclaredHereNote(msg, .fromInterned(field_ty.*)); 19743 break :msg msg; 19744 }; 19745 return sema.failWithOwnedErrorMsg(block, msg); 19746 } 19747 if (try sema.resolveValue(init)) |init_val| { 19748 field_val.* = init_val.toIntern(); 19749 any_values = true; 19750 } else { 19751 field_val.* = .none; 19752 runtime_index = @intCast(i_usize); 19753 } 19754 } 19755 break :rs runtime_index; 19756 }; 19757 19758 // We treat anonymous struct types as reified types, because there are similarities: 19759 // * They use a form of structural equivalence, which we can easily model using a custom hash 19760 // * They do not have captures 19761 // * They immediately have their fields resolved 19762 // In general, other code should treat anon struct types and reified struct types identically, 19763 // so there's no point having a separate `InternPool.NamespaceType` field for them. 19764 const type_hash: u64 = hash: { 19765 var hasher = std.hash.Wyhash.init(0); 19766 hasher.update(std.mem.sliceAsBytes(types)); 19767 hasher.update(std.mem.sliceAsBytes(values)); 19768 hasher.update(std.mem.sliceAsBytes(names)); 19769 break :hash hasher.final(); 19770 }; 19771 const tracked_inst = try block.trackZir(inst); 19772 const struct_ty = switch (try ip.getStructType(gpa, pt.tid, .{ 19773 .layout = .auto, 19774 .fields_len = extra_data.fields_len, 19775 .known_non_opv = false, 19776 .requires_comptime = .unknown, 19777 .any_comptime_fields = any_values, 19778 .any_default_inits = any_values, 19779 .inits_resolved = true, 19780 .any_aligned_fields = false, 19781 .key = .{ .reified = .{ 19782 .zir_index = tracked_inst, 19783 .type_hash = type_hash, 19784 } }, 19785 }, false)) { 19786 .wip => |wip| ty: { 19787 errdefer wip.cancel(ip, pt.tid); 19788 const type_name = try sema.createTypeName(block, .anon, "struct", inst, wip.index); 19789 wip.setName(ip, type_name.name, type_name.nav); 19790 19791 const struct_type = ip.loadStructType(wip.index); 19792 19793 for (names, values, 0..) |name, init_val, field_idx| { 19794 assert(struct_type.addFieldName(ip, name) == null); 19795 if (init_val != .none) struct_type.setFieldComptime(ip, field_idx); 19796 } 19797 19798 @memcpy(struct_type.field_types.get(ip), types); 19799 if (any_values) { 19800 @memcpy(struct_type.field_inits.get(ip), values); 19801 } 19802 19803 const new_namespace_index = try pt.createNamespace(.{ 19804 .parent = block.namespace.toOptional(), 19805 .owner_type = wip.index, 19806 .file_scope = block.getFileScopeIndex(zcu), 19807 .generation = zcu.generation, 19808 }); 19809 try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index }); 19810 codegen_type: { 19811 if (zcu.comp.config.use_llvm) break :codegen_type; 19812 if (block.ownerModule().strip) break :codegen_type; 19813 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 19814 try zcu.comp.queueJob(.{ .link_type = wip.index }); 19815 } 19816 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); 19817 break :ty wip.finish(ip, new_namespace_index); 19818 }, 19819 .existing => |ty| ty, 19820 }; 19821 try sema.declareDependency(.{ .interned = struct_ty }); 19822 try sema.addTypeReferenceEntry(src, struct_ty); 19823 19824 _ = opt_runtime_index orelse { 19825 const struct_val = try pt.aggregateValue(.fromInterned(struct_ty), values); 19826 return sema.addConstantMaybeRef(struct_val.toIntern(), is_ref); 19827 }; 19828 19829 if (is_ref) { 19830 const target = zcu.getTarget(); 19831 const alloc_ty = try pt.ptrTypeSema(.{ 19832 .child = struct_ty, 19833 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19834 }); 19835 const alloc = try block.addTy(.alloc, alloc_ty); 19836 var extra_index = extra_end; 19837 for (types, 0..) |field_ty, i_usize| { 19838 const i: u32 = @intCast(i_usize); 19839 const item = switch (kind) { 19840 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19841 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19842 }; 19843 extra_index = item.end; 19844 19845 const field_ptr_ty = try pt.ptrTypeSema(.{ 19846 .child = field_ty, 19847 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19848 }); 19849 if (values[i] == .none) { 19850 const init = try sema.resolveInst(item.data.init); 19851 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19852 _ = try block.addBinOp(.store, field_ptr, init); 19853 } 19854 } 19855 19856 return sema.makePtrConst(block, alloc); 19857 } 19858 19859 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); 19860 var extra_index = extra_end; 19861 for (types, 0..) |_, i| { 19862 const item = switch (kind) { 19863 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19864 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19865 }; 19866 extra_index = item.end; 19867 element_refs[i] = try sema.resolveInst(item.data.init); 19868 } 19869 19870 return block.addAggregateInit(.fromInterned(struct_ty), element_refs); 19871 } 19872 19873 fn zirArrayInit( 19874 sema: *Sema, 19875 block: *Block, 19876 inst: Zir.Inst.Index, 19877 is_ref: bool, 19878 ) CompileError!Air.Inst.Ref { 19879 const pt = sema.pt; 19880 const zcu = pt.zcu; 19881 const gpa = sema.gpa; 19882 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19883 const src = block.nodeOffset(inst_data.src_node); 19884 19885 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19886 const args = sema.code.refSlice(extra.end, extra.data.operands_len); 19887 assert(args.len >= 2); // array_ty + at least one element 19888 19889 const result_ty = try sema.resolveTypeOrPoison(block, src, args[0]) orelse { 19890 // The type wasn't actually known, so treat this as an anon array init. 19891 return sema.arrayInitAnon(block, src, args[1..], is_ref); 19892 }; 19893 const array_ty = result_ty.optEuBaseType(zcu); 19894 const is_tuple = array_ty.zigTypeTag(zcu) == .@"struct"; 19895 const sentinel_val = array_ty.sentinel(zcu); 19896 19897 var root_msg: ?*Zcu.ErrorMsg = null; 19898 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 19899 19900 const final_len = try sema.usizeCast(block, src, array_ty.arrayLenIncludingSentinel(zcu)); 19901 const resolved_args = try gpa.alloc(Air.Inst.Ref, final_len); 19902 defer gpa.free(resolved_args); 19903 for (resolved_args, 0..) |*dest, i| { 19904 const elem_src = block.src(.{ .init_elem = .{ 19905 .init_node_offset = src.offset.node_offset.x, 19906 .elem_index = @intCast(i), 19907 } }); 19908 // Less inits than needed. 19909 if (i + 2 > args.len) if (is_tuple) { 19910 const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern(); 19911 if (default_val == .unreachable_value) { 19912 const template = "missing tuple field with index {d}"; 19913 if (root_msg) |msg| { 19914 try sema.errNote(src, msg, template, .{i}); 19915 } else { 19916 root_msg = try sema.errMsg(src, template, .{i}); 19917 } 19918 } else { 19919 dest.* = Air.internedToRef(default_val); 19920 } 19921 continue; 19922 } else { 19923 dest.* = Air.internedToRef(sentinel_val.?.toIntern()); 19924 break; 19925 }; 19926 19927 const arg = args[i + 1]; 19928 const resolved_arg = try sema.resolveInst(arg); 19929 const elem_ty = if (is_tuple) 19930 array_ty.fieldType(i, zcu) 19931 else 19932 array_ty.elemType2(zcu); 19933 dest.* = try sema.coerce(block, elem_ty, resolved_arg, elem_src); 19934 if (is_tuple) { 19935 if (array_ty.structFieldIsComptime(i, zcu)) 19936 try array_ty.resolveStructFieldInits(pt); 19937 if (try array_ty.structFieldValueComptime(pt, i)) |field_val| { 19938 const init_val = try sema.resolveConstValue(block, elem_src, dest.*, .{ .simple = .stored_to_comptime_field }); 19939 if (!field_val.eql(init_val, elem_ty, zcu)) { 19940 return sema.failWithInvalidComptimeFieldStore(block, elem_src, array_ty, i); 19941 } 19942 } 19943 } 19944 } 19945 19946 if (root_msg) |msg| { 19947 try sema.addDeclaredHereNote(msg, array_ty); 19948 root_msg = null; 19949 return sema.failWithOwnedErrorMsg(block, msg); 19950 } 19951 19952 const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| { 19953 const comptime_known = try sema.isComptimeKnown(arg); 19954 if (!comptime_known) break @intCast(i); 19955 } else null; 19956 19957 _ = opt_runtime_index orelse { 19958 const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len); 19959 for (elem_vals, resolved_args) |*val, arg| { 19960 // We checked that all args are comptime above. 19961 val.* = (sema.resolveValue(arg) catch unreachable).?.toIntern(); 19962 } 19963 const arr_val = try pt.aggregateValue(array_ty, elem_vals); 19964 const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val.toIntern()), src); 19965 const result_val = (try sema.resolveValue(result_ref)).?; 19966 return sema.addConstantMaybeRef(result_val.toIntern(), is_ref); 19967 }; 19968 19969 if (is_ref) { 19970 const target = zcu.getTarget(); 19971 const alloc_ty = try pt.ptrTypeSema(.{ 19972 .child = result_ty.toIntern(), 19973 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19974 }); 19975 const alloc = try block.addTy(.alloc, alloc_ty); 19976 const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); 19977 19978 if (is_tuple) { 19979 for (resolved_args, 0..) |arg, i| { 19980 const elem_ptr_ty = try pt.ptrTypeSema(.{ 19981 .child = array_ty.fieldType(i, zcu).toIntern(), 19982 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19983 }); 19984 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 19985 19986 const index = try pt.intRef(.usize, i); 19987 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref); 19988 _ = try block.addBinOp(.store, elem_ptr, arg); 19989 } 19990 return sema.makePtrConst(block, alloc); 19991 } 19992 19993 const elem_ptr_ty = try pt.ptrTypeSema(.{ 19994 .child = array_ty.elemType2(zcu).toIntern(), 19995 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19996 }); 19997 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 19998 19999 for (resolved_args, 0..) |arg, i| { 20000 const index = try pt.intRef(.usize, i); 20001 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref); 20002 _ = try block.addBinOp(.store, elem_ptr, arg); 20003 } 20004 return sema.makePtrConst(block, alloc); 20005 } 20006 20007 const arr_ref = try block.addAggregateInit(array_ty, resolved_args); 20008 return sema.coerce(block, result_ty, arr_ref, src); 20009 } 20010 20011 fn zirArrayInitAnon( 20012 sema: *Sema, 20013 block: *Block, 20014 inst: Zir.Inst.Index, 20015 ) CompileError!Air.Inst.Ref { 20016 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 20017 const src = block.nodeOffset(inst_data.src_node); 20018 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 20019 const operands = sema.code.refSlice(extra.end, extra.data.operands_len); 20020 return sema.arrayInitAnon(block, src, operands, false); 20021 } 20022 20023 fn arrayInitAnon( 20024 sema: *Sema, 20025 block: *Block, 20026 src: LazySrcLoc, 20027 operands: []const Zir.Inst.Ref, 20028 is_ref: bool, 20029 ) CompileError!Air.Inst.Ref { 20030 const pt = sema.pt; 20031 const zcu = pt.zcu; 20032 const gpa = sema.gpa; 20033 const ip = &zcu.intern_pool; 20034 20035 const types = try sema.arena.alloc(InternPool.Index, operands.len); 20036 const values = try sema.arena.alloc(InternPool.Index, operands.len); 20037 20038 const opt_runtime_src = rs: { 20039 var runtime_src: ?LazySrcLoc = null; 20040 for (operands, 0..) |operand, i| { 20041 const operand_src = src; // TODO better source location 20042 const elem = try sema.resolveInst(operand); 20043 types[i] = sema.typeOf(elem).toIntern(); 20044 if (Type.fromInterned(types[i]).zigTypeTag(zcu) == .@"opaque") { 20045 const msg = msg: { 20046 const msg = try sema.errMsg(operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 20047 errdefer msg.destroy(gpa); 20048 20049 try sema.addDeclaredHereNote(msg, .fromInterned(types[i])); 20050 break :msg msg; 20051 }; 20052 return sema.failWithOwnedErrorMsg(block, msg); 20053 } 20054 if (try sema.resolveValue(elem)) |val| { 20055 values[i] = val.toIntern(); 20056 } else { 20057 values[i] = .none; 20058 runtime_src = operand_src; 20059 } 20060 } 20061 break :rs runtime_src; 20062 }; 20063 20064 const tuple_ty: Type = .fromInterned(try ip.getTupleType(gpa, pt.tid, .{ 20065 .types = types, 20066 .values = values, 20067 })); 20068 20069 const runtime_src = opt_runtime_src orelse { 20070 const tuple_val = try pt.aggregateValue(tuple_ty, values); 20071 return sema.addConstantMaybeRef(tuple_val.toIntern(), is_ref); 20072 }; 20073 20074 try sema.requireRuntimeBlock(block, src, runtime_src); 20075 20076 if (is_ref) { 20077 const target = sema.pt.zcu.getTarget(); 20078 const alloc_ty = try pt.ptrTypeSema(.{ 20079 .child = tuple_ty.toIntern(), 20080 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 20081 }); 20082 const alloc = try block.addTy(.alloc, alloc_ty); 20083 for (operands, 0..) |operand, i_usize| { 20084 const i: u32 = @intCast(i_usize); 20085 const field_ptr_ty = try pt.ptrTypeSema(.{ 20086 .child = types[i], 20087 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 20088 }); 20089 if (values[i] == .none) { 20090 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 20091 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); 20092 } 20093 } 20094 20095 return sema.makePtrConst(block, alloc); 20096 } 20097 20098 const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 20099 for (operands, 0..) |operand, i| { 20100 element_refs[i] = try sema.resolveInst(operand); 20101 } 20102 20103 return block.addAggregateInit(tuple_ty, element_refs); 20104 } 20105 20106 fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref { 20107 return if (is_ref) sema.uavRef(val) else Air.internedToRef(val); 20108 } 20109 20110 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20111 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 20112 const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data; 20113 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20114 const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); 20115 const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); 20116 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name }); 20117 return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); 20118 } 20119 20120 fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20121 const pt = sema.pt; 20122 const zcu = pt.zcu; 20123 const ip = &zcu.intern_pool; 20124 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 20125 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; 20126 const ty_src = block.nodeOffset(inst_data.src_node); 20127 const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node }); 20128 const wrapped_aggregate_ty = try sema.resolveTypeOrPoison(block, ty_src, extra.container_type) orelse return .generic_poison_type; 20129 const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu); 20130 const zir_field_name = sema.code.nullTerminatedString(extra.name_start); 20131 const field_name = try ip.getOrPutString(sema.gpa, pt.tid, zir_field_name, .no_embedded_nulls); 20132 return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); 20133 } 20134 20135 fn fieldType( 20136 sema: *Sema, 20137 block: *Block, 20138 aggregate_ty: Type, 20139 field_name: InternPool.NullTerminatedString, 20140 field_src: LazySrcLoc, 20141 ty_src: LazySrcLoc, 20142 ) CompileError!Air.Inst.Ref { 20143 const pt = sema.pt; 20144 const zcu = pt.zcu; 20145 const ip = &zcu.intern_pool; 20146 var cur_ty = aggregate_ty; 20147 while (true) { 20148 try cur_ty.resolveFields(pt); 20149 switch (cur_ty.zigTypeTag(zcu)) { 20150 .@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) { 20151 .tuple_type => |tuple| { 20152 const field_index = try sema.tupleFieldIndex(block, cur_ty, field_name, field_src); 20153 return Air.internedToRef(tuple.types.get(ip)[field_index]); 20154 }, 20155 .struct_type => { 20156 const struct_type = ip.loadStructType(cur_ty.toIntern()); 20157 const field_index = struct_type.nameIndex(ip, field_name) orelse 20158 return sema.failWithBadStructFieldAccess(block, cur_ty, struct_type, field_src, field_name); 20159 const field_ty = struct_type.field_types.get(ip)[field_index]; 20160 return Air.internedToRef(field_ty); 20161 }, 20162 else => unreachable, 20163 }, 20164 .@"union" => { 20165 const union_obj = zcu.typeToUnion(cur_ty).?; 20166 const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse 20167 return sema.failWithBadUnionFieldAccess(block, cur_ty, union_obj, field_src, field_name); 20168 const field_ty = union_obj.field_types.get(ip)[field_index]; 20169 return Air.internedToRef(field_ty); 20170 }, 20171 .optional => { 20172 // Struct/array init through optional requires the child type to not be a pointer. 20173 // If the child of .optional is a pointer it'll error on the next loop. 20174 cur_ty = .fromInterned(ip.indexToKey(cur_ty.toIntern()).opt_type); 20175 continue; 20176 }, 20177 .error_union => { 20178 cur_ty = cur_ty.errorUnionPayload(zcu); 20179 continue; 20180 }, 20181 else => {}, 20182 } 20183 return sema.fail(block, ty_src, "expected struct or union; found '{f}'", .{ 20184 cur_ty.fmt(pt), 20185 }); 20186 } 20187 } 20188 20189 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 20190 return sema.getErrorReturnTrace(block); 20191 } 20192 20193 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 20194 const pt = sema.pt; 20195 const zcu = pt.zcu; 20196 const ip = &zcu.intern_pool; 20197 const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); 20198 try stack_trace_ty.resolveFields(pt); 20199 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); 20200 const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern()); 20201 20202 switch (sema.owner.unwrap()) { 20203 .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) { 20204 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); 20205 }, 20206 .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, 20207 } 20208 return Air.internedToRef(try pt.intern(.{ .opt = .{ 20209 .ty = opt_ptr_stack_trace_ty.toIntern(), 20210 .val = .none, 20211 } })); 20212 } 20213 20214 fn zirFrame( 20215 sema: *Sema, 20216 block: *Block, 20217 extended: Zir.Inst.Extended.InstData, 20218 ) CompileError!Air.Inst.Ref { 20219 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 20220 const src = block.nodeOffset(src_node); 20221 return sema.failWithUseOfAsync(block, src); 20222 } 20223 20224 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20225 const zcu = sema.pt.zcu; 20226 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20227 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20228 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 20229 if (ty.isNoReturn(zcu)) { 20230 return sema.fail(block, operand_src, "no align available for type '{f}'", .{ty.fmt(sema.pt)}); 20231 } 20232 const val = try ty.lazyAbiAlignment(sema.pt); 20233 return Air.internedToRef(val.toIntern()); 20234 } 20235 20236 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20237 const pt = sema.pt; 20238 const zcu = pt.zcu; 20239 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20240 const src = block.nodeOffset(inst_data.src_node); 20241 const operand = try sema.resolveInst(inst_data.operand); 20242 const operand_ty = sema.typeOf(operand); 20243 const is_vector = operand_ty.zigTypeTag(zcu) == .vector; 20244 const operand_scalar_ty = operand_ty.scalarType(zcu); 20245 if (operand_scalar_ty.toIntern() != .bool_type) { 20246 return sema.fail(block, src, "expected 'bool', found '{t}'", .{operand_scalar_ty.zigTypeTag(zcu)}); 20247 } 20248 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; 20249 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1; 20250 if (try sema.resolveValue(operand)) |val| { 20251 if (!is_vector) { 20252 return if (val.isUndef(zcu)) .undef_u1 else if (val.toBool()) .one_u1 else .zero_u1; 20253 } 20254 if (val.isUndef(zcu)) return pt.undefRef(dest_ty); 20255 const new_elems = try sema.arena.alloc(InternPool.Index, len); 20256 for (new_elems, 0..) |*new_elem, i| { 20257 const old_elem = try val.elemValue(pt, i); 20258 new_elem.* = if (old_elem.isUndef(zcu)) 20259 .undef_u1 20260 else if (old_elem.toBool()) 20261 .one_u1 20262 else 20263 .zero_u1; 20264 } 20265 return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); 20266 } 20267 return block.addBitCast(dest_ty, operand); 20268 } 20269 20270 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20271 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20272 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20273 const uncoerced_operand = try sema.resolveInst(inst_data.operand); 20274 const operand = try sema.coerce(block, .anyerror, uncoerced_operand, operand_src); 20275 20276 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 20277 const err_name = sema.pt.zcu.intern_pool.indexToKey(val.toIntern()).err.name; 20278 return sema.addNullTerminatedStrLit(err_name); 20279 } 20280 20281 // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass 20282 // might be able to resolve the result at compile time. 20283 return block.addUnOp(.error_name, operand); 20284 } 20285 20286 fn zirAbs( 20287 sema: *Sema, 20288 block: *Block, 20289 inst: Zir.Inst.Index, 20290 ) CompileError!Air.Inst.Ref { 20291 const pt = sema.pt; 20292 const zcu = pt.zcu; 20293 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20294 const operand = try sema.resolveInst(inst_data.operand); 20295 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20296 const operand_ty = sema.typeOf(operand); 20297 const scalar_ty = operand_ty.scalarType(zcu); 20298 20299 const result_ty = switch (scalar_ty.zigTypeTag(zcu)) { 20300 .comptime_float, .float, .comptime_int => operand_ty, 20301 .int => if (scalar_ty.isSignedInt(zcu)) try operand_ty.toUnsigned(pt) else return operand, 20302 else => return sema.fail( 20303 block, 20304 operand_src, 20305 "expected integer, float, or vector of either integers or floats, found '{f}'", 20306 .{operand_ty.fmt(pt)}, 20307 ), 20308 }; 20309 20310 return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse { 20311 try sema.requireRuntimeBlock(block, operand_src, null); 20312 return block.addTyOp(.abs, result_ty, operand); 20313 }; 20314 } 20315 20316 fn maybeConstantUnaryMath( 20317 sema: *Sema, 20318 operand: Air.Inst.Ref, 20319 result_ty: Type, 20320 comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, 20321 ) CompileError!?Air.Inst.Ref { 20322 const pt = sema.pt; 20323 const zcu = pt.zcu; 20324 switch (result_ty.zigTypeTag(zcu)) { 20325 .vector => if (try sema.resolveValue(operand)) |val| { 20326 const scalar_ty = result_ty.scalarType(zcu); 20327 const vec_len = result_ty.vectorLen(zcu); 20328 if (val.isUndef(zcu)) 20329 return try pt.undefRef(result_ty); 20330 20331 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 20332 for (elems, 0..) |*elem, i| { 20333 const elem_val = try val.elemValue(pt, i); 20334 elem.* = (try eval(elem_val, scalar_ty, sema.arena, pt)).toIntern(); 20335 } 20336 return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern()); 20337 }, 20338 else => if (try sema.resolveValue(operand)) |operand_val| { 20339 if (operand_val.isUndef(zcu)) 20340 return try pt.undefRef(result_ty); 20341 const result_val = try eval(operand_val, result_ty, sema.arena, pt); 20342 return Air.internedToRef(result_val.toIntern()); 20343 }, 20344 } 20345 return null; 20346 } 20347 20348 fn zirUnaryMath( 20349 sema: *Sema, 20350 block: *Block, 20351 inst: Zir.Inst.Index, 20352 air_tag: Air.Inst.Tag, 20353 comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, 20354 ) CompileError!Air.Inst.Ref { 20355 const tracy = trace(@src()); 20356 defer tracy.end(); 20357 20358 const pt = sema.pt; 20359 const zcu = pt.zcu; 20360 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20361 const operand = try sema.resolveInst(inst_data.operand); 20362 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20363 const operand_ty = sema.typeOf(operand); 20364 const scalar_ty = operand_ty.scalarType(zcu); 20365 20366 switch (scalar_ty.zigTypeTag(zcu)) { 20367 .comptime_float, .float => {}, 20368 else => return sema.fail( 20369 block, 20370 operand_src, 20371 "expected vector of floats or float type, found '{f}'", 20372 .{operand_ty.fmt(pt)}, 20373 ), 20374 } 20375 20376 return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse { 20377 try sema.requireRuntimeBlock(block, operand_src, null); 20378 return block.addUnOp(air_tag, operand); 20379 }; 20380 } 20381 20382 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20383 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20384 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20385 const src = block.nodeOffset(inst_data.src_node); 20386 const operand = try sema.resolveInst(inst_data.operand); 20387 const operand_ty = sema.typeOf(operand); 20388 const pt = sema.pt; 20389 const zcu = pt.zcu; 20390 const ip = &zcu.intern_pool; 20391 try operand_ty.resolveLayout(pt); 20392 const enum_ty = switch (operand_ty.zigTypeTag(zcu)) { 20393 .enum_literal => { 20394 const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?; 20395 const tag_name = ip.indexToKey(val.toIntern()).enum_literal; 20396 return sema.addNullTerminatedStrLit(tag_name); 20397 }, 20398 .@"enum" => operand_ty, 20399 .@"union" => operand_ty.unionTagType(zcu) orelse 20400 return sema.fail(block, src, "union '{f}' is untagged", .{operand_ty.fmt(pt)}), 20401 else => return sema.fail(block, operand_src, "expected enum or union; found '{f}'", .{ 20402 operand_ty.fmt(pt), 20403 }), 20404 }; 20405 if (enum_ty.enumFieldCount(zcu) == 0) { 20406 // TODO I don't think this is the correct way to handle this but 20407 // it prevents a crash. 20408 // https://github.com/ziglang/zig/issues/15909 20409 return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{f}'", .{ 20410 enum_ty.fmt(pt), 20411 }); 20412 } 20413 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); 20414 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { 20415 const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse { 20416 const msg = msg: { 20417 const msg = try sema.errMsg(src, "no field with value '{f}' in enum '{f}'", .{ 20418 val.fmtValueSema(pt, sema), enum_ty.fmt(pt), 20419 }); 20420 errdefer msg.destroy(sema.gpa); 20421 try sema.errNote(enum_ty.srcLoc(zcu), msg, "declared here", .{}); 20422 break :msg msg; 20423 }; 20424 return sema.failWithOwnedErrorMsg(block, msg); 20425 }; 20426 // TODO: write something like getCoercedInts to avoid needing to dupe 20427 const field_name = enum_ty.enumFieldName(field_index, zcu); 20428 return sema.addNullTerminatedStrLit(field_name); 20429 } 20430 try sema.requireRuntimeBlock(block, src, operand_src); 20431 if (block.wantSafety() and zcu.backendSupportsFeature(.is_named_enum_value)) { 20432 const ok = try block.addUnOp(.is_named_enum_value, casted_operand); 20433 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); 20434 } 20435 // In case the value is runtime-known, we have an AIR instruction for this instead 20436 // of trying to lower it in Sema because an optimization pass may result in the operand 20437 // being comptime-known, which would let us elide the `tag_name` AIR instruction. 20438 return block.addUnOp(.tag_name, casted_operand); 20439 } 20440 20441 fn zirReify( 20442 sema: *Sema, 20443 block: *Block, 20444 extended: Zir.Inst.Extended.InstData, 20445 inst: Zir.Inst.Index, 20446 ) CompileError!Air.Inst.Ref { 20447 const pt = sema.pt; 20448 const zcu = pt.zcu; 20449 const gpa = sema.gpa; 20450 const ip = &zcu.intern_pool; 20451 const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small); 20452 const extra = sema.code.extraData(Zir.Inst.Reify, extended.operand).data; 20453 const tracked_inst = try block.trackZir(inst); 20454 const src: LazySrcLoc = .{ 20455 .base_node_inst = tracked_inst, 20456 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 20457 }; 20458 const operand_src: LazySrcLoc = .{ 20459 .base_node_inst = tracked_inst, 20460 .offset = .{ 20461 .node_offset_builtin_call_arg = .{ 20462 .builtin_call_node = .zero, // `tracked_inst` is precisely the `reify` instruction, so offset is 0 20463 .arg_index = 0, 20464 }, 20465 }, 20466 }; 20467 const type_info_ty = try sema.getBuiltinType(src, .Type); 20468 const uncasted_operand = try sema.resolveInst(extra.operand); 20469 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); 20470 const val = try sema.resolveConstDefinedValue(block, operand_src, type_info, .{ .simple = .operand_Type }); 20471 const union_val = ip.indexToKey(val.toIntern()).un; 20472 if (try sema.anyUndef(block, operand_src, Value.fromInterned(union_val.val))) { 20473 return sema.failWithUseOfUndef(block, operand_src, null); 20474 } 20475 const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), zcu).?; 20476 switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { 20477 .type => return .type_type, 20478 .void => return .void_type, 20479 .bool => return .bool_type, 20480 .noreturn => return .noreturn_type, 20481 .comptime_float => return .comptime_float_type, 20482 .comptime_int => return .comptime_int_type, 20483 .undefined => return .undefined_type, 20484 .null => return .null_type, 20485 .@"anyframe" => return sema.failWithUseOfAsync(block, src), 20486 .enum_literal => return .enum_literal_type, 20487 .int => { 20488 const int = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Int); 20489 const ty = try pt.intType(int.signedness, int.bits); 20490 return Air.internedToRef(ty.toIntern()); 20491 }, 20492 .vector => { 20493 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20494 const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20495 ip, 20496 try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), 20497 ).?); 20498 const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20499 ip, 20500 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), 20501 ).?); 20502 20503 const len: u32 = @intCast(try len_val.toUnsignedIntSema(pt)); 20504 const child_ty = child_val.toType(); 20505 20506 try sema.checkVectorElemType(block, src, child_ty); 20507 20508 const ty = try pt.vectorType(.{ 20509 .len = len, 20510 .child = child_ty.toIntern(), 20511 }); 20512 return Air.internedToRef(ty.toIntern()); 20513 }, 20514 .float => { 20515 const float = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Float); 20516 20517 const ty: Type = switch (float.bits) { 20518 16 => .f16, 20519 32 => .f32, 20520 64 => .f64, 20521 80 => .f80, 20522 128 => .f128, 20523 else => return sema.fail(block, src, "{d}-bit float unsupported", .{float.bits}), 20524 }; 20525 return Air.internedToRef(ty.toIntern()); 20526 }, 20527 .pointer => { 20528 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20529 const size_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20530 ip, 20531 try ip.getOrPutString(gpa, pt.tid, "size", .no_embedded_nulls), 20532 ).?); 20533 const is_const_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20534 ip, 20535 try ip.getOrPutString(gpa, pt.tid, "is_const", .no_embedded_nulls), 20536 ).?); 20537 const is_volatile_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20538 ip, 20539 try ip.getOrPutString(gpa, pt.tid, "is_volatile", .no_embedded_nulls), 20540 ).?); 20541 const alignment_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20542 ip, 20543 try ip.getOrPutString(gpa, pt.tid, "alignment", .no_embedded_nulls), 20544 ).?); 20545 const address_space_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20546 ip, 20547 try ip.getOrPutString(gpa, pt.tid, "address_space", .no_embedded_nulls), 20548 ).?); 20549 const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20550 ip, 20551 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), 20552 ).?); 20553 const is_allowzero_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20554 ip, 20555 try ip.getOrPutString(gpa, pt.tid, "is_allowzero", .no_embedded_nulls), 20556 ).?); 20557 const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20558 ip, 20559 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls), 20560 ).?); 20561 20562 if (!try sema.intFitsInType(alignment_val, align_ty, null)) { 20563 return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)}); 20564 } 20565 const alignment_val_int = try alignment_val.toUnsignedIntSema(pt); 20566 const abi_align = try sema.validateAlign(block, src, alignment_val_int); 20567 20568 const elem_ty = child_val.toType(); 20569 if (abi_align != .none) { 20570 try elem_ty.resolveLayout(pt); 20571 } 20572 20573 const ptr_size = try sema.interpretBuiltinType(block, operand_src, size_val, std.builtin.Type.Pointer.Size); 20574 20575 const actual_sentinel: InternPool.Index = s: { 20576 if (!sentinel_val.isNull(zcu)) { 20577 if (ptr_size == .one or ptr_size == .c) { 20578 return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); 20579 } 20580 const sentinel_ptr_val = sentinel_val.optionalValue(zcu).?; 20581 const ptr_ty = try pt.singleMutPtrType(elem_ty); 20582 const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; 20583 try sema.checkSentinelType(block, src, elem_ty); 20584 break :s sent_val.toIntern(); 20585 } 20586 break :s .none; 20587 }; 20588 20589 if (elem_ty.zigTypeTag(zcu) == .noreturn) { 20590 return sema.fail(block, src, "pointer to noreturn not allowed", .{}); 20591 } else if (elem_ty.zigTypeTag(zcu) == .@"fn") { 20592 if (ptr_size != .one) { 20593 return sema.fail(block, src, "function pointers must be single pointers", .{}); 20594 } 20595 } else if (ptr_size == .many and elem_ty.zigTypeTag(zcu) == .@"opaque") { 20596 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); 20597 } else if (ptr_size == .c) { 20598 if (!try sema.validateExternType(elem_ty, .other)) { 20599 const msg = msg: { 20600 const msg = try sema.errMsg(src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)}); 20601 errdefer msg.destroy(gpa); 20602 20603 try sema.explainWhyTypeIsNotExtern(msg, src, elem_ty, .other); 20604 20605 try sema.addDeclaredHereNote(msg, elem_ty); 20606 break :msg msg; 20607 }; 20608 return sema.failWithOwnedErrorMsg(block, msg); 20609 } 20610 if (elem_ty.zigTypeTag(zcu) == .@"opaque") { 20611 return sema.fail(block, src, "C pointers cannot point to opaque types", .{}); 20612 } 20613 } 20614 20615 const ty = try pt.ptrTypeSema(.{ 20616 .child = elem_ty.toIntern(), 20617 .sentinel = actual_sentinel, 20618 .flags = .{ 20619 .size = ptr_size, 20620 .is_const = is_const_val.toBool(), 20621 .is_volatile = is_volatile_val.toBool(), 20622 .alignment = abi_align, 20623 .address_space = try sema.interpretBuiltinType(block, operand_src, address_space_val, std.builtin.AddressSpace), 20624 .is_allowzero = is_allowzero_val.toBool(), 20625 }, 20626 }); 20627 return Air.internedToRef(ty.toIntern()); 20628 }, 20629 .array => { 20630 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20631 const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20632 ip, 20633 try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), 20634 ).?); 20635 const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20636 ip, 20637 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), 20638 ).?); 20639 const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20640 ip, 20641 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls), 20642 ).?); 20643 20644 const len = try len_val.toUnsignedIntSema(pt); 20645 const child_ty = child_val.toType(); 20646 const sentinel = if (sentinel_val.optionalValue(zcu)) |p| blk: { 20647 const ptr_ty = try pt.singleMutPtrType(child_ty); 20648 try sema.checkSentinelType(block, src, child_ty); 20649 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?; 20650 } else null; 20651 20652 const ty = try pt.arrayType(.{ 20653 .len = len, 20654 .sentinel = if (sentinel) |s| s.toIntern() else .none, 20655 .child = child_ty.toIntern(), 20656 }); 20657 return Air.internedToRef(ty.toIntern()); 20658 }, 20659 .optional => { 20660 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20661 const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20662 ip, 20663 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), 20664 ).?); 20665 20666 const child_ty = child_val.toType(); 20667 20668 const ty = try pt.optionalType(child_ty.toIntern()); 20669 return Air.internedToRef(ty.toIntern()); 20670 }, 20671 .error_union => { 20672 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20673 const error_set_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20674 ip, 20675 try ip.getOrPutString(gpa, pt.tid, "error_set", .no_embedded_nulls), 20676 ).?); 20677 const payload_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20678 ip, 20679 try ip.getOrPutString(gpa, pt.tid, "payload", .no_embedded_nulls), 20680 ).?); 20681 20682 const error_set_ty = error_set_val.toType(); 20683 const payload_ty = payload_val.toType(); 20684 20685 if (error_set_ty.zigTypeTag(zcu) != .error_set) { 20686 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{}); 20687 } 20688 20689 const ty = try pt.errorUnionType(error_set_ty, payload_ty); 20690 return Air.internedToRef(ty.toIntern()); 20691 }, 20692 .error_set => { 20693 const payload_val = Value.fromInterned(union_val.val).optionalValue(zcu) orelse 20694 return .anyerror_type; 20695 20696 const names_val = try sema.derefSliceAsArray(block, src, payload_val, .{ .simple = .error_set_contents }); 20697 20698 const len = try sema.usizeCast(block, src, names_val.typeOf(zcu).arrayLen(zcu)); 20699 var names: InferredErrorSet.NameMap = .{}; 20700 try names.ensureUnusedCapacity(sema.arena, len); 20701 for (0..len) |i| { 20702 const elem_val = try names_val.elemValue(pt, i); 20703 const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); 20704 const name_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( 20705 ip, 20706 try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), 20707 ).?); 20708 20709 const name = try sema.sliceToIpString(block, src, name_val, .{ .simple = .error_set_contents }); 20710 _ = try pt.getErrorValue(name); 20711 const gop = names.getOrPutAssumeCapacity(name); 20712 if (gop.found_existing) { 20713 return sema.fail(block, src, "duplicate error '{f}'", .{ 20714 name.fmt(ip), 20715 }); 20716 } 20717 } 20718 20719 const ty = try pt.errorSetFromUnsortedNames(names.keys()); 20720 return Air.internedToRef(ty.toIntern()); 20721 }, 20722 .@"struct" => { 20723 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20724 const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20725 ip, 20726 try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls), 20727 ).?); 20728 const backing_integer_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20729 ip, 20730 try ip.getOrPutString(gpa, pt.tid, "backing_integer", .no_embedded_nulls), 20731 ).?); 20732 const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20733 ip, 20734 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), 20735 ).?); 20736 const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20737 ip, 20738 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), 20739 ).?); 20740 const is_tuple_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20741 ip, 20742 try ip.getOrPutString(gpa, pt.tid, "is_tuple", .no_embedded_nulls), 20743 ).?); 20744 20745 const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout); 20746 20747 // Decls 20748 if (try decls_val.sliceLen(pt) > 0) { 20749 return sema.fail(block, src, "reified structs must have no decls", .{}); 20750 } 20751 20752 if (layout != .@"packed" and !backing_integer_val.isNull(zcu)) { 20753 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); 20754 } 20755 20756 const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .struct_fields }); 20757 20758 if (is_tuple_val.toBool()) { 20759 switch (layout) { 20760 .@"extern" => return sema.fail(block, src, "extern tuples are not supported", .{}), 20761 .@"packed" => return sema.fail(block, src, "packed tuples are not supported", .{}), 20762 .auto => {}, 20763 } 20764 return sema.reifyTuple(block, src, fields_arr); 20765 } else { 20766 return sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_arr, name_strategy); 20767 } 20768 }, 20769 .@"enum" => { 20770 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20771 const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20772 ip, 20773 try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls), 20774 ).?); 20775 const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20776 ip, 20777 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), 20778 ).?); 20779 const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20780 ip, 20781 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), 20782 ).?); 20783 const is_exhaustive_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20784 ip, 20785 try ip.getOrPutString(gpa, pt.tid, "is_exhaustive", .no_embedded_nulls), 20786 ).?); 20787 20788 if (try decls_val.sliceLen(pt) > 0) { 20789 return sema.fail(block, src, "reified enums must have no decls", .{}); 20790 } 20791 20792 const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .enum_fields }); 20793 20794 return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_arr, name_strategy); 20795 }, 20796 .@"opaque" => { 20797 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20798 const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20799 ip, 20800 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), 20801 ).?); 20802 20803 // Decls 20804 if (try decls_val.sliceLen(pt) > 0) { 20805 return sema.fail(block, src, "reified opaque must have no decls", .{}); 20806 } 20807 20808 const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, .{ 20809 .key = .{ .reified = .{ 20810 .zir_index = try block.trackZir(inst), 20811 } }, 20812 })) { 20813 .existing => |ty| { 20814 try sema.addTypeReferenceEntry(src, ty); 20815 return Air.internedToRef(ty); 20816 }, 20817 .wip => |wip| wip, 20818 }; 20819 errdefer wip_ty.cancel(ip, pt.tid); 20820 20821 const type_name = try sema.createTypeName( 20822 block, 20823 name_strategy, 20824 "opaque", 20825 inst, 20826 wip_ty.index, 20827 ); 20828 wip_ty.setName(ip, type_name.name, type_name.nav); 20829 20830 const new_namespace_index = try pt.createNamespace(.{ 20831 .parent = block.namespace.toOptional(), 20832 .owner_type = wip_ty.index, 20833 .file_scope = block.getFileScopeIndex(zcu), 20834 .generation = zcu.generation, 20835 }); 20836 20837 try sema.addTypeReferenceEntry(src, wip_ty.index); 20838 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 20839 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 20840 }, 20841 .@"union" => { 20842 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20843 const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20844 ip, 20845 try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls), 20846 ).?); 20847 const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20848 ip, 20849 try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls), 20850 ).?); 20851 const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20852 ip, 20853 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), 20854 ).?); 20855 const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20856 ip, 20857 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), 20858 ).?); 20859 20860 if (try decls_val.sliceLen(pt) > 0) { 20861 return sema.fail(block, src, "reified unions must have no decls", .{}); 20862 } 20863 const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout); 20864 20865 const has_tag = tag_type_val.optionalValue(zcu) != null; 20866 20867 if (has_tag) { 20868 switch (layout) { 20869 .@"extern" => return sema.fail(block, src, "extern union does not support enum tag type", .{}), 20870 .@"packed" => return sema.fail(block, src, "packed union does not support enum tag type", .{}), 20871 .auto => {}, 20872 } 20873 } 20874 20875 const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .union_fields }); 20876 20877 return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_arr, name_strategy); 20878 }, 20879 .@"fn" => { 20880 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20881 const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20882 ip, 20883 try ip.getOrPutString(gpa, pt.tid, "calling_convention", .no_embedded_nulls), 20884 ).?); 20885 const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20886 ip, 20887 try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls), 20888 ).?); 20889 const is_var_args_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20890 ip, 20891 try ip.getOrPutString(gpa, pt.tid, "is_var_args", .no_embedded_nulls), 20892 ).?); 20893 const return_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20894 ip, 20895 try ip.getOrPutString(gpa, pt.tid, "return_type", .no_embedded_nulls), 20896 ).?); 20897 const params_slice_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20898 ip, 20899 try ip.getOrPutString(gpa, pt.tid, "params", .no_embedded_nulls), 20900 ).?); 20901 20902 const is_generic = is_generic_val.toBool(); 20903 if (is_generic) { 20904 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{}); 20905 } 20906 20907 const is_var_args = is_var_args_val.toBool(); 20908 const cc = try sema.analyzeValueAsCallconv(block, src, calling_convention_val); 20909 if (is_var_args) { 20910 try sema.checkCallConvSupportsVarArgs(block, src, cc); 20911 } 20912 20913 const return_type = return_type_val.optionalValue(zcu) orelse 20914 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); 20915 20916 const params_val = try sema.derefSliceAsArray(block, operand_src, params_slice_val, .{ .simple = .function_parameters }); 20917 20918 const args_len = try sema.usizeCast(block, src, params_val.typeOf(zcu).arrayLen(zcu)); 20919 const param_types = try sema.arena.alloc(InternPool.Index, args_len); 20920 20921 var noalias_bits: u32 = 0; 20922 for (param_types, 0..) |*param_type, i| { 20923 const elem_val = try params_val.elemValue(pt, i); 20924 const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); 20925 const param_is_generic_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( 20926 ip, 20927 try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls), 20928 ).?); 20929 const param_is_noalias_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( 20930 ip, 20931 try ip.getOrPutString(gpa, pt.tid, "is_noalias", .no_embedded_nulls), 20932 ).?); 20933 const opt_param_type_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( 20934 ip, 20935 try ip.getOrPutString(gpa, pt.tid, "type", .no_embedded_nulls), 20936 ).?); 20937 20938 if (param_is_generic_val.toBool()) { 20939 return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{}); 20940 } 20941 20942 const param_type_val = opt_param_type_val.optionalValue(zcu) orelse 20943 return sema.fail(block, src, "Type.Fn.Param.type must be non-null for @Type", .{}); 20944 param_type.* = param_type_val.toIntern(); 20945 20946 if (param_is_noalias_val.toBool()) { 20947 if (!Type.fromInterned(param_type.*).isPtrAtRuntime(zcu)) { 20948 return sema.fail(block, src, "non-pointer parameter declared noalias", .{}); 20949 } 20950 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse 20951 return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 20952 } 20953 } 20954 20955 const ty = try pt.funcType(.{ 20956 .param_types = param_types, 20957 .noalias_bits = noalias_bits, 20958 .return_type = return_type.toIntern(), 20959 .cc = cc, 20960 .is_var_args = is_var_args, 20961 }); 20962 return Air.internedToRef(ty.toIntern()); 20963 }, 20964 .frame => return sema.failWithUseOfAsync(block, src), 20965 } 20966 } 20967 20968 fn reifyEnum( 20969 sema: *Sema, 20970 block: *Block, 20971 inst: Zir.Inst.Index, 20972 src: LazySrcLoc, 20973 tag_ty: Type, 20974 is_exhaustive: bool, 20975 fields_val: Value, 20976 name_strategy: Zir.Inst.NameStrategy, 20977 ) CompileError!Air.Inst.Ref { 20978 const pt = sema.pt; 20979 const zcu = pt.zcu; 20980 const gpa = sema.gpa; 20981 const ip = &zcu.intern_pool; 20982 20983 // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`. 20984 20985 const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); 20986 20987 // The validation work here is non-trivial, and it's possible the type already exists. 20988 // So in this first pass, let's just construct a hash to optimize for this case. If the 20989 // inputs turn out to be invalid, we can cancel the WIP type later. 20990 20991 // For deduplication purposes, we must create a hash including all details of this type. 20992 // TODO: use a longer hash! 20993 var hasher = std.hash.Wyhash.init(0); 20994 std.hash.autoHash(&hasher, tag_ty.toIntern()); 20995 std.hash.autoHash(&hasher, is_exhaustive); 20996 std.hash.autoHash(&hasher, fields_len); 20997 20998 for (0..fields_len) |field_idx| { 20999 const field_info = try fields_val.elemValue(pt, field_idx); 21000 21001 const field_name_val = try field_info.fieldValue(pt, 0); 21002 const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1)); 21003 21004 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .enum_field_name }); 21005 21006 std.hash.autoHash(&hasher, .{ 21007 field_name, 21008 field_value_val.toIntern(), 21009 }); 21010 } 21011 21012 const tracked_inst = try block.trackZir(inst); 21013 21014 const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{ 21015 .has_values = true, 21016 .tag_mode = if (is_exhaustive) .explicit else .nonexhaustive, 21017 .fields_len = fields_len, 21018 .key = .{ .reified = .{ 21019 .zir_index = tracked_inst, 21020 .type_hash = hasher.final(), 21021 } }, 21022 }, false)) { 21023 .wip => |wip| wip, 21024 .existing => |ty| { 21025 try sema.declareDependency(.{ .interned = ty }); 21026 try sema.addTypeReferenceEntry(src, ty); 21027 return Air.internedToRef(ty); 21028 }, 21029 }; 21030 var done = false; 21031 errdefer if (!done) wip_ty.cancel(ip, pt.tid); 21032 21033 if (tag_ty.zigTypeTag(zcu) != .int) { 21034 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); 21035 } 21036 21037 const type_name = try sema.createTypeName( 21038 block, 21039 name_strategy, 21040 "enum", 21041 inst, 21042 wip_ty.index, 21043 ); 21044 wip_ty.setName(ip, type_name.name, type_name.nav); 21045 21046 const new_namespace_index = try pt.createNamespace(.{ 21047 .parent = block.namespace.toOptional(), 21048 .owner_type = wip_ty.index, 21049 .file_scope = block.getFileScopeIndex(zcu), 21050 .generation = zcu.generation, 21051 }); 21052 21053 try sema.declareDependency(.{ .interned = wip_ty.index }); 21054 try sema.addTypeReferenceEntry(src, wip_ty.index); 21055 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 21056 wip_ty.prepare(ip, new_namespace_index); 21057 wip_ty.setTagTy(ip, tag_ty.toIntern()); 21058 done = true; 21059 21060 for (0..fields_len) |field_idx| { 21061 const field_info = try fields_val.elemValue(pt, field_idx); 21062 21063 const field_name_val = try field_info.fieldValue(pt, 0); 21064 const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1)); 21065 21066 // Don't pass a reason; first loop acts as an assertion that this is valid. 21067 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); 21068 21069 if (!try sema.intFitsInType(field_value_val, tag_ty, null)) { 21070 // TODO: better source location 21071 return sema.fail(block, src, "field '{f}' with enumeration value '{f}' is too large for backing int type '{f}'", .{ 21072 field_name.fmt(ip), 21073 field_value_val.fmtValueSema(pt, sema), 21074 tag_ty.fmt(pt), 21075 }); 21076 } 21077 21078 const coerced_field_val = try pt.getCoerced(field_value_val, tag_ty); 21079 if (wip_ty.nextField(ip, field_name, coerced_field_val.toIntern())) |conflict| { 21080 return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) { 21081 .name => msg: { 21082 const msg = try sema.errMsg(src, "duplicate enum field '{f}'", .{field_name.fmt(ip)}); 21083 errdefer msg.destroy(gpa); 21084 _ = conflict.prev_field_idx; // TODO: this note is incorrect 21085 try sema.errNote(src, msg, "other field here", .{}); 21086 break :msg msg; 21087 }, 21088 .value => msg: { 21089 const msg = try sema.errMsg(src, "enum tag value {f} already taken", .{field_value_val.fmtValueSema(pt, sema)}); 21090 errdefer msg.destroy(gpa); 21091 _ = conflict.prev_field_idx; // TODO: this note is incorrect 21092 try sema.errNote(src, msg, "other enum tag value here", .{}); 21093 break :msg msg; 21094 }, 21095 }); 21096 } 21097 } 21098 21099 if (!is_exhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(zcu)) { 21100 return sema.fail(block, src, "non-exhaustive enum specified every value", .{}); 21101 } 21102 21103 codegen_type: { 21104 if (zcu.comp.config.use_llvm) break :codegen_type; 21105 if (block.ownerModule().strip) break :codegen_type; 21106 // This job depends on any resolve_type_fully jobs queued up before it. 21107 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 21108 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 21109 } 21110 return Air.internedToRef(wip_ty.index); 21111 } 21112 21113 fn reifyUnion( 21114 sema: *Sema, 21115 block: *Block, 21116 inst: Zir.Inst.Index, 21117 src: LazySrcLoc, 21118 layout: std.builtin.Type.ContainerLayout, 21119 opt_tag_type_val: Value, 21120 fields_val: Value, 21121 name_strategy: Zir.Inst.NameStrategy, 21122 ) CompileError!Air.Inst.Ref { 21123 const pt = sema.pt; 21124 const zcu = pt.zcu; 21125 const gpa = sema.gpa; 21126 const ip = &zcu.intern_pool; 21127 21128 // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`. 21129 21130 const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); 21131 21132 // The validation work here is non-trivial, and it's possible the type already exists. 21133 // So in this first pass, let's just construct a hash to optimize for this case. If the 21134 // inputs turn out to be invalid, we can cancel the WIP type later. 21135 21136 // For deduplication purposes, we must create a hash including all details of this type. 21137 // TODO: use a longer hash! 21138 var hasher = std.hash.Wyhash.init(0); 21139 std.hash.autoHash(&hasher, layout); 21140 std.hash.autoHash(&hasher, opt_tag_type_val.toIntern()); 21141 std.hash.autoHash(&hasher, fields_len); 21142 21143 for (0..fields_len) |field_idx| { 21144 const field_info = try fields_val.elemValue(pt, field_idx); 21145 21146 const field_name_val = try field_info.fieldValue(pt, 0); 21147 const field_type_val = try field_info.fieldValue(pt, 1); 21148 const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 2)); 21149 21150 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .union_field_name }); 21151 std.hash.autoHash(&hasher, .{ 21152 field_name, 21153 field_type_val.toIntern(), 21154 field_align_val.toIntern(), 21155 }); 21156 } 21157 21158 const tracked_inst = try block.trackZir(inst); 21159 21160 const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{ 21161 .flags = .{ 21162 .layout = layout, 21163 .status = .none, 21164 .runtime_tag = if (opt_tag_type_val.optionalValue(zcu) != null) 21165 .tagged 21166 else if (layout != .auto) 21167 .none 21168 else switch (block.wantSafeTypes()) { 21169 true => .safety, 21170 false => .none, 21171 }, 21172 .any_aligned_fields = layout != .@"packed", 21173 .requires_comptime = .unknown, 21174 .assumed_runtime_bits = false, 21175 .assumed_pointer_aligned = false, 21176 .alignment = .none, 21177 }, 21178 .fields_len = fields_len, 21179 .enum_tag_ty = .none, // set later because not yet validated 21180 .field_types = &.{}, // set later 21181 .field_aligns = &.{}, // set later 21182 .key = .{ .reified = .{ 21183 .zir_index = tracked_inst, 21184 .type_hash = hasher.final(), 21185 } }, 21186 }, false)) { 21187 .wip => |wip| wip, 21188 .existing => |ty| { 21189 try sema.declareDependency(.{ .interned = ty }); 21190 try sema.addTypeReferenceEntry(src, ty); 21191 return Air.internedToRef(ty); 21192 }, 21193 }; 21194 errdefer wip_ty.cancel(ip, pt.tid); 21195 21196 const type_name = try sema.createTypeName( 21197 block, 21198 name_strategy, 21199 "union", 21200 inst, 21201 wip_ty.index, 21202 ); 21203 wip_ty.setName(ip, type_name.name, type_name.nav); 21204 21205 const loaded_union = ip.loadUnionType(wip_ty.index); 21206 21207 const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(zcu)) |tag_type_val| tag_ty: { 21208 switch (ip.indexToKey(tag_type_val.toIntern())) { 21209 .enum_type => {}, 21210 else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), 21211 } 21212 const enum_tag_ty = tag_type_val.toType(); 21213 21214 // We simply track which fields of the tag type have been seen. 21215 const tag_ty_fields_len = enum_tag_ty.enumFieldCount(zcu); 21216 var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len); 21217 21218 for (0..fields_len) |field_idx| { 21219 const field_info = try fields_val.elemValue(pt, field_idx); 21220 21221 const field_name_val = try field_info.fieldValue(pt, 0); 21222 const field_type_val = try field_info.fieldValue(pt, 1); 21223 const field_alignment_val = try field_info.fieldValue(pt, 2); 21224 21225 // Don't pass a reason; first loop acts as an assertion that this is valid. 21226 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); 21227 21228 const enum_index = enum_tag_ty.enumFieldIndex(field_name, zcu) orelse { 21229 // TODO: better source location 21230 return sema.fail(block, src, "no field named '{f}' in enum '{f}'", .{ 21231 field_name.fmt(ip), enum_tag_ty.fmt(pt), 21232 }); 21233 }; 21234 if (seen_tags.isSet(enum_index)) { 21235 // TODO: better source location 21236 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)}); 21237 } 21238 seen_tags.set(enum_index); 21239 21240 loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern(); 21241 const byte_align = try field_alignment_val.toUnsignedIntSema(pt); 21242 if (layout == .@"packed") { 21243 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{}); 21244 } else { 21245 loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); 21246 } 21247 } 21248 21249 if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: { 21250 const msg = try sema.errMsg(src, "enum fields missing in union", .{}); 21251 errdefer msg.destroy(gpa); 21252 var it = seen_tags.iterator(.{ .kind = .unset }); 21253 while (it.next()) |enum_index| { 21254 const field_name = enum_tag_ty.enumFieldName(enum_index, zcu); 21255 try sema.addFieldErrNote(enum_tag_ty, enum_index, msg, "field '{f}' missing, declared here", .{ 21256 field_name.fmt(ip), 21257 }); 21258 } 21259 try sema.addDeclaredHereNote(msg, enum_tag_ty); 21260 break :msg msg; 21261 }); 21262 21263 break :tag_ty .{ enum_tag_ty.toIntern(), true }; 21264 } else tag_ty: { 21265 // We must track field names and set up the tag type ourselves. 21266 var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty; 21267 try field_names.ensureTotalCapacity(sema.arena, fields_len); 21268 21269 for (0..fields_len) |field_idx| { 21270 const field_info = try fields_val.elemValue(pt, field_idx); 21271 21272 const field_name_val = try field_info.fieldValue(pt, 0); 21273 const field_type_val = try field_info.fieldValue(pt, 1); 21274 const field_alignment_val = try field_info.fieldValue(pt, 2); 21275 21276 // Don't pass a reason; first loop acts as an assertion that this is valid. 21277 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); 21278 const gop = field_names.getOrPutAssumeCapacity(field_name); 21279 if (gop.found_existing) { 21280 // TODO: better source location 21281 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)}); 21282 } 21283 21284 loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern(); 21285 const byte_align = try field_alignment_val.toUnsignedIntSema(pt); 21286 if (layout == .@"packed") { 21287 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{}); 21288 } else { 21289 loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); 21290 } 21291 } 21292 21293 const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name.name); 21294 break :tag_ty .{ enum_tag_ty, false }; 21295 }; 21296 errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error 21297 21298 for (loaded_union.field_types.get(ip)) |field_ty_ip| { 21299 const field_ty: Type = .fromInterned(field_ty_ip); 21300 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 21301 return sema.failWithOwnedErrorMsg(block, msg: { 21302 const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 21303 errdefer msg.destroy(gpa); 21304 21305 try sema.addDeclaredHereNote(msg, field_ty); 21306 break :msg msg; 21307 }); 21308 } 21309 if (layout == .@"extern" and !try sema.validateExternType(field_ty, .union_field)) { 21310 return sema.failWithOwnedErrorMsg(block, msg: { 21311 const msg = try sema.errMsg(src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 21312 errdefer msg.destroy(gpa); 21313 21314 try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .union_field); 21315 21316 try sema.addDeclaredHereNote(msg, field_ty); 21317 break :msg msg; 21318 }); 21319 } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { 21320 return sema.failWithOwnedErrorMsg(block, msg: { 21321 const msg = try sema.errMsg(src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 21322 errdefer msg.destroy(gpa); 21323 21324 try sema.explainWhyTypeIsNotPacked(msg, src, field_ty); 21325 21326 try sema.addDeclaredHereNote(msg, field_ty); 21327 break :msg msg; 21328 }); 21329 } 21330 } 21331 21332 loaded_union.setTagType(ip, enum_tag_ty); 21333 loaded_union.setStatus(ip, .have_field_types); 21334 21335 const new_namespace_index = try pt.createNamespace(.{ 21336 .parent = block.namespace.toOptional(), 21337 .owner_type = wip_ty.index, 21338 .file_scope = block.getFileScopeIndex(zcu), 21339 .generation = zcu.generation, 21340 }); 21341 21342 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 21343 codegen_type: { 21344 if (zcu.comp.config.use_llvm) break :codegen_type; 21345 if (block.ownerModule().strip) break :codegen_type; 21346 // This job depends on any resolve_type_fully jobs queued up before it. 21347 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 21348 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 21349 } 21350 try sema.declareDependency(.{ .interned = wip_ty.index }); 21351 try sema.addTypeReferenceEntry(src, wip_ty.index); 21352 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 21353 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 21354 } 21355 21356 fn reifyTuple( 21357 sema: *Sema, 21358 block: *Block, 21359 src: LazySrcLoc, 21360 fields_val: Value, 21361 ) CompileError!Air.Inst.Ref { 21362 const pt = sema.pt; 21363 const zcu = pt.zcu; 21364 const gpa = sema.gpa; 21365 const ip = &zcu.intern_pool; 21366 21367 const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); 21368 21369 const types = try sema.arena.alloc(InternPool.Index, fields_len); 21370 const inits = try sema.arena.alloc(InternPool.Index, fields_len); 21371 21372 for (types, inits, 0..) |*field_ty, *field_init, field_idx| { 21373 const field_info = try fields_val.elemValue(pt, field_idx); 21374 21375 const field_name_val = try field_info.fieldValue(pt, 0); 21376 const field_type_val = try field_info.fieldValue(pt, 1); 21377 const field_default_value_val = try field_info.fieldValue(pt, 2); 21378 const field_is_comptime_val = try field_info.fieldValue(pt, 3); 21379 const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4)); 21380 21381 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .tuple_field_name }); 21382 const field_type = field_type_val.toType(); 21383 const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: { 21384 const ptr_ty = try pt.singleConstPtrType(field_type_val.toType()); 21385 // We need to do this deref here, so we won't check for this error case later on. 21386 const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( 21387 block, 21388 src, 21389 .{ .simple = .tuple_field_default_value }, 21390 ); 21391 // Resolve the value so that lazy values do not create distinct types. 21392 break :d (try sema.resolveLazyValue(val)).toIntern(); 21393 } else .none; 21394 21395 const field_name_index = field_name.toUnsigned(ip) orelse return sema.fail( 21396 block, 21397 src, 21398 "tuple cannot have non-numeric field '{f}'", 21399 .{field_name.fmt(ip)}, 21400 ); 21401 if (field_name_index != field_idx) { 21402 return sema.fail( 21403 block, 21404 src, 21405 "tuple field name '{d}' does not match field index {d}", 21406 .{ field_name_index, field_idx }, 21407 ); 21408 } 21409 21410 try sema.validateTupleFieldType(block, field_type, src); 21411 21412 { 21413 const alignment_ok = ok: { 21414 if (field_alignment_val.toIntern() == .zero) break :ok true; 21415 const given_align = try field_alignment_val.getUnsignedIntSema(pt) orelse break :ok false; 21416 const abi_align = (try field_type.abiAlignmentSema(pt)).toByteUnits() orelse 0; 21417 break :ok abi_align == given_align; 21418 }; 21419 if (!alignment_ok) { 21420 return sema.fail(block, src, "tuple fields cannot specify alignment", .{}); 21421 } 21422 } 21423 21424 if (field_is_comptime_val.toBool() and field_default_value == .none) { 21425 return sema.fail(block, src, "comptime field without default initialization value", .{}); 21426 } 21427 21428 if (!field_is_comptime_val.toBool() and field_default_value != .none) { 21429 return sema.fail(block, src, "non-comptime tuple fields cannot specify default initialization value", .{}); 21430 } 21431 21432 const default_or_opv: InternPool.Index = default: { 21433 if (field_default_value != .none) { 21434 break :default field_default_value; 21435 } 21436 if (try sema.typeHasOnePossibleValue(field_type)) |opv| { 21437 break :default opv.toIntern(); 21438 } 21439 break :default .none; 21440 }; 21441 21442 field_ty.* = field_type.toIntern(); 21443 field_init.* = default_or_opv; 21444 } 21445 21446 return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{ 21447 .types = types, 21448 .values = inits, 21449 })); 21450 } 21451 21452 fn reifyStruct( 21453 sema: *Sema, 21454 block: *Block, 21455 inst: Zir.Inst.Index, 21456 src: LazySrcLoc, 21457 layout: std.builtin.Type.ContainerLayout, 21458 opt_backing_int_val: Value, 21459 fields_val: Value, 21460 name_strategy: Zir.Inst.NameStrategy, 21461 ) CompileError!Air.Inst.Ref { 21462 const pt = sema.pt; 21463 const zcu = pt.zcu; 21464 const gpa = sema.gpa; 21465 const ip = &zcu.intern_pool; 21466 21467 // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`. 21468 21469 const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); 21470 21471 // The validation work here is non-trivial, and it's possible the type already exists. 21472 // So in this first pass, let's just construct a hash to optimize for this case. If the 21473 // inputs turn out to be invalid, we can cancel the WIP type later. 21474 21475 // For deduplication purposes, we must create a hash including all details of this type. 21476 // TODO: use a longer hash! 21477 var hasher = std.hash.Wyhash.init(0); 21478 std.hash.autoHash(&hasher, layout); 21479 std.hash.autoHash(&hasher, opt_backing_int_val.toIntern()); 21480 std.hash.autoHash(&hasher, fields_len); 21481 21482 var any_comptime_fields = false; 21483 var any_default_inits = false; 21484 21485 for (0..fields_len) |field_idx| { 21486 const field_info = try fields_val.elemValue(pt, field_idx); 21487 21488 const field_name_val = try field_info.fieldValue(pt, 0); 21489 const field_type_val = try field_info.fieldValue(pt, 1); 21490 const field_default_value_val = try field_info.fieldValue(pt, 2); 21491 const field_is_comptime_val = try field_info.fieldValue(pt, 3); 21492 const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4)); 21493 21494 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .struct_field_name }); 21495 const field_is_comptime = field_is_comptime_val.toBool(); 21496 const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: { 21497 const ptr_ty = try pt.singleConstPtrType(field_type_val.toType()); 21498 // We need to do this deref here, so we won't check for this error case later on. 21499 const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( 21500 block, 21501 src, 21502 .{ .simple = .struct_field_default_value }, 21503 ); 21504 // Resolve the value so that lazy values do not create distinct types. 21505 break :d (try sema.resolveLazyValue(val)).toIntern(); 21506 } else .none; 21507 21508 std.hash.autoHash(&hasher, .{ 21509 field_name, 21510 field_type_val.toIntern(), 21511 field_default_value, 21512 field_is_comptime, 21513 field_alignment_val.toIntern(), 21514 }); 21515 21516 if (field_is_comptime) any_comptime_fields = true; 21517 if (field_default_value != .none) any_default_inits = true; 21518 } 21519 21520 const tracked_inst = try block.trackZir(inst); 21521 21522 const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{ 21523 .layout = layout, 21524 .fields_len = fields_len, 21525 .known_non_opv = false, 21526 .requires_comptime = .unknown, 21527 .any_comptime_fields = any_comptime_fields, 21528 .any_default_inits = any_default_inits, 21529 .any_aligned_fields = layout != .@"packed", 21530 .inits_resolved = true, 21531 .key = .{ .reified = .{ 21532 .zir_index = tracked_inst, 21533 .type_hash = hasher.final(), 21534 } }, 21535 }, false)) { 21536 .wip => |wip| wip, 21537 .existing => |ty| { 21538 try sema.declareDependency(.{ .interned = ty }); 21539 try sema.addTypeReferenceEntry(src, ty); 21540 return Air.internedToRef(ty); 21541 }, 21542 }; 21543 errdefer wip_ty.cancel(ip, pt.tid); 21544 21545 const type_name = try sema.createTypeName( 21546 block, 21547 name_strategy, 21548 "struct", 21549 inst, 21550 wip_ty.index, 21551 ); 21552 wip_ty.setName(ip, type_name.name, type_name.nav); 21553 21554 const struct_type = ip.loadStructType(wip_ty.index); 21555 21556 for (0..fields_len) |field_idx| { 21557 const field_info = try fields_val.elemValue(pt, field_idx); 21558 21559 const field_name_val = try field_info.fieldValue(pt, 0); 21560 const field_type_val = try field_info.fieldValue(pt, 1); 21561 const field_default_value_val = try field_info.fieldValue(pt, 2); 21562 const field_is_comptime_val = try field_info.fieldValue(pt, 3); 21563 const field_alignment_val = try field_info.fieldValue(pt, 4); 21564 21565 const field_ty = field_type_val.toType(); 21566 // Don't pass a reason; first loop acts as an assertion that this is valid. 21567 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); 21568 if (struct_type.addFieldName(ip, field_name)) |prev_index| { 21569 _ = prev_index; // TODO: better source location 21570 return sema.fail(block, src, "duplicate struct field name {f}", .{field_name.fmt(ip)}); 21571 } 21572 21573 if (!try sema.intFitsInType(field_alignment_val, align_ty, null)) { 21574 return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)}); 21575 } 21576 const byte_align = try field_alignment_val.toUnsignedIntSema(pt); 21577 if (layout == .@"packed") { 21578 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed struct field must be set to 0", .{}); 21579 } else { 21580 struct_type.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); 21581 } 21582 21583 const field_is_comptime = field_is_comptime_val.toBool(); 21584 if (field_is_comptime) { 21585 assert(any_comptime_fields); 21586 switch (layout) { 21587 .@"extern" => return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}), 21588 .@"packed" => return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}), 21589 .auto => struct_type.setFieldComptime(ip, field_idx), 21590 } 21591 } 21592 21593 const field_default: InternPool.Index = d: { 21594 if (!any_default_inits) break :d .none; 21595 const ptr_val = field_default_value_val.optionalValue(zcu) orelse break :d .none; 21596 const ptr_ty = try pt.singleConstPtrType(field_ty); 21597 // Asserted comptime-dereferencable above. 21598 const val = (try sema.pointerDeref(block, src, ptr_val, ptr_ty)).?; 21599 // We already resolved this for deduplication, so we may as well do it now. 21600 break :d (try sema.resolveLazyValue(val)).toIntern(); 21601 }; 21602 21603 if (field_is_comptime and field_default == .none) { 21604 return sema.fail(block, src, "comptime field without default initialization value", .{}); 21605 } 21606 21607 struct_type.field_types.get(ip)[field_idx] = field_type_val.toIntern(); 21608 if (field_default != .none) { 21609 struct_type.field_inits.get(ip)[field_idx] = field_default; 21610 } 21611 21612 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 21613 return sema.failWithOwnedErrorMsg(block, msg: { 21614 const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 21615 errdefer msg.destroy(gpa); 21616 21617 try sema.addDeclaredHereNote(msg, field_ty); 21618 break :msg msg; 21619 }); 21620 } 21621 if (field_ty.zigTypeTag(zcu) == .noreturn) { 21622 return sema.failWithOwnedErrorMsg(block, msg: { 21623 const msg = try sema.errMsg(src, "struct fields cannot be 'noreturn'", .{}); 21624 errdefer msg.destroy(gpa); 21625 21626 try sema.addDeclaredHereNote(msg, field_ty); 21627 break :msg msg; 21628 }); 21629 } 21630 if (layout == .@"extern" and !try sema.validateExternType(field_ty, .struct_field)) { 21631 return sema.failWithOwnedErrorMsg(block, msg: { 21632 const msg = try sema.errMsg(src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 21633 errdefer msg.destroy(gpa); 21634 21635 try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .struct_field); 21636 21637 try sema.addDeclaredHereNote(msg, field_ty); 21638 break :msg msg; 21639 }); 21640 } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { 21641 return sema.failWithOwnedErrorMsg(block, msg: { 21642 const msg = try sema.errMsg(src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 21643 errdefer msg.destroy(gpa); 21644 21645 try sema.explainWhyTypeIsNotPacked(msg, src, field_ty); 21646 21647 try sema.addDeclaredHereNote(msg, field_ty); 21648 break :msg msg; 21649 }); 21650 } 21651 } 21652 21653 if (layout == .@"packed") { 21654 var fields_bit_sum: u64 = 0; 21655 for (0..struct_type.field_types.len) |field_idx| { 21656 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_idx]); 21657 field_ty.resolveLayout(pt) catch |err| switch (err) { 21658 error.AnalysisFail => { 21659 const msg = sema.err orelse return err; 21660 try sema.errNote(src, msg, "while checking a field of this struct", .{}); 21661 return err; 21662 }, 21663 else => return err, 21664 }; 21665 fields_bit_sum += field_ty.bitSize(zcu); 21666 } 21667 21668 if (opt_backing_int_val.optionalValue(zcu)) |backing_int_val| { 21669 const backing_int_ty = backing_int_val.toType(); 21670 try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); 21671 struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); 21672 } else { 21673 const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum)); 21674 struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); 21675 } 21676 } 21677 21678 const new_namespace_index = try pt.createNamespace(.{ 21679 .parent = block.namespace.toOptional(), 21680 .owner_type = wip_ty.index, 21681 .file_scope = block.getFileScopeIndex(zcu), 21682 .generation = zcu.generation, 21683 }); 21684 21685 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 21686 codegen_type: { 21687 if (zcu.comp.config.use_llvm) break :codegen_type; 21688 if (block.ownerModule().strip) break :codegen_type; 21689 // This job depends on any resolve_type_fully jobs queued up before it. 21690 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 21691 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 21692 } 21693 try sema.declareDependency(.{ .interned = wip_ty.index }); 21694 try sema.addTypeReferenceEntry(src, wip_ty.index); 21695 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 21696 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 21697 } 21698 21699 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { 21700 const pt = sema.pt; 21701 const va_list_ty = try sema.getBuiltinType(src, .VaList); 21702 const va_list_ptr = try pt.singleMutPtrType(va_list_ty); 21703 21704 const inst = try sema.resolveInst(zir_ref); 21705 return sema.coerce(block, va_list_ptr, inst, src); 21706 } 21707 21708 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21709 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 21710 const src = block.nodeOffset(extra.node); 21711 const va_list_src = block.builtinCallArgSrc(extra.node, 0); 21712 const ty_src = block.builtinCallArgSrc(extra.node, 1); 21713 21714 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs); 21715 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs); 21716 21717 if (!try sema.validateExternType(arg_ty, .param_ty)) { 21718 const msg = msg: { 21719 const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)}); 21720 errdefer msg.destroy(sema.gpa); 21721 21722 try sema.explainWhyTypeIsNotExtern(msg, ty_src, arg_ty, .param_ty); 21723 21724 try sema.addDeclaredHereNote(msg, arg_ty); 21725 break :msg msg; 21726 }; 21727 return sema.failWithOwnedErrorMsg(block, msg); 21728 } 21729 21730 try sema.requireRuntimeBlock(block, src, null); 21731 return block.addTyOp(.c_va_arg, arg_ty, va_list_ref); 21732 } 21733 21734 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21735 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 21736 const src = block.nodeOffset(extra.node); 21737 const va_list_src = block.builtinCallArgSrc(extra.node, 0); 21738 21739 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 21740 const va_list_ty = try sema.getBuiltinType(src, .VaList); 21741 21742 try sema.requireRuntimeBlock(block, src, null); 21743 return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref); 21744 } 21745 21746 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21747 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 21748 const src = block.nodeOffset(extra.node); 21749 const va_list_src = block.builtinCallArgSrc(extra.node, 0); 21750 21751 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 21752 21753 try sema.requireRuntimeBlock(block, src, null); 21754 return block.addUnOp(.c_va_end, va_list_ref); 21755 } 21756 21757 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21758 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 21759 const src = block.nodeOffset(src_node); 21760 21761 const va_list_ty = try sema.getBuiltinType(src, .VaList); 21762 try sema.requireRuntimeBlock(block, src, null); 21763 return block.addInst(.{ 21764 .tag = .c_va_start, 21765 .data = .{ .ty = va_list_ty }, 21766 }); 21767 } 21768 21769 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21770 const pt = sema.pt; 21771 const zcu = pt.zcu; 21772 const ip = &zcu.intern_pool; 21773 21774 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 21775 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21776 const ty = try sema.resolveType(block, ty_src, inst_data.operand); 21777 21778 const type_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{f}", .{ty.fmt(pt)}, .no_embedded_nulls); 21779 return sema.addNullTerminatedStrLit(type_name); 21780 } 21781 21782 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21783 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 21784 const src = block.nodeOffset(inst_data.src_node); 21785 return sema.failWithUseOfAsync(block, src); 21786 } 21787 21788 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21789 const pt = sema.pt; 21790 const zcu = pt.zcu; 21791 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21792 const src = block.nodeOffset(inst_data.src_node); 21793 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21794 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21795 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat"); 21796 const operand = try sema.resolveInst(extra.rhs); 21797 const operand_ty = sema.typeOf(operand); 21798 21799 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 21800 const is_vector = dest_ty.zigTypeTag(zcu) == .vector; 21801 21802 const dest_scalar_ty = dest_ty.scalarType(zcu); 21803 const operand_scalar_ty = operand_ty.scalarType(zcu); 21804 21805 _ = try sema.checkIntType(block, src, dest_scalar_ty); 21806 try sema.checkFloatType(block, operand_src, operand_scalar_ty); 21807 21808 if (try sema.resolveValue(operand)) |operand_val| { 21809 const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate); 21810 return Air.internedToRef(result_val.toIntern()); 21811 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { 21812 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int }); 21813 } 21814 21815 try sema.requireRuntimeBlock(block, src, operand_src); 21816 if (dest_scalar_ty.intInfo(zcu).bits == 0) { 21817 if (block.wantSafety()) { 21818 // Emit an explicit safety check. We can do this one like `abs(x) < 1`. 21819 const abs_ref = try block.addTyOp(.abs, operand_ty, operand); 21820 const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref; 21821 const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern()); 21822 const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref); 21823 try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds); 21824 } 21825 const scalar_val = try pt.intValue(dest_scalar_ty, 0); 21826 return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern()); 21827 } 21828 if (block.wantSafety()) { 21829 try sema.preparePanicId(src, .integer_part_out_of_bounds); 21830 return block.addTyOp(switch (block.float_mode) { 21831 .optimized => .int_from_float_optimized_safe, 21832 .strict => .int_from_float_safe, 21833 }, dest_ty, operand); 21834 } 21835 return block.addTyOp(switch (block.float_mode) { 21836 .optimized => .int_from_float_optimized, 21837 .strict => .int_from_float, 21838 }, dest_ty, operand); 21839 } 21840 21841 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21842 const pt = sema.pt; 21843 const zcu = pt.zcu; 21844 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21845 const src = block.nodeOffset(inst_data.src_node); 21846 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21847 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21848 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt"); 21849 const operand = try sema.resolveInst(extra.rhs); 21850 const operand_ty = sema.typeOf(operand); 21851 21852 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 21853 21854 const dest_scalar_ty = dest_ty.scalarType(zcu); 21855 const operand_scalar_ty = operand_ty.scalarType(zcu); 21856 21857 try sema.checkFloatType(block, src, dest_scalar_ty); 21858 _ = try sema.checkIntType(block, operand_src, operand_scalar_ty); 21859 21860 if (try sema.resolveValue(operand)) |operand_val| { 21861 const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, pt, .sema); 21862 return Air.internedToRef(result_val.toIntern()); 21863 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) { 21864 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_float }); 21865 } 21866 21867 try sema.requireRuntimeBlock(block, src, operand_src); 21868 return block.addTyOp(.float_from_int, dest_ty, operand); 21869 } 21870 21871 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21872 const pt = sema.pt; 21873 const zcu = pt.zcu; 21874 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21875 const src = block.nodeOffset(inst_data.src_node); 21876 21877 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21878 21879 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21880 const operand_res = try sema.resolveInst(extra.rhs); 21881 21882 const uncoerced_operand_ty = sema.typeOf(operand_res); 21883 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt"); 21884 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src); 21885 21886 const is_vector = dest_ty.zigTypeTag(zcu) == .vector; 21887 const operand_ty: Type = if (is_vector) operand_ty: { 21888 const len = dest_ty.vectorLen(zcu); 21889 break :operand_ty try pt.vectorType(.{ .child = .usize_type, .len = len }); 21890 } else .usize; 21891 21892 const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src); 21893 21894 const ptr_ty = dest_ty.scalarType(zcu); 21895 try sema.checkPtrType(block, src, ptr_ty, true); 21896 21897 const elem_ty = ptr_ty.elemType2(zcu); 21898 const ptr_align = try ptr_ty.ptrAlignmentSema(pt); 21899 21900 if (ptr_ty.isSlice(zcu)) { 21901 const msg = msg: { 21902 const msg = try sema.errMsg(src, "integer cannot be converted to slice type '{f}'", .{ptr_ty.fmt(pt)}); 21903 errdefer msg.destroy(sema.gpa); 21904 try sema.errNote(src, msg, "slice length cannot be inferred from address", .{}); 21905 break :msg msg; 21906 }; 21907 return sema.failWithOwnedErrorMsg(block, msg); 21908 } 21909 21910 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { 21911 if (!is_vector) { 21912 const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align, null); 21913 return Air.internedToRef(ptr_val.toIntern()); 21914 } 21915 const len = dest_ty.vectorLen(zcu); 21916 const new_elems = try sema.arena.alloc(InternPool.Index, len); 21917 for (new_elems, 0..) |*new_elem, elem_idx| { 21918 const elem = try val.elemValue(pt, elem_idx); 21919 const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align, elem_idx); 21920 new_elem.* = ptr_val.toIntern(); 21921 } 21922 return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); 21923 } 21924 if (try ptr_ty.comptimeOnlySema(pt)) { 21925 return sema.failWithOwnedErrorMsg(block, msg: { 21926 const msg = try sema.errMsg(src, "pointer to comptime-only type '{f}' must be comptime-known, but operand is runtime-known", .{ptr_ty.fmt(pt)}); 21927 errdefer msg.destroy(sema.gpa); 21928 21929 try sema.explainWhyTypeIsComptime(msg, src, ptr_ty); 21930 break :msg msg; 21931 }); 21932 } 21933 try sema.requireRuntimeBlock(block, src, operand_src); 21934 try sema.checkLogicalPtrOperation(block, src, ptr_ty); 21935 if (block.wantSafety() and (try elem_ty.hasRuntimeBitsSema(pt) or elem_ty.zigTypeTag(zcu) == .@"fn")) { 21936 if (!ptr_ty.isAllowzeroPtr(zcu)) { 21937 const is_non_zero = if (is_vector) all_non_zero: { 21938 const zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern()); 21939 const is_non_zero = try block.addCmpVector(operand_coerced, zero_usize, .neq); 21940 break :all_non_zero try block.addReduce(is_non_zero, .And); 21941 } else try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); 21942 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null); 21943 } 21944 if (ptr_align.compare(.gt, .@"1")) { 21945 const align_bytes_minus_1 = ptr_align.toByteUnits().? - 1; 21946 const align_mask = Air.internedToRef((try sema.splat(operand_ty, try pt.intValue( 21947 .usize, 21948 if (elem_ty.fnPtrMaskOrNull(zcu)) |mask| 21949 align_bytes_minus_1 & mask 21950 else 21951 align_bytes_minus_1, 21952 ))).toIntern()); 21953 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_mask); 21954 const is_aligned = if (is_vector) all_aligned: { 21955 const splat_zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern()); 21956 const is_aligned = try block.addCmpVector(remainder, splat_zero_usize, .eq); 21957 break :all_aligned try block.addReduce(is_aligned, .And); 21958 } else try block.addBinOp(.cmp_eq, remainder, .zero_usize); 21959 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment); 21960 } 21961 } 21962 return block.addBitCast(dest_ty, operand_coerced); 21963 } 21964 21965 fn ptrFromIntVal( 21966 sema: *Sema, 21967 block: *Block, 21968 operand_src: LazySrcLoc, 21969 operand_val: Value, 21970 ptr_ty: Type, 21971 ptr_align: Alignment, 21972 vec_idx: ?usize, 21973 ) !Value { 21974 const pt = sema.pt; 21975 const zcu = pt.zcu; 21976 if (operand_val.isUndef(zcu)) { 21977 if (ptr_ty.isAllowzeroPtr(zcu) and ptr_align == .@"1") { 21978 return pt.undefValue(ptr_ty); 21979 } 21980 return sema.failWithUseOfUndef(block, operand_src, vec_idx); 21981 } 21982 const addr = try operand_val.toUnsignedIntSema(pt); 21983 if (!ptr_ty.isAllowzeroPtr(zcu) and addr == 0) 21984 return sema.fail(block, operand_src, "pointer type '{f}' does not allow address zero", .{ptr_ty.fmt(pt)}); 21985 if (addr != 0 and ptr_align != .none) { 21986 const masked_addr = if (ptr_ty.childType(zcu).fnPtrMaskOrNull(zcu)) |mask| 21987 addr & mask 21988 else 21989 addr; 21990 21991 if (!ptr_align.check(masked_addr)) { 21992 return sema.fail(block, operand_src, "pointer type '{f}' requires aligned address", .{ptr_ty.fmt(pt)}); 21993 } 21994 } 21995 21996 return switch (ptr_ty.zigTypeTag(zcu)) { 21997 .optional => Value.fromInterned(try pt.intern(.{ .opt = .{ 21998 .ty = ptr_ty.toIntern(), 21999 .val = if (addr == 0) .none else (try pt.ptrIntValue(ptr_ty.childType(zcu), addr)).toIntern(), 22000 } })), 22001 .pointer => try pt.ptrIntValue(ptr_ty, addr), 22002 else => unreachable, 22003 }; 22004 } 22005 22006 fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 22007 const pt = sema.pt; 22008 const zcu = pt.zcu; 22009 const ip = &zcu.intern_pool; 22010 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 22011 const src = block.nodeOffset(extra.node); 22012 const operand_src = block.builtinCallArgSrc(extra.node, 0); 22013 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); 22014 const operand = try sema.resolveInst(extra.rhs); 22015 const operand_ty = sema.typeOf(operand); 22016 22017 const dest_tag = dest_ty.zigTypeTag(zcu); 22018 const operand_tag = operand_ty.zigTypeTag(zcu); 22019 22020 if (dest_tag != .error_set and dest_tag != .error_union) { 22021 return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)}); 22022 } 22023 if (operand_tag != .error_set and operand_tag != .error_union) { 22024 return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(operand_tag)}); 22025 } 22026 if (dest_tag == .error_set and operand_tag == .error_union) { 22027 return sema.fail(block, src, "cannot cast an error union type to error set", .{}); 22028 } 22029 if (dest_tag == .error_union and operand_tag == .error_union and 22030 dest_ty.errorUnionPayload(zcu).toIntern() != operand_ty.errorUnionPayload(zcu).toIntern()) 22031 { 22032 return sema.failWithOwnedErrorMsg(block, msg: { 22033 const msg = try sema.errMsg(src, "payload types of error unions must match", .{}); 22034 errdefer msg.destroy(sema.gpa); 22035 const dest_payload_ty = dest_ty.errorUnionPayload(zcu); 22036 const operand_payload_ty = operand_ty.errorUnionPayload(zcu); 22037 try sema.errNote(src, msg, "destination payload is '{f}'", .{dest_payload_ty.fmt(pt)}); 22038 try sema.errNote(src, msg, "operand payload is '{f}'", .{operand_payload_ty.fmt(pt)}); 22039 try addDeclaredHereNote(sema, msg, dest_ty); 22040 try addDeclaredHereNote(sema, msg, operand_ty); 22041 break :msg msg; 22042 }); 22043 } 22044 const dest_err_ty = switch (dest_tag) { 22045 .error_union => dest_ty.errorUnionSet(zcu), 22046 .error_set => dest_ty, 22047 else => unreachable, 22048 }; 22049 const operand_err_ty = switch (operand_tag) { 22050 .error_union => operand_ty.errorUnionSet(zcu), 22051 .error_set => operand_ty, 22052 else => unreachable, 22053 }; 22054 22055 const disjoint = disjoint: { 22056 // Try avoiding resolving inferred error sets if we can 22057 if (!dest_err_ty.isAnyError(zcu) and dest_err_ty.errorSetIsEmpty(zcu)) break :disjoint true; 22058 if (!operand_err_ty.isAnyError(zcu) and operand_err_ty.errorSetIsEmpty(zcu)) break :disjoint true; 22059 if (dest_err_ty.isAnyError(zcu)) break :disjoint false; 22060 if (operand_err_ty.isAnyError(zcu)) break :disjoint false; 22061 const dest_err_names = dest_err_ty.errorSetNames(zcu); 22062 for (0..dest_err_names.len) |dest_err_index| { 22063 if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) 22064 break :disjoint false; 22065 } 22066 22067 if (!ip.isInferredErrorSetType(dest_err_ty.toIntern()) and 22068 !ip.isInferredErrorSetType(operand_err_ty.toIntern())) 22069 { 22070 break :disjoint true; 22071 } 22072 22073 _ = try sema.resolveInferredErrorSetTy(block, src, dest_err_ty.toIntern()); 22074 _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_err_ty.toIntern()); 22075 for (0..dest_err_names.len) |dest_err_index| { 22076 if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) 22077 break :disjoint false; 22078 } 22079 22080 break :disjoint true; 22081 }; 22082 if (disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) { 22083 return sema.fail(block, src, "error sets '{f}' and '{f}' have no common errors", .{ 22084 operand_err_ty.fmt(pt), dest_err_ty.fmt(pt), 22085 }); 22086 } 22087 22088 // operand must be defined since it can be an invalid error value 22089 if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| { 22090 const err_name: InternPool.NullTerminatedString = switch (operand_tag) { 22091 .error_set => ip.indexToKey(operand_val.toIntern()).err.name, 22092 .error_union => switch (ip.indexToKey(operand_val.toIntern()).error_union.val) { 22093 .err_name => |name| name, 22094 .payload => |payload_val| { 22095 assert(dest_tag == .error_union); // should be guaranteed from the type checks above 22096 return sema.coerce(block, dest_ty, Air.internedToRef(payload_val), operand_src); 22097 }, 22098 }, 22099 else => unreachable, 22100 }; 22101 22102 if (!dest_err_ty.isAnyError(zcu) and !Type.errorSetHasFieldIp(ip, dest_err_ty.toIntern(), err_name)) { 22103 return sema.fail(block, src, "'error.{f}' not a member of error set '{f}'", .{ 22104 err_name.fmt(ip), dest_err_ty.fmt(pt), 22105 }); 22106 } 22107 22108 return Air.internedToRef(try pt.intern(switch (dest_tag) { 22109 .error_set => .{ .err = .{ 22110 .ty = dest_ty.toIntern(), 22111 .name = err_name, 22112 } }, 22113 .error_union => .{ .error_union = .{ 22114 .ty = dest_ty.toIntern(), 22115 .val = .{ .err_name = err_name }, 22116 } }, 22117 else => unreachable, 22118 })); 22119 } 22120 22121 const err_int_ty = try pt.errorIntType(); 22122 if (block.wantSafety() and !dest_err_ty.isAnyError(zcu) and 22123 dest_err_ty.toIntern() != .adhoc_inferred_error_set_type and 22124 zcu.backendSupportsFeature(.error_set_has_value)) 22125 { 22126 const err_code_inst = switch (operand_tag) { 22127 .error_set => operand, 22128 .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand), 22129 else => unreachable, 22130 }; 22131 const err_int_inst = try block.addBitCast(err_int_ty, err_code_inst); 22132 22133 if (dest_tag == .error_union) { 22134 const zero_err = try pt.intRef(err_int_ty, 0); 22135 const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err); 22136 if (disjoint) { 22137 // Error must be zero. 22138 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code); 22139 } else { 22140 // Error must be in destination set or zero. 22141 const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst); 22142 const ok = try block.addBinOp(.bool_or, has_value, is_zero); 22143 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 22144 } 22145 } else { 22146 const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst); 22147 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 22148 } 22149 } 22150 22151 if (operand_tag == .error_set and dest_tag == .error_union) { 22152 const err_val = try block.addBitCast(dest_err_ty, operand); 22153 return block.addTyOp(.wrap_errunion_err, dest_ty, err_val); 22154 } else { 22155 return block.addBitCast(dest_ty, operand); 22156 } 22157 } 22158 22159 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 22160 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 22161 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); 22162 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 22163 const src = block.nodeOffset(extra.node); 22164 const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node }); 22165 const operand = try sema.resolveInst(extra.rhs); 22166 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName()); 22167 return sema.ptrCastFull( 22168 block, 22169 flags, 22170 src, 22171 operand, 22172 operand_src, 22173 dest_ty, 22174 flags.needResultTypeBuiltinName(), 22175 ); 22176 } 22177 22178 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22179 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 22180 const src = block.nodeOffset(inst_data.src_node); 22181 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22182 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22183 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast"); 22184 const operand = try sema.resolveInst(extra.rhs); 22185 22186 return sema.ptrCastFull( 22187 block, 22188 .{ .ptr_cast = true }, 22189 src, 22190 operand, 22191 operand_src, 22192 dest_ty, 22193 "@ptrCast", 22194 ); 22195 } 22196 22197 fn ptrCastFull( 22198 sema: *Sema, 22199 block: *Block, 22200 flags: Zir.Inst.FullPtrCastFlags, 22201 src: LazySrcLoc, 22202 operand: Air.Inst.Ref, 22203 operand_src: LazySrcLoc, 22204 dest_ty: Type, 22205 operation: []const u8, 22206 ) CompileError!Air.Inst.Ref { 22207 const pt = sema.pt; 22208 const zcu = pt.zcu; 22209 const operand_ty = sema.typeOf(operand); 22210 22211 try sema.checkPtrType(block, src, dest_ty, true); 22212 try sema.checkPtrOperand(block, operand_src, operand_ty); 22213 22214 const src_info = operand_ty.ptrInfo(zcu); 22215 const dest_info = dest_ty.ptrInfo(zcu); 22216 22217 try Type.fromInterned(src_info.child).resolveLayout(pt); 22218 try Type.fromInterned(dest_info.child).resolveLayout(pt); 22219 22220 const DestSliceLen = union(enum) { 22221 undef, 22222 constant: u64, 22223 equal_runtime_src_slice, 22224 change_runtime_src_slice: struct { 22225 bytes_per_src: u64, 22226 bytes_per_dest: u64, 22227 }, 22228 }; 22229 // Populated iff the destination type is a slice. 22230 const dest_slice_len: ?DestSliceLen = len: { 22231 switch (dest_info.flags.size) { 22232 .slice => {}, 22233 .many, .c, .one => break :len null, 22234 } 22235 // A `null` length means the operand is a runtime-known slice (so the length is runtime-known). 22236 // `src_elem_type` is different from `src_info.child` if the latter is an array, to ensure we ignore sentinels. 22237 const src_elem_ty: Type, const opt_src_len: ?u64 = switch (src_info.flags.size) { 22238 .one => src: { 22239 const true_child: Type = .fromInterned(src_info.child); 22240 break :src switch (true_child.zigTypeTag(zcu)) { 22241 .array => .{ true_child.childType(zcu), true_child.arrayLen(zcu) }, 22242 else => .{ true_child, 1 }, 22243 }; 22244 }, 22245 .slice => src: { 22246 const operand_val = try sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null }; 22247 if (operand_val.isUndef(zcu)) break :len .undef; 22248 const slice_val = switch (operand_ty.zigTypeTag(zcu)) { 22249 .optional => operand_val.optionalValue(zcu) orelse break :len .undef, 22250 .pointer => operand_val, 22251 else => unreachable, 22252 }; 22253 const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern()))); 22254 if (slice_len_resolved.isUndef(zcu)) break :len .undef; 22255 break :src .{ .fromInterned(src_info.child), slice_len_resolved.toUnsignedInt(zcu) }; 22256 }, 22257 .many, .c => { 22258 return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)}); 22259 }, 22260 }; 22261 const dest_elem_ty: Type = .fromInterned(dest_info.child); 22262 if (dest_elem_ty.toIntern() == src_elem_ty.toIntern()) { 22263 break :len if (opt_src_len) |l| .{ .constant = l } else .equal_runtime_src_slice; 22264 } 22265 if (!src_elem_ty.comptimeOnly(zcu) and !dest_elem_ty.comptimeOnly(zcu)) { 22266 const src_elem_size = src_elem_ty.abiSize(zcu); 22267 const dest_elem_size = dest_elem_ty.abiSize(zcu); 22268 if (dest_elem_size == 0) { 22269 return sema.fail(block, src, "cannot infer length of slice of zero-bit '{f}' from '{f}'", .{ 22270 dest_elem_ty.fmt(pt), operand_ty.fmt(pt), 22271 }); 22272 } 22273 if (opt_src_len) |src_len| { 22274 const bytes = src_len * src_elem_size; 22275 const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch switch (src_info.flags.size) { 22276 .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), 22277 .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{Type.fromInterned(src_info.child).fmt(pt)}), 22278 else => unreachable, 22279 }; 22280 break :len .{ .constant = dest_len }; 22281 } 22282 assert(src_info.flags.size == .slice); 22283 break :len .{ .change_runtime_src_slice = .{ 22284 .bytes_per_src = src_elem_size, 22285 .bytes_per_dest = dest_elem_size, 22286 } }; 22287 } 22288 // We apply rules for comptime memory consistent with comptime loads/stores, where arrays of 22289 // comptime-only types can be "restructured". 22290 const dest_base_ty: Type, const dest_base_per_elem: u64 = dest_elem_ty.arrayBase(zcu); 22291 const src_base_ty: Type, const src_base_per_elem: u64 = src_elem_ty.arrayBase(zcu); 22292 // The source value has `src_len * src_base_per_elem` values of type `src_base_ty`. 22293 // The result value will have `dest_len * dest_base_per_elem` values of type `dest_base_ty`. 22294 if (dest_base_ty.toIntern() != src_base_ty.toIntern()) { 22295 return sema.fail(block, src, "cannot infer length of comptime-only '{f}' from incompatible '{f}'", .{ 22296 dest_ty.fmt(pt), operand_ty.fmt(pt), 22297 }); 22298 } 22299 // `src_base_ty` is comptime-only, so `src_elem_ty` is comptime-only, so `operand_ty` is 22300 // comptime-only, so `operand` is comptime-known, so `opt_src_len` is non-`null`. 22301 const src_len = opt_src_len.?; 22302 const base_len = src_len * src_base_per_elem; 22303 const dest_len = std.math.divExact(u64, base_len, dest_base_per_elem) catch switch (src_info.flags.size) { 22304 .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), 22305 .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}), 22306 else => unreachable, 22307 }; 22308 break :len .{ .constant = dest_len }; 22309 }; 22310 22311 // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs 22312 22313 if (!flags.ptr_cast) { 22314 const is_array_ptr_to_slice = b: { 22315 if (dest_info.flags.size != .slice) break :b false; 22316 if (src_info.flags.size != .one) break :b false; 22317 const src_pointer_child: Type = .fromInterned(src_info.child); 22318 if (src_pointer_child.zigTypeTag(zcu) != .array) break :b false; 22319 const src_elem = src_pointer_child.childType(zcu); 22320 break :b src_elem.toIntern() == dest_info.child; 22321 }; 22322 22323 check_size: { 22324 if (src_info.flags.size == dest_info.flags.size) break :check_size; 22325 if (is_array_ptr_to_slice) break :check_size; 22326 if (src_info.flags.size == .c) break :check_size; 22327 if (dest_info.flags.size == .c) break :check_size; 22328 return sema.failWithOwnedErrorMsg(block, msg: { 22329 const msg = try sema.errMsg(src, "cannot implicitly convert {s} to {s}", .{ 22330 pointerSizeString(src_info.flags.size), 22331 pointerSizeString(dest_info.flags.size), 22332 }); 22333 errdefer msg.destroy(sema.gpa); 22334 if (dest_info.flags.size == .many and 22335 (src_info.flags.size == .slice or 22336 (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array))) 22337 { 22338 try sema.errNote(src, msg, "use 'ptr' field to convert slice to many pointer", .{}); 22339 } else { 22340 try sema.errNote(src, msg, "use @ptrCast to change pointer size", .{}); 22341 } 22342 break :msg msg; 22343 }); 22344 } 22345 22346 check_child: { 22347 const src_child: Type = if (dest_info.flags.size == .slice and src_info.flags.size == .one) blk: { 22348 // *[n]T -> []T 22349 break :blk Type.fromInterned(src_info.child).childType(zcu); 22350 } else .fromInterned(src_info.child); 22351 22352 const dest_child: Type = .fromInterned(dest_info.child); 22353 22354 const imc_res = try sema.coerceInMemoryAllowed( 22355 block, 22356 dest_child, 22357 src_child, 22358 !dest_info.flags.is_const, 22359 zcu.getTarget(), 22360 src, 22361 operand_src, 22362 null, 22363 ); 22364 if (imc_res == .ok) break :check_child; 22365 return sema.failWithOwnedErrorMsg(block, msg: { 22366 const msg = try sema.errMsg(src, "pointer element type '{f}' cannot coerce into element type '{f}'", .{ 22367 src_child.fmt(pt), dest_child.fmt(pt), 22368 }); 22369 errdefer msg.destroy(sema.gpa); 22370 try imc_res.report(sema, src, msg); 22371 try sema.errNote(src, msg, "use @ptrCast to cast pointer element type", .{}); 22372 break :msg msg; 22373 }); 22374 } 22375 22376 check_sent: { 22377 if (dest_info.sentinel == .none) break :check_sent; 22378 if (src_info.flags.size == .c) break :check_sent; 22379 if (src_info.sentinel != .none) { 22380 const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_info.sentinel, dest_info.child); 22381 if (dest_info.sentinel == coerced_sent) break :check_sent; 22382 } 22383 if (is_array_ptr_to_slice) { 22384 // [*]nT -> []T 22385 const arr_ty: Type = .fromInterned(src_info.child); 22386 if (arr_ty.sentinel(zcu)) |src_sentinel| { 22387 const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_sentinel.toIntern(), dest_info.child); 22388 if (dest_info.sentinel == coerced_sent) break :check_sent; 22389 } 22390 } 22391 return sema.failWithOwnedErrorMsg(block, msg: { 22392 const msg = if (src_info.sentinel == .none) blk: { 22393 break :blk try sema.errMsg(src, "destination pointer requires '{f}' sentinel", .{ 22394 Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema), 22395 }); 22396 } else blk: { 22397 break :blk try sema.errMsg(src, "pointer sentinel '{f}' cannot coerce into pointer sentinel '{f}'", .{ 22398 Value.fromInterned(src_info.sentinel).fmtValueSema(pt, sema), 22399 Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema), 22400 }); 22401 }; 22402 errdefer msg.destroy(sema.gpa); 22403 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{}); 22404 break :msg msg; 22405 }); 22406 } 22407 22408 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) { 22409 return sema.failWithOwnedErrorMsg(block, msg: { 22410 const msg = try sema.errMsg(src, "pointer host size '{d}' cannot coerce into pointer host size '{d}'", .{ 22411 src_info.packed_offset.host_size, 22412 dest_info.packed_offset.host_size, 22413 }); 22414 errdefer msg.destroy(sema.gpa); 22415 try sema.errNote(src, msg, "use @ptrCast to cast pointer host size", .{}); 22416 break :msg msg; 22417 }); 22418 } 22419 22420 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) { 22421 return sema.failWithOwnedErrorMsg(block, msg: { 22422 const msg = try sema.errMsg(src, "pointer bit offset '{d}' cannot coerce into pointer bit offset '{d}'", .{ 22423 src_info.packed_offset.bit_offset, 22424 dest_info.packed_offset.bit_offset, 22425 }); 22426 errdefer msg.destroy(sema.gpa); 22427 try sema.errNote(src, msg, "use @ptrCast to cast pointer bit offset", .{}); 22428 break :msg msg; 22429 }); 22430 } 22431 22432 check_allowzero: { 22433 const src_allows_zero = operand_ty.ptrAllowsZero(zcu); 22434 const dest_allows_zero = dest_ty.ptrAllowsZero(zcu); 22435 if (!src_allows_zero) break :check_allowzero; 22436 if (dest_allows_zero) break :check_allowzero; 22437 22438 return sema.failWithOwnedErrorMsg(block, msg: { 22439 const msg = try sema.errMsg(src, "'{f}' could have null values which are illegal in type '{f}'", .{ 22440 operand_ty.fmt(pt), 22441 dest_ty.fmt(pt), 22442 }); 22443 errdefer msg.destroy(sema.gpa); 22444 try sema.errNote(src, msg, "use @ptrCast to assert the pointer is not null", .{}); 22445 break :msg msg; 22446 }); 22447 } 22448 22449 // TODO: vector index? 22450 } 22451 22452 const src_align = if (src_info.flags.alignment != .none) 22453 src_info.flags.alignment 22454 else 22455 Type.fromInterned(src_info.child).abiAlignment(zcu); 22456 22457 const dest_align = if (dest_info.flags.alignment != .none) 22458 dest_info.flags.alignment 22459 else 22460 Type.fromInterned(dest_info.child).abiAlignment(zcu); 22461 22462 if (!flags.align_cast) { 22463 if (dest_align.compare(.gt, src_align)) { 22464 return sema.failWithOwnedErrorMsg(block, msg: { 22465 const msg = try sema.errMsg(src, "{s} increases pointer alignment", .{operation}); 22466 errdefer msg.destroy(sema.gpa); 22467 try sema.errNote(operand_src, msg, "'{f}' has alignment '{d}'", .{ 22468 operand_ty.fmt(pt), src_align.toByteUnits() orelse 0, 22469 }); 22470 try sema.errNote(src, msg, "'{f}' has alignment '{d}'", .{ 22471 dest_ty.fmt(pt), dest_align.toByteUnits() orelse 0, 22472 }); 22473 try sema.errNote(src, msg, "use @alignCast to assert pointer alignment", .{}); 22474 break :msg msg; 22475 }); 22476 } 22477 } 22478 22479 if (!flags.addrspace_cast) { 22480 if (src_info.flags.address_space != dest_info.flags.address_space) { 22481 return sema.failWithOwnedErrorMsg(block, msg: { 22482 const msg = try sema.errMsg(src, "{s} changes pointer address space", .{operation}); 22483 errdefer msg.destroy(sema.gpa); 22484 try sema.errNote(operand_src, msg, "'{f}' has address space '{s}'", .{ 22485 operand_ty.fmt(pt), @tagName(src_info.flags.address_space), 22486 }); 22487 try sema.errNote(src, msg, "'{f}' has address space '{s}'", .{ 22488 dest_ty.fmt(pt), @tagName(dest_info.flags.address_space), 22489 }); 22490 try sema.errNote(src, msg, "use @addrSpaceCast to cast pointer address space", .{}); 22491 break :msg msg; 22492 }); 22493 } 22494 } else { 22495 // Some address space casts are always disallowed 22496 if (!target_util.addrSpaceCastIsValid(zcu.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) { 22497 return sema.failWithOwnedErrorMsg(block, msg: { 22498 const msg = try sema.errMsg(src, "invalid address space cast", .{}); 22499 errdefer msg.destroy(sema.gpa); 22500 try sema.errNote(operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{ 22501 @tagName(src_info.flags.address_space), 22502 @tagName(dest_info.flags.address_space), 22503 }); 22504 break :msg msg; 22505 }); 22506 } 22507 } 22508 22509 if (!flags.const_cast) { 22510 if (src_info.flags.is_const and !dest_info.flags.is_const) { 22511 return sema.failWithOwnedErrorMsg(block, msg: { 22512 const msg = try sema.errMsg(src, "{s} discards const qualifier", .{operation}); 22513 errdefer msg.destroy(sema.gpa); 22514 try sema.errNote(src, msg, "use @constCast to discard const qualifier", .{}); 22515 break :msg msg; 22516 }); 22517 } 22518 } 22519 22520 if (!flags.volatile_cast) { 22521 if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) { 22522 return sema.failWithOwnedErrorMsg(block, msg: { 22523 const msg = try sema.errMsg(src, "{s} discards volatile qualifier", .{operation}); 22524 errdefer msg.destroy(sema.gpa); 22525 try sema.errNote(src, msg, "use @volatileCast to discard volatile qualifier", .{}); 22526 break :msg msg; 22527 }); 22528 } 22529 } 22530 22531 // Type validation done -- this cast is okay. Let's do it! 22532 // 22533 // `operand` is a maybe-optional pointer or slice. 22534 // `dest_ty` is a maybe-optional pointer or slice. 22535 // 22536 // We have a few safety checks: 22537 // * if the destination does not allow zero, check the operand is not null / 0 22538 // * if the destination is more aligned than the operand, check the pointer alignment 22539 // * if `slice_needs_len_change`, check the element count divides neatly 22540 22541 ct: { 22542 if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime 22543 const operand_val = try sema.resolveValue(operand) orelse break :ct; 22544 22545 if (operand_val.isUndef(zcu)) { 22546 if (!dest_ty.ptrAllowsZero(zcu)) { 22547 return sema.failWithUseOfUndef(block, operand_src, null); 22548 } 22549 return pt.undefRef(dest_ty); 22550 } 22551 22552 if (operand_val.isNull(zcu)) { 22553 if (!dest_ty.ptrAllowsZero(zcu)) { 22554 return sema.fail(block, operand_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)}); 22555 } 22556 if (dest_ty.zigTypeTag(zcu) == .optional) { 22557 return Air.internedToRef((try pt.nullValue(dest_ty)).toIntern()); 22558 } else { 22559 return Air.internedToRef((try pt.ptrIntValue(dest_ty, 0)).toIntern()); 22560 } 22561 } 22562 22563 const ptr_val: Value = switch (src_info.flags.size) { 22564 .slice => .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.ptr), 22565 .one, .many, .c => operand_val, 22566 }; 22567 22568 if (dest_align.compare(.gt, src_align)) { 22569 if (try ptr_val.getUnsignedIntSema(pt)) |addr| { 22570 const masked_addr = if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask| 22571 addr & mask 22572 else 22573 addr; 22574 22575 if (!dest_align.check(masked_addr)) { 22576 return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ 22577 addr, 22578 dest_align.toByteUnits().?, 22579 }); 22580 } 22581 } 22582 } 22583 22584 if (dest_info.flags.size == .slice) { 22585 // Because the operand is comptime-known and not `null`, the slice length has already been computed: 22586 const len: Value = switch (dest_slice_len.?) { 22587 .undef => .undef_usize, 22588 .constant => |n| try pt.intValue(.usize, n), 22589 .equal_runtime_src_slice => unreachable, 22590 .change_runtime_src_slice => unreachable, 22591 }; 22592 return Air.internedToRef(try pt.intern(.{ .slice = .{ 22593 .ty = dest_ty.toIntern(), 22594 .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), 22595 .len = len.toIntern(), 22596 } })); 22597 } else { 22598 // Any to non-slice 22599 const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty); 22600 return Air.internedToRef(new_ptr_val.toIntern()); 22601 } 22602 } 22603 22604 try sema.validateRuntimeValue(block, operand_src, operand); 22605 22606 const can_cast_to_int = !target_util.arePointersLogical(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu)); 22607 const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); 22608 const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align); 22609 22610 const slice_needs_len_change = if (dest_slice_len) |l| switch (l) { 22611 .undef, .equal_runtime_src_slice => false, 22612 .constant, .change_runtime_src_slice => true, 22613 } else false; 22614 22615 // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer. 22616 const need_operand_ptr = src_info.flags.size != .slice or // we already have it 22617 dest_info.flags.size != .slice or // the result is a raw pointer 22618 need_null_check or // safety check happens on pointer 22619 need_align_check or // safety check happens on pointer 22620 flags.addrspace_cast or // AIR addrspace_cast acts on a pointer 22621 slice_needs_len_change; // to change the length, we reconstruct the slice 22622 22623 // This is not quite just the pointer part of `operand` -- it's also had the address space cast done already. 22624 const operand_ptr: Air.Inst.Ref = ptr: { 22625 if (!need_operand_ptr) break :ptr .none; 22626 // First, just get the pointer. 22627 const pre_addrspace_cast = inner: { 22628 if (src_info.flags.size != .slice) break :inner operand; 22629 if (operand_ty.zigTypeTag(zcu) == .optional) { 22630 break :inner try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty); 22631 } else { 22632 break :inner try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); 22633 } 22634 }; 22635 // Now, do an addrspace cast if necessary! 22636 if (!flags.addrspace_cast) break :ptr pre_addrspace_cast; 22637 22638 const intermediate_ptr_ty = try pt.ptrTypeSema(info: { 22639 var info = src_info; 22640 info.flags.address_space = dest_info.flags.address_space; 22641 break :info info; 22642 }); 22643 const intermediate_ty = if (operand_ty.zigTypeTag(zcu) == .optional) blk: { 22644 break :blk try pt.optionalType(intermediate_ptr_ty.toIntern()); 22645 } else intermediate_ptr_ty; 22646 break :ptr try block.addInst(.{ 22647 .tag = .addrspace_cast, 22648 .data = .{ .ty_op = .{ 22649 .ty = Air.internedToRef(intermediate_ty.toIntern()), 22650 .operand = pre_addrspace_cast, 22651 } }, 22652 }); 22653 }; 22654 22655 // Whether we need to know if the (slice) operand has `len == 0`. 22656 const need_operand_len_is_zero = src_info.flags.size == .slice and 22657 dest_info.flags.size == .slice and 22658 (need_null_check or need_align_check); 22659 // Whether we need to get the (slice) operand's `len`. 22660 const need_operand_len = need_len: { 22661 if (src_info.flags.size != .slice) break :need_len false; 22662 if (dest_info.flags.size != .slice) break :need_len false; 22663 if (need_operand_len_is_zero) break :need_len true; 22664 if (flags.addrspace_cast or slice_needs_len_change) break :need_len true; 22665 break :need_len false; 22666 }; 22667 // `.none` if `!need_operand_len`. 22668 const operand_len: Air.Inst.Ref = len: { 22669 if (!need_operand_len) break :len .none; 22670 break :len try block.addTyOp(.slice_len, .usize, operand); 22671 }; 22672 // `.none` if `!need_operand_len_is_zero`. 22673 const operand_len_is_zero: Air.Inst.Ref = zero: { 22674 if (!need_operand_len_is_zero) break :zero .none; 22675 assert(need_operand_len); 22676 break :zero try block.addBinOp(.cmp_eq, operand_len, .zero_usize); 22677 }; 22678 22679 // `operand_ptr` converted to an integer, for safety checks. 22680 const operand_ptr_int: Air.Inst.Ref = if (need_null_check or need_align_check) i: { 22681 assert(need_operand_ptr); 22682 break :i try block.addBitCast(.usize, operand_ptr); 22683 } else .none; 22684 22685 if (need_null_check) { 22686 assert(operand_ptr_int != .none); 22687 const ptr_is_non_zero = try block.addBinOp(.cmp_neq, operand_ptr_int, .zero_usize); 22688 const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { 22689 break :ok try block.addBinOp(.bool_or, operand_len_is_zero, ptr_is_non_zero); 22690 } else ptr_is_non_zero; 22691 try sema.addSafetyCheck(block, src, ok, .cast_to_null); 22692 } 22693 if (need_align_check) { 22694 assert(operand_ptr_int != .none); 22695 const align_mask = try pt.intRef(.usize, mask: { 22696 const target_ptr_mask = Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu) orelse ~@as(u64, 0); 22697 break :mask (dest_align.toByteUnits().? - 1) & target_ptr_mask; 22698 }); 22699 const ptr_masked = try block.addBinOp(.bit_and, operand_ptr_int, align_mask); 22700 const is_aligned = try block.addBinOp(.cmp_eq, ptr_masked, .zero_usize); 22701 const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { 22702 break :ok try block.addBinOp(.bool_or, operand_len_is_zero, is_aligned); 22703 } else is_aligned; 22704 try sema.addSafetyCheck(block, src, ok, .incorrect_alignment); 22705 } 22706 22707 if (dest_info.flags.size == .slice) { 22708 if (src_info.flags.size == .slice and !flags.addrspace_cast and !slice_needs_len_change) { 22709 // Fast path: just bitcast! 22710 return block.addBitCast(dest_ty, operand); 22711 } 22712 22713 // We need to deconstruct the slice (if applicable) and reconstruct it. 22714 assert(need_operand_ptr); 22715 22716 const result_len: Air.Inst.Ref = switch (dest_slice_len.?) { 22717 .undef => .undef_usize, 22718 .constant => |n| try pt.intRef(.usize, n), 22719 .equal_runtime_src_slice => len: { 22720 assert(need_operand_len); 22721 break :len operand_len; 22722 }, 22723 .change_runtime_src_slice => |change| len: { 22724 assert(need_operand_len); 22725 // If `mul / div` is a whole number, then just multiply the length by it. 22726 if (std.math.divExact(u64, change.bytes_per_src, change.bytes_per_dest)) |dest_per_src| { 22727 const multiplier = try pt.intRef(.usize, dest_per_src); 22728 break :len try block.addBinOp(.mul, operand_len, multiplier); 22729 } else |err| switch (err) { 22730 error.DivisionByZero => unreachable, 22731 error.UnexpectedRemainder => {}, // fall through to code below 22732 } 22733 // If `div / mul` is a whole number, then just divide the length by it. 22734 // This incurs a safety check. 22735 if (std.math.divExact(u64, change.bytes_per_dest, change.bytes_per_src)) |src_per_dest| { 22736 const divisor = try pt.intRef(.usize, src_per_dest); 22737 if (block.wantSafety()) { 22738 // Check that the element count divides neatly. 22739 const remainder = try block.addBinOp(.rem, operand_len, divisor); 22740 const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 22741 try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); 22742 } 22743 break :len try block.addBinOp(.div_exact, operand_len, divisor); 22744 } else |err| switch (err) { 22745 error.DivisionByZero => unreachable, 22746 error.UnexpectedRemainder => {}, // fall through to code below 22747 } 22748 // Fallback: the elements don't divide easily. We'll multiply *and* divide. This incurs a safety check. 22749 const total_bytes_ref = try block.addBinOp(.mul, operand_len, try pt.intRef(.usize, change.bytes_per_src)); 22750 const bytes_per_dest_ref = try pt.intRef(.usize, change.bytes_per_dest); 22751 if (block.wantSafety()) { 22752 // Check that `total_bytes_ref` divides neatly into `bytes_per_dest_ref`. 22753 const remainder = try block.addBinOp(.rem, total_bytes_ref, bytes_per_dest_ref); 22754 const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 22755 try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); 22756 } 22757 break :len try block.addBinOp(.div_exact, total_bytes_ref, bytes_per_dest_ref); 22758 }, 22759 }; 22760 22761 const operand_ptr_ty = sema.typeOf(operand_ptr); 22762 const want_ptr_ty = switch (dest_ty.zigTypeTag(zcu)) { 22763 .optional => try pt.optionalType(dest_ty.childType(zcu).slicePtrFieldType(zcu).toIntern()), 22764 .pointer => dest_ty.slicePtrFieldType(zcu), 22765 else => unreachable, 22766 }; 22767 const coerced_ptr = if (operand_ptr_ty.toIntern() != want_ptr_ty.toIntern()) ptr: { 22768 break :ptr try block.addBitCast(want_ptr_ty, operand_ptr); 22769 } else operand_ptr; 22770 22771 return block.addInst(.{ 22772 .tag = .slice, 22773 .data = .{ .ty_pl = .{ 22774 .ty = Air.internedToRef(dest_ty.toIntern()), 22775 .payload = try sema.addExtra(Air.Bin{ 22776 .lhs = coerced_ptr, 22777 .rhs = result_len, 22778 }), 22779 } }, 22780 }); 22781 } else { 22782 assert(need_operand_ptr); 22783 // We just need to bitcast the pointer, if necessary. 22784 // It might not be necessary, since we might have just needed the `addrspace_cast`. 22785 const result = if (sema.typeOf(operand_ptr).toIntern() == dest_ty.toIntern()) 22786 operand_ptr 22787 else 22788 try block.addBitCast(dest_ty, operand_ptr); 22789 22790 try sema.checkKnownAllocPtr(block, operand, result); 22791 return result; 22792 } 22793 } 22794 22795 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 22796 const pt = sema.pt; 22797 const zcu = pt.zcu; 22798 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 22799 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); 22800 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 22801 const src = block.nodeOffset(extra.node); 22802 const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node }); 22803 const operand = try sema.resolveInst(extra.operand); 22804 const operand_ty = sema.typeOf(operand); 22805 try sema.checkPtrOperand(block, operand_src, operand_ty); 22806 22807 var ptr_info = operand_ty.ptrInfo(zcu); 22808 if (flags.const_cast) ptr_info.flags.is_const = false; 22809 if (flags.volatile_cast) ptr_info.flags.is_volatile = false; 22810 22811 const dest_ty = blk: { 22812 const dest_ty = try pt.ptrTypeSema(ptr_info); 22813 if (operand_ty.zigTypeTag(zcu) == .optional) { 22814 break :blk try pt.optionalType(dest_ty.toIntern()); 22815 } 22816 break :blk dest_ty; 22817 }; 22818 22819 if (try sema.resolveValue(operand)) |operand_val| { 22820 return Air.internedToRef((try pt.getCoerced(operand_val, dest_ty)).toIntern()); 22821 } 22822 22823 try sema.requireRuntimeBlock(block, src, null); 22824 const new_ptr = try block.addBitCast(dest_ty, operand); 22825 try sema.checkKnownAllocPtr(block, operand, new_ptr); 22826 return new_ptr; 22827 } 22828 22829 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22830 const pt = sema.pt; 22831 const zcu = pt.zcu; 22832 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 22833 const src = block.nodeOffset(inst_data.src_node); 22834 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22835 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22836 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate"); 22837 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src); 22838 const operand = try sema.resolveInst(extra.rhs); 22839 const operand_ty = sema.typeOf(operand); 22840 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 22841 22842 const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector; 22843 const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector; 22844 if (operand_is_vector != dest_is_vector) { 22845 return sema.fail(block, operand_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), operand_ty.fmt(pt) }); 22846 } 22847 22848 if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { 22849 return sema.coerce(block, dest_ty, operand, operand_src); 22850 } 22851 22852 const dest_info = dest_scalar_ty.intInfo(zcu); 22853 22854 if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { 22855 return Air.internedToRef(val.toIntern()); 22856 } 22857 22858 if (operand_scalar_ty.zigTypeTag(zcu) != .comptime_int) { 22859 const operand_info = operand_ty.intInfo(zcu); 22860 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22861 return Air.internedToRef(val.toIntern()); 22862 } 22863 22864 if (operand_info.signedness != dest_info.signedness) { 22865 return sema.fail(block, operand_src, "expected {s} integer type, found '{f}'", .{ 22866 @tagName(dest_info.signedness), operand_ty.fmt(pt), 22867 }); 22868 } 22869 switch (std.math.order(dest_info.bits, operand_info.bits)) { 22870 .gt => { 22871 const msg = msg: { 22872 const msg = try sema.errMsg( 22873 src, 22874 "destination type '{f}' has more bits than source type '{f}'", 22875 .{ dest_ty.fmt(pt), operand_ty.fmt(pt) }, 22876 ); 22877 errdefer msg.destroy(sema.gpa); 22878 try sema.errNote(src, msg, "destination type has {d} bits", .{ 22879 dest_info.bits, 22880 }); 22881 try sema.errNote(operand_src, msg, "operand type has {d} bits", .{ 22882 operand_info.bits, 22883 }); 22884 break :msg msg; 22885 }; 22886 return sema.failWithOwnedErrorMsg(block, msg); 22887 }, 22888 .eq => return operand, 22889 .lt => {}, 22890 } 22891 } 22892 22893 if (try sema.resolveValueResolveLazy(operand)) |val| { 22894 const result_val = try arith.truncate(sema, val, operand_ty, dest_ty, dest_info.signedness, dest_info.bits); 22895 return Air.internedToRef(result_val.toIntern()); 22896 } 22897 22898 try sema.requireRuntimeBlock(block, src, operand_src); 22899 return block.addTyOp(.trunc, dest_ty, operand); 22900 } 22901 22902 fn zirBitCount( 22903 sema: *Sema, 22904 block: *Block, 22905 inst: Zir.Inst.Index, 22906 air_tag: Air.Inst.Tag, 22907 comptime comptimeOp: fn (val: Value, ty: Type, zcu: *Zcu) u64, 22908 ) CompileError!Air.Inst.Ref { 22909 const pt = sema.pt; 22910 const zcu = pt.zcu; 22911 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22912 const src = block.nodeOffset(inst_data.src_node); 22913 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22914 const operand = try sema.resolveInst(inst_data.operand); 22915 const operand_ty = sema.typeOf(operand); 22916 _ = try sema.checkIntOrVector(block, operand, operand_src); 22917 const bits = operand_ty.intInfo(zcu).bits; 22918 22919 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22920 return Air.internedToRef(val.toIntern()); 22921 } 22922 22923 const result_scalar_ty = try pt.smallestUnsignedInt(bits); 22924 switch (operand_ty.zigTypeTag(zcu)) { 22925 .vector => { 22926 const vec_len = operand_ty.vectorLen(zcu); 22927 const result_ty = try pt.vectorType(.{ 22928 .len = vec_len, 22929 .child = result_scalar_ty.toIntern(), 22930 }); 22931 if (try sema.resolveValue(operand)) |val| { 22932 if (val.isUndef(zcu)) return pt.undefRef(result_ty); 22933 22934 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 22935 const scalar_ty = operand_ty.scalarType(zcu); 22936 for (elems, 0..) |*elem, i| { 22937 const elem_val = try val.elemValue(pt, i); 22938 const count = comptimeOp(elem_val, scalar_ty, zcu); 22939 elem.* = (try pt.intValue(result_scalar_ty, count)).toIntern(); 22940 } 22941 return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern()); 22942 } else { 22943 try sema.requireRuntimeBlock(block, src, operand_src); 22944 return block.addTyOp(air_tag, result_ty, operand); 22945 } 22946 }, 22947 .int => { 22948 if (try sema.resolveValueResolveLazy(operand)) |val| { 22949 if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty); 22950 return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu)); 22951 } else { 22952 try sema.requireRuntimeBlock(block, src, operand_src); 22953 return block.addTyOp(air_tag, result_scalar_ty, operand); 22954 } 22955 }, 22956 else => unreachable, 22957 } 22958 } 22959 22960 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22961 const pt = sema.pt; 22962 const zcu = pt.zcu; 22963 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22964 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22965 const operand = try sema.resolveInst(inst_data.operand); 22966 const operand_ty = sema.typeOf(operand); 22967 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 22968 const bits = scalar_ty.intInfo(zcu).bits; 22969 if (bits % 8 != 0) { 22970 return sema.fail( 22971 block, 22972 operand_src, 22973 "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {d} bits", 22974 .{ scalar_ty.fmt(pt), bits }, 22975 ); 22976 } 22977 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22978 return .fromValue(val); 22979 } 22980 if (try sema.resolveValue(operand)) |operand_val| { 22981 return .fromValue(try arith.byteSwap(sema, operand_val, operand_ty)); 22982 } 22983 return block.addTyOp(.byte_swap, operand_ty, operand); 22984 } 22985 22986 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22987 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22988 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22989 const operand = try sema.resolveInst(inst_data.operand); 22990 const operand_ty = sema.typeOf(operand); 22991 _ = try sema.checkIntOrVector(block, operand, operand_src); 22992 22993 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22994 return .fromValue(val); 22995 } 22996 if (try sema.resolveValue(operand)) |operand_val| { 22997 return .fromValue(try arith.bitReverse(sema, operand_val, operand_ty)); 22998 } 22999 return block.addTyOp(.bit_reverse, operand_ty, operand); 23000 } 23001 23002 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23003 const offset = try sema.bitOffsetOf(block, inst); 23004 return sema.pt.intRef(.comptime_int, offset); 23005 } 23006 23007 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23008 const offset = try sema.bitOffsetOf(block, inst); 23009 // TODO reminder to make this a compile error for packed structs 23010 return sema.pt.intRef(.comptime_int, offset / 8); 23011 } 23012 23013 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 { 23014 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23015 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 23016 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 23017 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); 23018 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23019 23020 const ty = try sema.resolveType(block, ty_src, extra.lhs); 23021 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name }); 23022 23023 const pt = sema.pt; 23024 const zcu = pt.zcu; 23025 const ip = &zcu.intern_pool; 23026 try ty.resolveLayout(pt); 23027 switch (ty.zigTypeTag(zcu)) { 23028 .@"struct" => {}, 23029 else => return sema.fail(block, ty_src, "expected struct type, found '{f}'", .{ty.fmt(pt)}), 23030 } 23031 23032 const field_index = if (ty.isTuple(zcu)) blk: { 23033 if (field_name.eqlSlice("len", ip)) { 23034 return sema.fail(block, src, "no offset available for 'len' field of tuple", .{}); 23035 } 23036 break :blk try sema.tupleFieldIndex(block, ty, field_name, field_name_src); 23037 } else try sema.structFieldIndex(block, ty, field_name, field_name_src); 23038 23039 if (ty.structFieldIsComptime(field_index, zcu)) { 23040 return sema.fail(block, src, "no offset available for comptime field", .{}); 23041 } 23042 23043 switch (ty.containerLayout(zcu)) { 23044 .@"packed" => { 23045 var bit_sum: u64 = 0; 23046 const struct_type = ip.loadStructType(ty.toIntern()); 23047 for (0..struct_type.field_types.len) |i| { 23048 if (i == field_index) { 23049 return bit_sum; 23050 } 23051 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 23052 bit_sum += field_ty.bitSize(zcu); 23053 } else unreachable; 23054 }, 23055 else => return ty.structFieldOffset(field_index, zcu) * 8, 23056 } 23057 } 23058 23059 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 23060 const pt = sema.pt; 23061 const zcu = pt.zcu; 23062 switch (ty.zigTypeTag(zcu)) { 23063 .@"struct", .@"enum", .@"union", .@"opaque" => return, 23064 else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{f}'", .{ty.fmt(pt)}), 23065 } 23066 } 23067 23068 /// Returns `true` if the type was a comptime_int. 23069 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { 23070 const pt = sema.pt; 23071 const zcu = pt.zcu; 23072 switch (ty.zigTypeTag(zcu)) { 23073 .comptime_int => return true, 23074 .int => return false, 23075 else => return sema.fail(block, src, "expected integer type, found '{f}'", .{ty.fmt(pt)}), 23076 } 23077 } 23078 23079 fn checkInvalidPtrIntArithmetic( 23080 sema: *Sema, 23081 block: *Block, 23082 src: LazySrcLoc, 23083 ty: Type, 23084 ) CompileError!void { 23085 const pt = sema.pt; 23086 const zcu = pt.zcu; 23087 switch (ty.zigTypeTag(zcu)) { 23088 .pointer => switch (ty.ptrSize(zcu)) { 23089 .one, .slice => return, 23090 .many, .c => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), 23091 }, 23092 else => return, 23093 } 23094 } 23095 23096 fn checkArithmeticOp( 23097 sema: *Sema, 23098 block: *Block, 23099 src: LazySrcLoc, 23100 scalar_tag: std.builtin.TypeId, 23101 lhs_zig_ty_tag: std.builtin.TypeId, 23102 rhs_zig_ty_tag: std.builtin.TypeId, 23103 zir_tag: Zir.Inst.Tag, 23104 ) CompileError!void { 23105 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 23106 const is_float = scalar_tag == .float or scalar_tag == .comptime_float; 23107 23108 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { 23109 return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ 23110 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), 23111 }); 23112 } 23113 } 23114 23115 fn checkPtrOperand( 23116 sema: *Sema, 23117 block: *Block, 23118 ty_src: LazySrcLoc, 23119 ty: Type, 23120 ) CompileError!void { 23121 const pt = sema.pt; 23122 const zcu = pt.zcu; 23123 switch (ty.zigTypeTag(zcu)) { 23124 .pointer => return, 23125 .@"fn" => { 23126 const msg = msg: { 23127 const msg = try sema.errMsg( 23128 ty_src, 23129 "expected pointer, found '{f}'", 23130 .{ty.fmt(pt)}, 23131 ); 23132 errdefer msg.destroy(sema.gpa); 23133 23134 try sema.errNote(ty_src, msg, "use '&' to obtain a function pointer", .{}); 23135 23136 break :msg msg; 23137 }; 23138 return sema.failWithOwnedErrorMsg(block, msg); 23139 }, 23140 .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return, 23141 else => {}, 23142 } 23143 return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)}); 23144 } 23145 23146 fn checkPtrType( 23147 sema: *Sema, 23148 block: *Block, 23149 ty_src: LazySrcLoc, 23150 ty: Type, 23151 allow_slice: bool, 23152 ) CompileError!void { 23153 const pt = sema.pt; 23154 const zcu = pt.zcu; 23155 switch (ty.zigTypeTag(zcu)) { 23156 .pointer => if (allow_slice or !ty.isSlice(zcu)) return, 23157 .@"fn" => { 23158 const msg = msg: { 23159 const msg = try sema.errMsg( 23160 ty_src, 23161 "expected pointer type, found '{f}'", 23162 .{ty.fmt(pt)}, 23163 ); 23164 errdefer msg.destroy(sema.gpa); 23165 23166 try sema.errNote(ty_src, msg, "use '*const ' to make a function pointer type", .{}); 23167 23168 break :msg msg; 23169 }; 23170 return sema.failWithOwnedErrorMsg(block, msg); 23171 }, 23172 .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return, 23173 else => {}, 23174 } 23175 return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)}); 23176 } 23177 23178 fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 23179 const pt = sema.pt; 23180 const zcu = pt.zcu; 23181 if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) { 23182 const target = zcu.getTarget(); 23183 const as = ty.ptrAddressSpace(zcu); 23184 if (target_util.arePointersLogical(target, as)) { 23185 return sema.failWithOwnedErrorMsg(block, msg: { 23186 const msg = try sema.errMsg(src, "illegal operation on logical pointer of type '{f}'", .{ty.fmt(pt)}); 23187 errdefer msg.destroy(sema.gpa); 23188 try sema.errNote( 23189 src, 23190 msg, 23191 "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}", 23192 .{ 23193 @tagName(as), 23194 @tagName(target.cpu.arch.family()), 23195 @tagName(target.os.tag), 23196 }, 23197 ); 23198 break :msg msg; 23199 }); 23200 } 23201 } 23202 } 23203 23204 fn checkVectorElemType( 23205 sema: *Sema, 23206 block: *Block, 23207 ty_src: LazySrcLoc, 23208 ty: Type, 23209 ) CompileError!void { 23210 const pt = sema.pt; 23211 const zcu = pt.zcu; 23212 switch (ty.zigTypeTag(zcu)) { 23213 .int, .float, .bool => return, 23214 .optional, .pointer => if (ty.isPtrAtRuntime(zcu)) return, 23215 else => {}, 23216 } 23217 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{f}'", .{ty.fmt(pt)}); 23218 } 23219 23220 fn checkFloatType( 23221 sema: *Sema, 23222 block: *Block, 23223 ty_src: LazySrcLoc, 23224 ty: Type, 23225 ) CompileError!void { 23226 const pt = sema.pt; 23227 const zcu = pt.zcu; 23228 switch (ty.zigTypeTag(zcu)) { 23229 .comptime_int, .comptime_float, .float => {}, 23230 else => return sema.fail(block, ty_src, "expected float type, found '{f}'", .{ty.fmt(pt)}), 23231 } 23232 } 23233 23234 fn checkNumericType( 23235 sema: *Sema, 23236 block: *Block, 23237 ty_src: LazySrcLoc, 23238 ty: Type, 23239 ) CompileError!void { 23240 const pt = sema.pt; 23241 const zcu = pt.zcu; 23242 switch (ty.zigTypeTag(zcu)) { 23243 .comptime_float, .float, .comptime_int, .int => {}, 23244 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 23245 .comptime_float, .float, .comptime_int, .int => {}, 23246 else => |t| return sema.fail(block, ty_src, "expected number, found '{t}'", .{t}), 23247 }, 23248 else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}), 23249 } 23250 } 23251 23252 /// Returns the casted pointer. 23253 fn checkAtomicPtrOperand( 23254 sema: *Sema, 23255 block: *Block, 23256 elem_ty: Type, 23257 elem_ty_src: LazySrcLoc, 23258 ptr: Air.Inst.Ref, 23259 ptr_src: LazySrcLoc, 23260 ptr_const: bool, 23261 ) CompileError!Air.Inst.Ref { 23262 const pt = sema.pt; 23263 const zcu = pt.zcu; 23264 var diag: Zcu.AtomicPtrAlignmentDiagnostics = .{}; 23265 const alignment = zcu.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) { 23266 error.OutOfMemory => return error.OutOfMemory, 23267 error.FloatTooBig => return sema.fail( 23268 block, 23269 elem_ty_src, 23270 "expected {d}-bit float type or smaller; found {d}-bit float type", 23271 .{ diag.max_bits, diag.bits }, 23272 ), 23273 error.IntTooBig => return sema.fail( 23274 block, 23275 elem_ty_src, 23276 "expected {d}-bit integer type or smaller; found {d}-bit integer type", 23277 .{ diag.max_bits, diag.bits }, 23278 ), 23279 error.BadType => return sema.fail( 23280 block, 23281 elem_ty_src, 23282 "expected bool, integer, float, enum, packed struct, or pointer type; found '{f}'", 23283 .{elem_ty.fmt(pt)}, 23284 ), 23285 }; 23286 23287 var wanted_ptr_data: InternPool.Key.PtrType = .{ 23288 .child = elem_ty.toIntern(), 23289 .flags = .{ 23290 .alignment = alignment, 23291 .is_const = ptr_const, 23292 }, 23293 }; 23294 23295 const ptr_ty = sema.typeOf(ptr); 23296 const ptr_data = switch (ptr_ty.zigTypeTag(zcu)) { 23297 .pointer => ptr_ty.ptrInfo(zcu), 23298 else => { 23299 const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data); 23300 _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 23301 unreachable; 23302 }, 23303 }; 23304 23305 wanted_ptr_data.flags.address_space = ptr_data.flags.address_space; 23306 wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero; 23307 wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile; 23308 23309 const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data); 23310 const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 23311 23312 return casted_ptr; 23313 } 23314 23315 fn checkPtrIsNotComptimeMutable( 23316 sema: *Sema, 23317 block: *Block, 23318 ptr_val: Value, 23319 ptr_src: LazySrcLoc, 23320 operand_src: LazySrcLoc, 23321 ) CompileError!void { 23322 _ = operand_src; 23323 if (sema.isComptimeMutablePtr(ptr_val)) { 23324 return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); 23325 } 23326 } 23327 23328 fn checkIntOrVector( 23329 sema: *Sema, 23330 block: *Block, 23331 operand: Air.Inst.Ref, 23332 operand_src: LazySrcLoc, 23333 ) CompileError!Type { 23334 const pt = sema.pt; 23335 const zcu = pt.zcu; 23336 const operand_ty = sema.typeOf(operand); 23337 switch (operand_ty.zigTypeTag(zcu)) { 23338 .int => return operand_ty, 23339 .vector => { 23340 const elem_ty = operand_ty.childType(zcu); 23341 switch (elem_ty.zigTypeTag(zcu)) { 23342 .int => return elem_ty, 23343 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{ 23344 elem_ty.fmt(pt), 23345 }), 23346 } 23347 }, 23348 else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{ 23349 operand_ty.fmt(pt), 23350 }), 23351 } 23352 } 23353 23354 fn checkIntOrVectorAllowComptime( 23355 sema: *Sema, 23356 block: *Block, 23357 operand_ty: Type, 23358 operand_src: LazySrcLoc, 23359 ) CompileError!Type { 23360 const pt = sema.pt; 23361 const zcu = pt.zcu; 23362 switch (operand_ty.zigTypeTag(zcu)) { 23363 .int, .comptime_int => return operand_ty, 23364 .vector => { 23365 const elem_ty = operand_ty.childType(zcu); 23366 switch (elem_ty.zigTypeTag(zcu)) { 23367 .int, .comptime_int => return elem_ty, 23368 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{ 23369 elem_ty.fmt(pt), 23370 }), 23371 } 23372 }, 23373 else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{ 23374 operand_ty.fmt(pt), 23375 }), 23376 } 23377 } 23378 23379 const SimdBinOp = struct { 23380 len: ?usize, 23381 /// Coerced to `result_ty`. 23382 lhs: Air.Inst.Ref, 23383 /// Coerced to `result_ty`. 23384 rhs: Air.Inst.Ref, 23385 lhs_val: ?Value, 23386 rhs_val: ?Value, 23387 /// Only different than `scalar_ty` when it is a vector operation. 23388 result_ty: Type, 23389 scalar_ty: Type, 23390 }; 23391 23392 fn checkSimdBinOp( 23393 sema: *Sema, 23394 block: *Block, 23395 src: LazySrcLoc, 23396 uncasted_lhs: Air.Inst.Ref, 23397 uncasted_rhs: Air.Inst.Ref, 23398 lhs_src: LazySrcLoc, 23399 rhs_src: LazySrcLoc, 23400 ) CompileError!SimdBinOp { 23401 const pt = sema.pt; 23402 const zcu = pt.zcu; 23403 const lhs_ty = sema.typeOf(uncasted_lhs); 23404 const rhs_ty = sema.typeOf(uncasted_rhs); 23405 23406 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 23407 const vec_len: ?usize = if (lhs_ty.zigTypeTag(zcu) == .vector) lhs_ty.vectorLen(zcu) else null; 23408 const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ 23409 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 23410 }); 23411 const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); 23412 const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); 23413 23414 return SimdBinOp{ 23415 .len = vec_len, 23416 .lhs = lhs, 23417 .rhs = rhs, 23418 .lhs_val = try sema.resolveValue(lhs), 23419 .rhs_val = try sema.resolveValue(rhs), 23420 .result_ty = result_ty, 23421 .scalar_ty = result_ty.scalarType(zcu), 23422 }; 23423 } 23424 23425 fn checkVectorizableBinaryOperands( 23426 sema: *Sema, 23427 block: *Block, 23428 src: LazySrcLoc, 23429 lhs_ty: Type, 23430 rhs_ty: Type, 23431 lhs_src: LazySrcLoc, 23432 rhs_src: LazySrcLoc, 23433 ) CompileError!void { 23434 const pt = sema.pt; 23435 const zcu = pt.zcu; 23436 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 23437 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 23438 if (lhs_zig_ty_tag != .vector and rhs_zig_ty_tag != .vector) return; 23439 23440 const lhs_is_vector = switch (lhs_zig_ty_tag) { 23441 .vector, .array => true, 23442 else => false, 23443 }; 23444 const rhs_is_vector = switch (rhs_zig_ty_tag) { 23445 .vector, .array => true, 23446 else => false, 23447 }; 23448 23449 if (lhs_is_vector and rhs_is_vector) { 23450 const lhs_len = lhs_ty.arrayLen(zcu); 23451 const rhs_len = rhs_ty.arrayLen(zcu); 23452 if (lhs_len != rhs_len) { 23453 const msg = msg: { 23454 const msg = try sema.errMsg(src, "vector length mismatch", .{}); 23455 errdefer msg.destroy(sema.gpa); 23456 try sema.errNote(lhs_src, msg, "length {d} here", .{lhs_len}); 23457 try sema.errNote(rhs_src, msg, "length {d} here", .{rhs_len}); 23458 break :msg msg; 23459 }; 23460 return sema.failWithOwnedErrorMsg(block, msg); 23461 } 23462 } else { 23463 const msg = msg: { 23464 const msg = try sema.errMsg(src, "mixed scalar and vector operands: '{f}' and '{f}'", .{ 23465 lhs_ty.fmt(pt), rhs_ty.fmt(pt), 23466 }); 23467 errdefer msg.destroy(sema.gpa); 23468 if (lhs_is_vector) { 23469 try sema.errNote(lhs_src, msg, "vector here", .{}); 23470 try sema.errNote(rhs_src, msg, "scalar here", .{}); 23471 } else { 23472 try sema.errNote(lhs_src, msg, "scalar here", .{}); 23473 try sema.errNote(rhs_src, msg, "vector here", .{}); 23474 } 23475 break :msg msg; 23476 }; 23477 return sema.failWithOwnedErrorMsg(block, msg); 23478 } 23479 } 23480 23481 fn checkAllScalarsDefined(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) CompileError!void { 23482 const zcu = sema.pt.zcu; 23483 switch (zcu.intern_pool.indexToKey(val.toIntern())) { 23484 .int, .float => {}, 23485 .undef => return sema.failWithUseOfUndef(block, src, null), 23486 .aggregate => |agg| { 23487 assert(Type.fromInterned(agg.ty).zigTypeTag(zcu) == .vector); 23488 for (agg.storage.values(), 0..) |elem_val, elem_idx| { 23489 if (Value.fromInterned(elem_val).isUndef(zcu)) 23490 return sema.failWithUseOfUndef(block, src, elem_idx); 23491 } 23492 }, 23493 else => unreachable, 23494 } 23495 } 23496 23497 fn resolveExportOptions( 23498 sema: *Sema, 23499 block: *Block, 23500 src: LazySrcLoc, 23501 zir_ref: Zir.Inst.Ref, 23502 ) CompileError!Zcu.Export.Options { 23503 const pt = sema.pt; 23504 const zcu = pt.zcu; 23505 const gpa = sema.gpa; 23506 const ip = &zcu.intern_pool; 23507 const export_options_ty = try sema.getBuiltinType(src, .ExportOptions); 23508 const air_ref = try sema.resolveInst(zir_ref); 23509 const options = try sema.coerce(block, export_options_ty, air_ref, src); 23510 23511 const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 23512 const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 23513 const section_src = block.src(.{ .init_field_section = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 23514 const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 23515 23516 const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src); 23517 const name = try sema.toConstString(block, name_src, name_operand, .{ .simple = .export_options }); 23518 23519 const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src); 23520 const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ .simple = .export_options }); 23521 const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage); 23522 23523 const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "section", .no_embedded_nulls), section_src); 23524 const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .simple = .export_options }); 23525 const section = if (section_opt_val.optionalValue(zcu)) |section_val| 23526 try sema.toConstString(block, section_src, Air.internedToRef(section_val.toIntern()), .{ .simple = .export_options }) 23527 else 23528 null; 23529 23530 const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src); 23531 const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ .simple = .export_options }); 23532 const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility); 23533 23534 if (name.len < 1) { 23535 return sema.fail(block, name_src, "exported symbol name cannot be empty", .{}); 23536 } 23537 23538 if (visibility != .default and linkage == .internal) { 23539 return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{ 23540 name, @tagName(visibility), 23541 }); 23542 } 23543 23544 return .{ 23545 .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls), 23546 .linkage = linkage, 23547 .section = try ip.getOrPutStringOpt(gpa, pt.tid, section, .no_embedded_nulls), 23548 .visibility = visibility, 23549 }; 23550 } 23551 23552 fn resolveBuiltinEnum( 23553 sema: *Sema, 23554 block: *Block, 23555 src: LazySrcLoc, 23556 zir_ref: Zir.Inst.Ref, 23557 comptime name: Zcu.BuiltinDecl, 23558 reason: ComptimeReason, 23559 ) CompileError!@field(std.builtin, @tagName(name)) { 23560 const ty = try sema.getBuiltinType(src, name); 23561 const air_ref = try sema.resolveInst(zir_ref); 23562 const coerced = try sema.coerce(block, ty, air_ref, src); 23563 const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); 23564 return sema.interpretBuiltinType(block, src, val, @field(std.builtin, @tagName(name))); 23565 } 23566 23567 fn resolveAtomicOrder( 23568 sema: *Sema, 23569 block: *Block, 23570 src: LazySrcLoc, 23571 zir_ref: Zir.Inst.Ref, 23572 reason: ComptimeReason, 23573 ) CompileError!std.builtin.AtomicOrder { 23574 return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicOrder, reason); 23575 } 23576 23577 fn resolveAtomicRmwOp( 23578 sema: *Sema, 23579 block: *Block, 23580 src: LazySrcLoc, 23581 zir_ref: Zir.Inst.Ref, 23582 ) CompileError!std.builtin.AtomicRmwOp { 23583 return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicRmwOp, .{ .simple = .operand_atomicRmw_operation }); 23584 } 23585 23586 fn zirCmpxchg( 23587 sema: *Sema, 23588 block: *Block, 23589 extended: Zir.Inst.Extended.InstData, 23590 ) CompileError!Air.Inst.Ref { 23591 const pt = sema.pt; 23592 const zcu = pt.zcu; 23593 const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data; 23594 const air_tag: Air.Inst.Tag = switch (extended.small) { 23595 0 => .cmpxchg_weak, 23596 1 => .cmpxchg_strong, 23597 else => unreachable, 23598 }; 23599 const src = block.nodeOffset(extra.node); 23600 // zig fmt: off 23601 const elem_ty_src = block.builtinCallArgSrc(extra.node, 0); 23602 const ptr_src = block.builtinCallArgSrc(extra.node, 1); 23603 const expected_src = block.builtinCallArgSrc(extra.node, 2); 23604 const new_value_src = block.builtinCallArgSrc(extra.node, 3); 23605 const success_order_src = block.builtinCallArgSrc(extra.node, 4); 23606 const failure_order_src = block.builtinCallArgSrc(extra.node, 5); 23607 // zig fmt: on 23608 const expected_value = try sema.resolveInst(extra.expected_value); 23609 const elem_ty = sema.typeOf(expected_value); 23610 if (elem_ty.zigTypeTag(zcu) == .float) { 23611 return sema.fail( 23612 block, 23613 elem_ty_src, 23614 "expected bool, integer, enum, packed struct, or pointer type; found '{f}'", 23615 .{elem_ty.fmt(pt)}, 23616 ); 23617 } 23618 const uncasted_ptr = try sema.resolveInst(extra.ptr); 23619 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 23620 const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); 23621 const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{ .simple = .atomic_order }); 23622 const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{ .simple = .atomic_order }); 23623 23624 if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.monotonic)) { 23625 return sema.fail(block, success_order_src, "success atomic ordering must be monotonic or stricter", .{}); 23626 } 23627 if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.monotonic)) { 23628 return sema.fail(block, failure_order_src, "failure atomic ordering must be monotonic or stricter", .{}); 23629 } 23630 if (@intFromEnum(failure_order) > @intFromEnum(success_order)) { 23631 return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{}); 23632 } 23633 if (failure_order == .release or failure_order == .acq_rel) { 23634 return sema.fail(block, failure_order_src, "failure atomic ordering must not be release or acq_rel", .{}); 23635 } 23636 23637 const result_ty = try pt.optionalType(elem_ty.toIntern()); 23638 23639 // special case zero bit types 23640 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { 23641 return Air.internedToRef((try pt.intern(.{ .opt = .{ 23642 .ty = result_ty.toIntern(), 23643 .val = .none, 23644 } }))); 23645 } 23646 23647 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 23648 if (try sema.resolveValue(expected_value)) |expected_val| { 23649 if (try sema.resolveValue(new_value)) |new_val| { 23650 if (expected_val.isUndef(zcu) or new_val.isUndef(zcu)) { 23651 // TODO: this should probably cause the memory stored at the pointer 23652 // to become undef as well 23653 return pt.undefRef(result_ty); 23654 } 23655 const ptr_ty = sema.typeOf(ptr); 23656 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 23657 const result_val = try pt.intern(.{ .opt = .{ 23658 .ty = result_ty.toIntern(), 23659 .val = if (stored_val.eql(expected_val, elem_ty, zcu)) blk: { 23660 try sema.storePtr(block, src, ptr, new_value); 23661 break :blk .none; 23662 } else stored_val.toIntern(), 23663 } }); 23664 return Air.internedToRef(result_val); 23665 } else break :rs new_value_src; 23666 } else break :rs expected_src; 23667 } else ptr_src; 23668 23669 const flags: u32 = @as(u32, @intFromEnum(success_order)) | 23670 (@as(u32, @intFromEnum(failure_order)) << 3); 23671 23672 try sema.requireRuntimeBlock(block, src, runtime_src); 23673 return block.addInst(.{ 23674 .tag = air_tag, 23675 .data = .{ .ty_pl = .{ 23676 .ty = Air.internedToRef(result_ty.toIntern()), 23677 .payload = try sema.addExtra(Air.Cmpxchg{ 23678 .ptr = ptr, 23679 .expected_value = expected_value, 23680 .new_value = new_value, 23681 .flags = flags, 23682 }), 23683 } }, 23684 }); 23685 } 23686 23687 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23688 const pt = sema.pt; 23689 const zcu = pt.zcu; 23690 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23691 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23692 const src = block.nodeOffset(inst_data.src_node); 23693 const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0); 23694 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat"); 23695 23696 switch (dest_ty.zigTypeTag(zcu)) { 23697 .array, .vector => {}, 23698 else => return sema.fail(block, src, "expected array or vector type, found '{f}'", .{dest_ty.fmt(pt)}), 23699 } 23700 23701 const operand = try sema.resolveInst(extra.rhs); 23702 const scalar_ty = dest_ty.childType(zcu); 23703 const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src); 23704 23705 const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu)); 23706 23707 if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { 23708 return Air.internedToRef(val.toIntern()); 23709 } 23710 23711 // We also need this case because `[0:s]T` is not OPV. 23712 if (len == 0) return .fromValue(try pt.aggregateValue(dest_ty, &.{})); 23713 23714 const maybe_sentinel = dest_ty.sentinel(zcu); 23715 23716 if (try sema.resolveValue(scalar)) |scalar_val| { 23717 full: { 23718 if (dest_ty.zigTypeTag(zcu) == .vector) break :full; 23719 const sentinel = maybe_sentinel orelse break :full; 23720 if (sentinel.toIntern() == scalar_val.toIntern()) break :full; 23721 // This is a array with non-zero length and a sentinel which does not match the element. 23722 // We have to use the full `elems` representation. 23723 const elems = try sema.arena.alloc(InternPool.Index, len + 1); 23724 @memset(elems[0..len], scalar_val.toIntern()); 23725 elems[len] = sentinel.toIntern(); 23726 return .fromValue(try pt.aggregateValue(dest_ty, elems)); 23727 } 23728 return .fromValue(try pt.aggregateSplatValue(dest_ty, scalar_val)); 23729 } 23730 23731 try sema.requireRuntimeBlock(block, src, scalar_src); 23732 23733 switch (dest_ty.zigTypeTag(zcu)) { 23734 .array => { 23735 const elems = try sema.arena.alloc(Air.Inst.Ref, len + @intFromBool(maybe_sentinel != null)); 23736 @memset(elems[0..len], scalar); 23737 if (maybe_sentinel) |s| elems[len] = Air.internedToRef(s.toIntern()); 23738 return block.addAggregateInit(dest_ty, elems); 23739 }, 23740 .vector => return block.addTyOp(.splat, dest_ty, scalar), 23741 else => unreachable, 23742 } 23743 } 23744 23745 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23746 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23747 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23748 const op_src = block.builtinCallArgSrc(inst_data.src_node, 0); 23749 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 1); 23750 const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, .ReduceOp, .{ .simple = .operand_reduce_operation }); 23751 const operand = try sema.resolveInst(extra.rhs); 23752 const operand_ty = sema.typeOf(operand); 23753 const pt = sema.pt; 23754 const zcu = pt.zcu; 23755 23756 if (operand_ty.zigTypeTag(zcu) != .vector) { 23757 return sema.fail(block, operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)}); 23758 } 23759 23760 const scalar_ty = operand_ty.childType(zcu); 23761 23762 // Type-check depending on operation. 23763 switch (operation) { 23764 .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) { 23765 .int, .bool => {}, 23766 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{f}'", .{ 23767 @tagName(operation), operand_ty.fmt(pt), 23768 }), 23769 }, 23770 .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) { 23771 .int, .float => {}, 23772 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{f}'", .{ 23773 @tagName(operation), operand_ty.fmt(pt), 23774 }), 23775 }, 23776 } 23777 23778 const vec_len = operand_ty.vectorLen(zcu); 23779 if (vec_len == 0) { 23780 // TODO re-evaluate if we should introduce a "neutral value" for some operations, 23781 // e.g. zero for add and one for mul. 23782 return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{}); 23783 } 23784 23785 if (try sema.resolveValue(operand)) |operand_val| { 23786 if (operand_val.isUndef(zcu)) return pt.undefRef(scalar_ty); 23787 23788 var accum: Value = try operand_val.elemValue(pt, 0); 23789 var i: u32 = 1; 23790 while (i < vec_len) : (i += 1) { 23791 const elem_val = try operand_val.elemValue(pt, i); 23792 accum = switch (operation) { 23793 // zig fmt: off 23794 .And => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .@"and"), 23795 .Or => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .@"or"), 23796 .Xor => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .xor), 23797 .Min => Value.numberMin ( accum, elem_val, zcu), 23798 .Max => Value.numberMax ( accum, elem_val, zcu), 23799 .Add => try arith.addMaybeWrap(sema, scalar_ty, accum, elem_val), 23800 .Mul => try arith.mulMaybeWrap(sema, scalar_ty, accum, elem_val), 23801 // zig fmt: on 23802 }; 23803 } 23804 return Air.internedToRef(accum.toIntern()); 23805 } 23806 23807 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src); 23808 return block.addReduce(operand, operation); 23809 } 23810 23811 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23812 const pt = sema.pt; 23813 const zcu = pt.zcu; 23814 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23815 const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; 23816 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 23817 const mask_src = block.builtinCallArgSrc(inst_data.src_node, 3); 23818 23819 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 23820 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 23821 const a = try sema.resolveInst(extra.a); 23822 const b = try sema.resolveInst(extra.b); 23823 var mask = try sema.resolveInst(extra.mask); 23824 var mask_ty = sema.typeOf(mask); 23825 23826 const mask_len = switch (sema.typeOf(mask).zigTypeTag(zcu)) { 23827 .array, .vector => sema.typeOf(mask).arrayLen(zcu), 23828 else => return sema.fail(block, mask_src, "expected vector or array, found '{f}'", .{sema.typeOf(mask).fmt(pt)}), 23829 }; 23830 mask_ty = try pt.vectorType(.{ 23831 .len = @intCast(mask_len), 23832 .child = .i32_type, 23833 }); 23834 mask = try sema.coerce(block, mask_ty, mask, mask_src); 23835 const mask_val = try sema.resolveConstValue(block, mask_src, mask, .{ .simple = .operand_shuffle_mask }); 23836 return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(mask_len)); 23837 } 23838 23839 fn analyzeShuffle( 23840 sema: *Sema, 23841 block: *Block, 23842 src_node: std.zig.Ast.Node.Offset, 23843 elem_ty: Type, 23844 a_uncoerced: Air.Inst.Ref, 23845 b_uncoerced: Air.Inst.Ref, 23846 mask: Value, 23847 mask_len: u32, 23848 ) CompileError!Air.Inst.Ref { 23849 const pt = sema.pt; 23850 const zcu = pt.zcu; 23851 const a_src = block.builtinCallArgSrc(src_node, 1); 23852 const b_src = block.builtinCallArgSrc(src_node, 2); 23853 const mask_src = block.builtinCallArgSrc(src_node, 3); 23854 23855 // If the type of `a` is `@Type(.undefined)`, i.e. the argument is untyped, 23856 // this is 0, because it is an error to index into this vector. 23857 const a_len: u32 = switch (sema.typeOf(a_uncoerced).zigTypeTag(zcu)) { 23858 .array, .vector => @intCast(sema.typeOf(a_uncoerced).arrayLen(zcu)), 23859 .undefined => 0, 23860 else => return sema.fail(block, a_src, "expected vector of '{f}', found '{f}'", .{ 23861 elem_ty.fmt(pt), sema.typeOf(a_uncoerced).fmt(pt), 23862 }), 23863 }; 23864 const a_ty = try pt.vectorType(.{ .len = a_len, .child = elem_ty.toIntern() }); 23865 const a_coerced = try sema.coerce(block, a_ty, a_uncoerced, a_src); 23866 23867 // If the type of `b` is `@Type(.undefined)`, i.e. the argument is untyped, this is 0, because it is an error to index into this vector. 23868 const b_len: u32 = switch (sema.typeOf(b_uncoerced).zigTypeTag(zcu)) { 23869 .array, .vector => @intCast(sema.typeOf(b_uncoerced).arrayLen(zcu)), 23870 .undefined => 0, 23871 else => return sema.fail(block, b_src, "expected vector of '{f}', found '{f}'", .{ 23872 elem_ty.fmt(pt), sema.typeOf(b_uncoerced).fmt(pt), 23873 }), 23874 }; 23875 const b_ty = try pt.vectorType(.{ .len = b_len, .child = elem_ty.toIntern() }); 23876 const b_coerced = try sema.coerce(block, b_ty, b_uncoerced, b_src); 23877 23878 const result_ty = try pt.vectorType(.{ .len = mask_len, .child = elem_ty.toIntern() }); 23879 23880 // We're going to pre-emptively reserve space in `sema.air_extra`. The reason for this is we need 23881 // a `u32` buffer of length `mask_len` anyway, and putting it in `sema.air_extra` avoids a copy 23882 // in the runtime case. If the result is comptime-known, we'll shrink `air_extra` back. 23883 const air_extra_idx: u32 = @intCast(sema.air_extra.items.len); 23884 const air_mask_buf = try sema.air_extra.addManyAsSlice(sema.gpa, mask_len); 23885 23886 // We want to interpret that buffer in `air_extra` in a few ways. Initially, we'll consider its 23887 // elements as `Air.Inst.ShuffleTwoMask`, essentially representing the raw mask values; then, we'll 23888 // convert it to `InternPool.Index` or `Air.Inst.ShuffleOneMask` if there are comptime-known operands. 23889 const mask_ip_index: []InternPool.Index = @ptrCast(air_mask_buf); 23890 const mask_shuffle_one: []Air.ShuffleOneMask = @ptrCast(air_mask_buf); 23891 const mask_shuffle_two: []Air.ShuffleTwoMask = @ptrCast(air_mask_buf); 23892 23893 // Initial loop: check mask elements, populate `mask_shuffle_two`. 23894 var a_used = false; 23895 var b_used = false; 23896 for (mask_shuffle_two, 0..mask_len) |*out, mask_idx| { 23897 const mask_val = try mask.elemValue(pt, mask_idx); 23898 if (mask_val.isUndef(zcu)) { 23899 out.* = .undef; 23900 continue; 23901 } 23902 // Safe because mask elements are `i32` and we already checked for undef: 23903 const raw = (try sema.resolveLazyValue(mask_val)).toSignedInt(zcu); 23904 if (raw >= 0) { 23905 const idx: u32 = @intCast(raw); 23906 a_used = true; 23907 out.* = .aElem(idx); 23908 if (idx >= a_len) return sema.failWithOwnedErrorMsg(block, msg: { 23909 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx}); 23910 errdefer msg.destroy(sema.gpa); 23911 try sema.errNote(a_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, a_ty.fmt(pt) }); 23912 if (idx < b_len) { 23913 try sema.errNote(b_src, msg, "use '~@as(u32, {d})' to index into second vector given here", .{idx}); 23914 } 23915 break :msg msg; 23916 }); 23917 } else { 23918 const idx: u32 = @intCast(~raw); 23919 b_used = true; 23920 out.* = .bElem(idx); 23921 if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: { 23922 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx}); 23923 errdefer msg.destroy(sema.gpa); 23924 try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, b_ty.fmt(pt) }); 23925 break :msg msg; 23926 }); 23927 } 23928 } 23929 23930 const maybe_a_val = try sema.resolveValue(a_coerced); 23931 const maybe_b_val = try sema.resolveValue(b_coerced); 23932 23933 const a_rt = a_used and maybe_a_val == null; 23934 const b_rt = b_used and maybe_b_val == null; 23935 23936 if (a_rt and b_rt) { 23937 // Both operands are needed and runtime-known. We need a `[]ShuffleTwomask`... which is 23938 // exactly what we already have in `mask_shuffle_two`! So, we're basically done already. 23939 // We just need to append the two operands. 23940 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 2); 23941 sema.appendRefsAssumeCapacity(&.{ a_coerced, b_coerced }); 23942 return block.addInst(.{ 23943 .tag = .shuffle_two, 23944 .data = .{ .ty_pl = .{ 23945 .ty = Air.internedToRef(result_ty.toIntern()), 23946 .payload = air_extra_idx, 23947 } }, 23948 }); 23949 } else if (a_rt) { 23950 // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`. 23951 for (mask_shuffle_two, mask_shuffle_one) |in, *out| { 23952 out.* = switch (in.unwrap()) { 23953 .undef => .value(try pt.undefValue(elem_ty)), 23954 .a_elem => |idx| .elem(idx), 23955 .b_elem => |idx| .value(try maybe_b_val.?.elemValue(pt, idx)), 23956 }; 23957 } 23958 // Now just append our single runtime operand, and we're done. 23959 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1); 23960 sema.appendRefsAssumeCapacity(&.{a_coerced}); 23961 return block.addInst(.{ 23962 .tag = .shuffle_one, 23963 .data = .{ .ty_pl = .{ 23964 .ty = Air.internedToRef(result_ty.toIntern()), 23965 .payload = air_extra_idx, 23966 } }, 23967 }); 23968 } else if (b_rt) { 23969 // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`. 23970 for (mask_shuffle_two, mask_shuffle_one) |in, *out| { 23971 out.* = switch (in.unwrap()) { 23972 .undef => .value(try pt.undefValue(elem_ty)), 23973 .a_elem => |idx| .value(try maybe_a_val.?.elemValue(pt, idx)), 23974 .b_elem => |idx| .elem(idx), 23975 }; 23976 } 23977 // Now just append our single runtime operand, and we're done. 23978 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1); 23979 sema.appendRefsAssumeCapacity(&.{b_coerced}); 23980 return block.addInst(.{ 23981 .tag = .shuffle_one, 23982 .data = .{ .ty_pl = .{ 23983 .ty = Air.internedToRef(result_ty.toIntern()), 23984 .payload = air_extra_idx, 23985 } }, 23986 }); 23987 } else { 23988 // The result will be comptime-known. We must convert the `ShuffleTwoMask` values to 23989 // `InternPool.Index` values using the known operands. 23990 for (mask_shuffle_two, mask_ip_index) |in, *out| { 23991 const val: Value = switch (in.unwrap()) { 23992 .undef => try pt.undefValue(elem_ty), 23993 .a_elem => |idx| try maybe_a_val.?.elemValue(pt, idx), 23994 .b_elem => |idx| try maybe_b_val.?.elemValue(pt, idx), 23995 }; 23996 out.* = val.toIntern(); 23997 } 23998 const res = try pt.aggregateValue(result_ty, mask_ip_index); 23999 // We have a comptime-known result, so didn't need `air_mask_buf` -- remove it from `sema.air_extra`. 24000 assert(sema.air_extra.items.len == air_extra_idx + air_mask_buf.len); 24001 sema.air_extra.shrinkRetainingCapacity(air_extra_idx); 24002 return Air.internedToRef(res.toIntern()); 24003 } 24004 } 24005 24006 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 24007 const pt = sema.pt; 24008 const zcu = pt.zcu; 24009 const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; 24010 24011 const src = block.nodeOffset(extra.node); 24012 const elem_ty_src = block.builtinCallArgSrc(extra.node, 0); 24013 const pred_src = block.builtinCallArgSrc(extra.node, 1); 24014 const a_src = block.builtinCallArgSrc(extra.node, 2); 24015 const b_src = block.builtinCallArgSrc(extra.node, 3); 24016 24017 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 24018 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 24019 const pred_uncoerced = try sema.resolveInst(extra.pred); 24020 const pred_ty = sema.typeOf(pred_uncoerced); 24021 24022 const vec_len_u64 = switch (pred_ty.zigTypeTag(zcu)) { 24023 .vector, .array => pred_ty.arrayLen(zcu), 24024 else => return sema.fail(block, pred_src, "expected vector or array, found '{f}'", .{pred_ty.fmt(pt)}), 24025 }; 24026 const vec_len: u32 = @intCast(try sema.usizeCast(block, pred_src, vec_len_u64)); 24027 24028 const bool_vec_ty = try pt.vectorType(.{ 24029 .len = vec_len, 24030 .child = .bool_type, 24031 }); 24032 const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); 24033 24034 const vec_ty = try pt.vectorType(.{ 24035 .len = vec_len, 24036 .child = elem_ty.toIntern(), 24037 }); 24038 const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src); 24039 const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src); 24040 24041 const maybe_pred = try sema.resolveValue(pred); 24042 const maybe_a = try sema.resolveValue(a); 24043 const maybe_b = try sema.resolveValue(b); 24044 24045 const runtime_src = if (maybe_pred) |pred_val| rs: { 24046 if (pred_val.isUndef(zcu)) return pt.undefRef(vec_ty); 24047 24048 if (maybe_a) |a_val| { 24049 if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty); 24050 24051 if (maybe_b) |b_val| { 24052 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); 24053 24054 const elems = try sema.gpa.alloc(InternPool.Index, vec_len); 24055 defer sema.gpa.free(elems); 24056 for (elems, 0..) |*elem, i| { 24057 const pred_elem_val = try pred_val.elemValue(pt, i); 24058 const should_choose_a = pred_elem_val.toBool(); 24059 elem.* = (try (if (should_choose_a) a_val else b_val).elemValue(pt, i)).toIntern(); 24060 } 24061 24062 return Air.internedToRef((try pt.aggregateValue(vec_ty, elems)).toIntern()); 24063 } else { 24064 break :rs b_src; 24065 } 24066 } else { 24067 if (maybe_b) |b_val| { 24068 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); 24069 } 24070 break :rs a_src; 24071 } 24072 } else rs: { 24073 if (maybe_a) |a_val| { 24074 if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty); 24075 } 24076 if (maybe_b) |b_val| { 24077 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); 24078 } 24079 break :rs pred_src; 24080 }; 24081 24082 try sema.requireRuntimeBlock(block, src, runtime_src); 24083 return block.addInst(.{ 24084 .tag = .select, 24085 .data = .{ .pl_op = .{ 24086 .operand = pred, 24087 .payload = try block.sema.addExtra(Air.Bin{ 24088 .lhs = a, 24089 .rhs = b, 24090 }), 24091 } }, 24092 }); 24093 } 24094 24095 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24096 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24097 const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; 24098 // zig fmt: off 24099 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24100 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24101 const order_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24102 // zig fmt: on 24103 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 24104 const uncasted_ptr = try sema.resolveInst(extra.ptr); 24105 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); 24106 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); 24107 24108 switch (order) { 24109 .release, .acq_rel => { 24110 return sema.fail( 24111 block, 24112 order_src, 24113 "@atomicLoad atomic ordering must not be release or acq_rel", 24114 .{}, 24115 ); 24116 }, 24117 else => {}, 24118 } 24119 24120 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 24121 return Air.internedToRef(val.toIntern()); 24122 } 24123 24124 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 24125 if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { 24126 return Air.internedToRef(elem_val.toIntern()); 24127 } 24128 } 24129 24130 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src); 24131 return block.addInst(.{ 24132 .tag = .atomic_load, 24133 .data = .{ .atomic_load = .{ 24134 .ptr = ptr, 24135 .order = order, 24136 } }, 24137 }); 24138 } 24139 24140 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24141 const pt = sema.pt; 24142 const zcu = pt.zcu; 24143 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24144 const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; 24145 const src = block.nodeOffset(inst_data.src_node); 24146 // zig fmt: off 24147 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24148 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24149 const op_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24150 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 3); 24151 const order_src = block.builtinCallArgSrc(inst_data.src_node, 4); 24152 // zig fmt: on 24153 const operand = try sema.resolveInst(extra.operand); 24154 const elem_ty = sema.typeOf(operand); 24155 const uncasted_ptr = try sema.resolveInst(extra.ptr); 24156 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 24157 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); 24158 24159 switch (elem_ty.zigTypeTag(zcu)) { 24160 .@"enum" => if (op != .Xchg) { 24161 return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); 24162 }, 24163 .bool => if (op != .Xchg) { 24164 return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{}); 24165 }, 24166 .float => switch (op) { 24167 .Xchg, .Add, .Sub, .Max, .Min => {}, 24168 else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}), 24169 }, 24170 else => {}, 24171 } 24172 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); 24173 24174 if (order == .unordered) { 24175 return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be unordered", .{}); 24176 } 24177 24178 // special case zero bit types 24179 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 24180 return Air.internedToRef(val.toIntern()); 24181 } 24182 24183 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 24184 const maybe_operand_val = try sema.resolveValue(operand); 24185 const operand_val = maybe_operand_val orelse { 24186 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); 24187 break :rs operand_src; 24188 }; 24189 if (sema.isComptimeMutablePtr(ptr_val)) { 24190 const ptr_ty = sema.typeOf(ptr); 24191 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 24192 const new_val = switch (op) { 24193 // zig fmt: off 24194 .Xchg => operand_val, 24195 .Add => try arith.addMaybeWrap(sema, elem_ty, stored_val, operand_val), 24196 .Sub => try arith.subMaybeWrap(sema, elem_ty, stored_val, operand_val), 24197 .And => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .@"and"), 24198 .Nand => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .nand), 24199 .Or => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .@"or"), 24200 .Xor => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .xor), 24201 .Max => Value.numberMax ( stored_val, operand_val, zcu), 24202 .Min => Value.numberMin ( stored_val, operand_val, zcu), 24203 // zig fmt: on 24204 }; 24205 try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); 24206 return Air.internedToRef(stored_val.toIntern()); 24207 } else break :rs ptr_src; 24208 } else ptr_src; 24209 24210 const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3); 24211 24212 try sema.requireRuntimeBlock(block, src, runtime_src); 24213 return block.addInst(.{ 24214 .tag = .atomic_rmw, 24215 .data = .{ .pl_op = .{ 24216 .operand = ptr, 24217 .payload = try sema.addExtra(Air.AtomicRmw{ 24218 .operand = operand, 24219 .flags = flags, 24220 }), 24221 } }, 24222 }); 24223 } 24224 24225 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 24226 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24227 const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; 24228 const src = block.nodeOffset(inst_data.src_node); 24229 // zig fmt: off 24230 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24231 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24232 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24233 const order_src = block.builtinCallArgSrc(inst_data.src_node, 3); 24234 // zig fmt: on 24235 const operand = try sema.resolveInst(extra.operand); 24236 const elem_ty = sema.typeOf(operand); 24237 const uncasted_ptr = try sema.resolveInst(extra.ptr); 24238 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 24239 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); 24240 24241 const air_tag: Air.Inst.Tag = switch (order) { 24242 .acquire, .acq_rel => { 24243 return sema.fail( 24244 block, 24245 order_src, 24246 "@atomicStore atomic ordering must not be acquire or acq_rel", 24247 .{}, 24248 ); 24249 }, 24250 .unordered => .atomic_store_unordered, 24251 .monotonic => .atomic_store_monotonic, 24252 .release => .atomic_store_release, 24253 .seq_cst => .atomic_store_seq_cst, 24254 }; 24255 24256 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 24257 } 24258 24259 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24260 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24261 const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data; 24262 const src = block.nodeOffset(inst_data.src_node); 24263 24264 const mulend1_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24265 const mulend2_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24266 const addend_src = block.builtinCallArgSrc(inst_data.src_node, 3); 24267 24268 const addend = try sema.resolveInst(extra.addend); 24269 const ty = sema.typeOf(addend); 24270 const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src); 24271 const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src); 24272 24273 const maybe_mulend1 = try sema.resolveValue(mulend1); 24274 const maybe_mulend2 = try sema.resolveValue(mulend2); 24275 const maybe_addend = try sema.resolveValue(addend); 24276 const pt = sema.pt; 24277 const zcu = pt.zcu; 24278 24279 switch (ty.scalarType(zcu).zigTypeTag(zcu)) { 24280 .comptime_float, .float => {}, 24281 else => return sema.fail(block, src, "expected vector of floats or float type, found '{f}'", .{ty.fmt(pt)}), 24282 } 24283 24284 const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { 24285 if (maybe_mulend2) |mulend2_val| { 24286 if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty); 24287 24288 if (maybe_addend) |addend_val| { 24289 if (addend_val.isUndef(zcu)) return pt.undefRef(ty); 24290 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, pt); 24291 return Air.internedToRef(result_val.toIntern()); 24292 } else { 24293 break :rs addend_src; 24294 } 24295 } else { 24296 if (maybe_addend) |addend_val| { 24297 if (addend_val.isUndef(zcu)) return pt.undefRef(ty); 24298 } 24299 break :rs mulend2_src; 24300 } 24301 } else rs: { 24302 if (maybe_mulend2) |mulend2_val| { 24303 if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty); 24304 } 24305 if (maybe_addend) |addend_val| { 24306 if (addend_val.isUndef(zcu)) return pt.undefRef(ty); 24307 } 24308 break :rs mulend1_src; 24309 }; 24310 24311 try sema.requireRuntimeBlock(block, src, runtime_src); 24312 return block.addInst(.{ 24313 .tag = .mul_add, 24314 .data = .{ .pl_op = .{ 24315 .operand = addend, 24316 .payload = try sema.addExtra(Air.Bin{ 24317 .lhs = mulend1, 24318 .rhs = mulend2, 24319 }), 24320 } }, 24321 }); 24322 } 24323 24324 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24325 const tracy = trace(@src()); 24326 defer tracy.end(); 24327 24328 const pt = sema.pt; 24329 const zcu = pt.zcu; 24330 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24331 const modifier_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24332 const func_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24333 const args_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24334 const call_src = block.nodeOffset(inst_data.src_node); 24335 24336 const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; 24337 const func = try sema.resolveInst(extra.callee); 24338 24339 const modifier_ty = try sema.getBuiltinType(call_src, .CallModifier); 24340 const air_ref = try sema.resolveInst(extra.modifier); 24341 const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); 24342 const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ .simple = .call_modifier }); 24343 var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier); 24344 switch (modifier) { 24345 // These can be upgraded to comptime or nosuspend calls. 24346 .auto, .never_tail, .no_suspend => { 24347 if (block.isComptime()) { 24348 if (modifier == .never_tail) { 24349 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{}); 24350 } 24351 modifier = .compile_time; 24352 } else if (extra.flags.is_nosuspend) { 24353 modifier = .no_suspend; 24354 } 24355 }, 24356 // These can be upgraded to comptime. nosuspend bit can be safely ignored. 24357 .always_inline, .compile_time => { 24358 _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { 24359 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)}); 24360 }; 24361 24362 if (block.isComptime()) { 24363 modifier = .compile_time; 24364 } 24365 }, 24366 .always_tail => { 24367 if (block.isComptime()) { 24368 modifier = .compile_time; 24369 } 24370 }, 24371 .never_inline => { 24372 if (block.isComptime()) { 24373 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{}); 24374 } 24375 }, 24376 } 24377 24378 const args = try sema.resolveInst(extra.args); 24379 24380 const args_ty = sema.typeOf(args); 24381 if (!args_ty.isTuple(zcu)) { 24382 return sema.fail(block, args_src, "expected a tuple, found '{f}'", .{args_ty.fmt(pt)}); 24383 } 24384 24385 const resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(zcu)); 24386 for (resolved_args, 0..) |*resolved, i| { 24387 resolved.* = try sema.tupleFieldValByIndex(block, args, @intCast(i), args_ty); 24388 } 24389 24390 const callee_ty = sema.typeOf(func); 24391 const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false); 24392 const ensure_result_used = extra.flags.ensure_result_used; 24393 return sema.analyzeCall( 24394 block, 24395 func, 24396 func_ty, 24397 func_src, 24398 call_src, 24399 modifier, 24400 ensure_result_used, 24401 .{ .call_builtin = .{ 24402 .call_node_offset = inst_data.src_node, 24403 .args = resolved_args, 24404 } }, 24405 null, 24406 .@"@call", 24407 ); 24408 } 24409 24410 fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 24411 const pt = sema.pt; 24412 const zcu = pt.zcu; 24413 const ip = &zcu.intern_pool; 24414 24415 const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, extended.operand).data; 24416 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 24417 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); 24418 assert(!flags.ptr_cast); 24419 const inst_src = block.nodeOffset(extra.src_node); 24420 const field_name_src = block.builtinCallArgSrc(extra.src_node, 0); 24421 const field_ptr_src = block.builtinCallArgSrc(extra.src_node, 1); 24422 24423 const parent_ptr_ty = try sema.resolveDestType(block, inst_src, extra.parent_ptr_type, .remove_eu, "@fieldParentPtr"); 24424 try sema.checkPtrType(block, inst_src, parent_ptr_ty, true); 24425 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); 24426 if (parent_ptr_info.flags.size != .one) { 24427 return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)}); 24428 } 24429 const parent_ty: Type = .fromInterned(parent_ptr_info.child); 24430 switch (parent_ty.zigTypeTag(zcu)) { 24431 .@"struct", .@"union" => {}, 24432 else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}), 24433 } 24434 try parent_ty.resolveLayout(pt); 24435 24436 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); 24437 const field_index = switch (parent_ty.zigTypeTag(zcu)) { 24438 .@"struct" => blk: { 24439 if (parent_ty.isTuple(zcu)) { 24440 if (field_name.eqlSlice("len", ip)) { 24441 return sema.fail(block, inst_src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); 24442 } 24443 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, field_name_src); 24444 } else { 24445 break :blk try sema.structFieldIndex(block, parent_ty, field_name, field_name_src); 24446 } 24447 }, 24448 .@"union" => try sema.unionFieldIndex(block, parent_ty, field_name, field_name_src), 24449 else => unreachable, 24450 }; 24451 if (parent_ty.zigTypeTag(zcu) == .@"struct" and parent_ty.structFieldIsComptime(field_index, zcu)) { 24452 return sema.fail(block, field_name_src, "cannot get @fieldParentPtr of a comptime field", .{}); 24453 } 24454 24455 const field_ptr = try sema.resolveInst(extra.field_ptr); 24456 const field_ptr_ty = sema.typeOf(field_ptr); 24457 try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty); 24458 const field_ptr_info = field_ptr_ty.ptrInfo(zcu); 24459 24460 var actual_parent_ptr_info: InternPool.Key.PtrType = .{ 24461 .child = parent_ty.toIntern(), 24462 .flags = .{ 24463 .alignment = try parent_ptr_ty.ptrAlignmentSema(pt), 24464 .is_const = field_ptr_info.flags.is_const, 24465 .is_volatile = field_ptr_info.flags.is_volatile, 24466 .is_allowzero = field_ptr_info.flags.is_allowzero, 24467 .address_space = field_ptr_info.flags.address_space, 24468 }, 24469 .packed_offset = parent_ptr_info.packed_offset, 24470 }; 24471 const field_ty = parent_ty.fieldType(field_index, zcu); 24472 var actual_field_ptr_info: InternPool.Key.PtrType = .{ 24473 .child = field_ty.toIntern(), 24474 .flags = .{ 24475 .alignment = try field_ptr_ty.ptrAlignmentSema(pt), 24476 .is_const = field_ptr_info.flags.is_const, 24477 .is_volatile = field_ptr_info.flags.is_volatile, 24478 .is_allowzero = field_ptr_info.flags.is_allowzero, 24479 .address_space = field_ptr_info.flags.address_space, 24480 }, 24481 .packed_offset = field_ptr_info.packed_offset, 24482 }; 24483 switch (parent_ty.containerLayout(zcu)) { 24484 .auto => { 24485 actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict( 24486 if (zcu.typeToStruct(parent_ty)) |struct_obj| 24487 try field_ty.structFieldAlignmentSema( 24488 struct_obj.fieldAlign(ip, field_index), 24489 struct_obj.layout, 24490 pt, 24491 ) 24492 else if (zcu.typeToUnion(parent_ty)) |union_obj| 24493 try field_ty.unionFieldAlignmentSema( 24494 union_obj.fieldAlign(ip, field_index), 24495 union_obj.flagsUnordered(ip).layout, 24496 pt, 24497 ) 24498 else 24499 actual_field_ptr_info.flags.alignment, 24500 ); 24501 24502 actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; 24503 actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; 24504 }, 24505 .@"extern" => { 24506 const field_offset = parent_ty.structFieldOffset(field_index, zcu); 24507 actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (field_offset > 0) 24508 Alignment.fromLog2Units(@ctz(field_offset)) 24509 else 24510 actual_field_ptr_info.flags.alignment); 24511 24512 actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; 24513 actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; 24514 }, 24515 .@"packed" => { 24516 const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) + 24517 (if (zcu.typeToStruct(parent_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, field_index) else 0) - 24518 actual_field_ptr_info.packed_offset.bit_offset), 8) catch 24519 return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{}); 24520 actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0) 24521 Alignment.fromLog2Units(@ctz(byte_offset)) 24522 else 24523 actual_field_ptr_info.flags.alignment); 24524 }, 24525 } 24526 24527 const actual_field_ptr_ty = try pt.ptrTypeSema(actual_field_ptr_info); 24528 const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, field_ptr_src); 24529 const actual_parent_ptr_ty = try pt.ptrTypeSema(actual_parent_ptr_info); 24530 24531 const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: { 24532 switch (parent_ty.zigTypeTag(zcu)) { 24533 .@"struct" => switch (parent_ty.containerLayout(zcu)) { 24534 .auto => {}, 24535 .@"extern" => { 24536 const byte_offset = parent_ty.structFieldOffset(field_index, zcu); 24537 const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty); 24538 break :result Air.internedToRef(parent_ptr_val.toIntern()); 24539 }, 24540 .@"packed" => { 24541 // Logic lifted from type computation above - I'm just assuming it's correct. 24542 // `catch unreachable` since error case handled above. 24543 const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) + 24544 zcu.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) - 24545 actual_field_ptr_info.packed_offset.bit_offset), 8) catch unreachable; 24546 const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty); 24547 break :result Air.internedToRef(parent_ptr_val.toIntern()); 24548 }, 24549 }, 24550 .@"union" => switch (parent_ty.containerLayout(zcu)) { 24551 .auto => {}, 24552 .@"extern", .@"packed" => { 24553 // For an extern or packed union, just coerce the pointer. 24554 const parent_ptr_val = try pt.getCoerced(field_ptr_val, actual_parent_ptr_ty); 24555 break :result Air.internedToRef(parent_ptr_val.toIntern()); 24556 }, 24557 }, 24558 else => unreachable, 24559 } 24560 24561 const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: { 24562 const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) { 24563 .ptr => |ptr| ptr, 24564 else => break :opt_field null, 24565 }; 24566 if (ptr.byte_offset != 0) break :opt_field null; 24567 break :opt_field switch (ptr.base_addr) { 24568 .field => |field| field, 24569 else => null, 24570 }; 24571 }; 24572 24573 const field = opt_field orelse { 24574 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); 24575 }; 24576 24577 if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) { 24578 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); 24579 } 24580 24581 if (field.index != field_index) { 24582 return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{ 24583 field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt), 24584 }); 24585 } 24586 break :result try sema.coerce(block, actual_parent_ptr_ty, Air.internedToRef(field.base), inst_src); 24587 } else result: { 24588 try sema.requireRuntimeBlock(block, inst_src, field_ptr_src); 24589 break :result try block.addInst(.{ 24590 .tag = .field_parent_ptr, 24591 .data = .{ .ty_pl = .{ 24592 .ty = Air.internedToRef(actual_parent_ptr_ty.toIntern()), 24593 .payload = try block.sema.addExtra(Air.FieldParentPtr{ 24594 .field_ptr = casted_field_ptr, 24595 .field_index = @intCast(field_index), 24596 }), 24597 } }, 24598 }); 24599 }; 24600 return sema.ptrCastFull(block, flags, inst_src, result, inst_src, parent_ptr_ty, "@fieldParentPtr"); 24601 } 24602 24603 fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value { 24604 const pt = sema.pt; 24605 const zcu = pt.zcu; 24606 if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty); 24607 var ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) { 24608 .undef => return sema.failWithUseOfUndef(block, src, null), 24609 .ptr => |ptr| ptr, 24610 else => unreachable, 24611 }; 24612 if (ptr.byte_offset < byte_subtract) { 24613 return sema.failWithOwnedErrorMsg(block, msg: { 24614 const msg = try sema.errMsg(src, "pointer computation here causes illegal behavior", .{}); 24615 errdefer msg.destroy(sema.gpa); 24616 try sema.errNote(src, msg, "resulting pointer exceeds bounds of containing value which may trigger overflow", .{}); 24617 break :msg msg; 24618 }); 24619 } 24620 ptr.byte_offset -= byte_subtract; 24621 ptr.ty = new_ty.toIntern(); 24622 return Value.fromInterned(try pt.intern(.{ .ptr = ptr })); 24623 } 24624 24625 fn zirMinMax( 24626 sema: *Sema, 24627 block: *Block, 24628 inst: Zir.Inst.Index, 24629 comptime air_tag: Air.Inst.Tag, 24630 ) CompileError!Air.Inst.Ref { 24631 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24632 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 24633 const src = block.nodeOffset(inst_data.src_node); 24634 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24635 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24636 const lhs = try sema.resolveInst(extra.lhs); 24637 const rhs = try sema.resolveInst(extra.rhs); 24638 return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src }); 24639 } 24640 24641 fn zirMinMaxMulti( 24642 sema: *Sema, 24643 block: *Block, 24644 extended: Zir.Inst.Extended.InstData, 24645 comptime air_tag: Air.Inst.Tag, 24646 ) CompileError!Air.Inst.Ref { 24647 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 24648 const src_node = extra.data.src_node; 24649 const src = block.nodeOffset(src_node); 24650 const operands = sema.code.refSlice(extra.end, extended.small); 24651 24652 const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 24653 const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len); 24654 24655 for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| { 24656 op_src.* = block.builtinCallArgSrc(src_node, @intCast(i)); 24657 air_ref.* = try sema.resolveInst(zir_ref); 24658 } 24659 24660 return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs); 24661 } 24662 24663 fn analyzeMinMax( 24664 sema: *Sema, 24665 block: *Block, 24666 src: LazySrcLoc, 24667 comptime air_tag: Air.Inst.Tag, 24668 operands: []const Air.Inst.Ref, 24669 operand_srcs: []const LazySrcLoc, 24670 ) CompileError!Air.Inst.Ref { 24671 assert(operands.len == operand_srcs.len); 24672 assert(operands.len > 0); 24673 24674 const pt = sema.pt; 24675 const zcu = pt.zcu; 24676 24677 // This function has the signature `fn (Value, Value, *Zcu) Value`. 24678 // It is only used on scalar values, although the values may have different types. 24679 // If either operand is undef, it returns undef. 24680 const opFunc = switch (air_tag) { 24681 .min => Value.numberMin, 24682 .max => Value.numberMax, 24683 else => comptime unreachable, 24684 }; 24685 24686 if (operands.len == 1) { 24687 try sema.checkNumericType(block, operand_srcs[0], sema.typeOf(operands[0])); 24688 return operands[0]; 24689 } 24690 24691 // First, basic type validation; we'll make sure all the operands are numeric and agree on vector length. 24692 // This value will be `null` for a scalar type, otherwise the length of the vector type. 24693 const vector_len: ?u64 = vec_len: { 24694 const first_operand_ty = sema.typeOf(operands[0]); 24695 try sema.checkNumericType(block, operand_srcs[0], first_operand_ty); 24696 if (first_operand_ty.zigTypeTag(zcu) == .vector) { 24697 const vec_len = first_operand_ty.vectorLen(zcu); 24698 for (operands[1..], operand_srcs[1..]) |operand, operand_src| { 24699 const operand_ty = sema.typeOf(operand); 24700 try sema.checkNumericType(block, operand_src, operand_ty); 24701 if (operand_ty.zigTypeTag(zcu) != .vector) { 24702 return sema.failWithOwnedErrorMsg(block, msg: { 24703 const msg = try sema.errMsg(operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)}); 24704 errdefer msg.destroy(zcu.gpa); 24705 try sema.errNote(operand_srcs[0], msg, "vector operand here", .{}); 24706 break :msg msg; 24707 }); 24708 } 24709 if (operand_ty.vectorLen(zcu) != vec_len) { 24710 return sema.failWithOwnedErrorMsg(block, msg: { 24711 const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{f}'", .{ vec_len, operand_ty.fmt(pt) }); 24712 errdefer msg.destroy(zcu.gpa); 24713 try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len}); 24714 break :msg msg; 24715 }); 24716 } 24717 } 24718 break :vec_len vec_len; 24719 } else { 24720 for (operands[1..], operand_srcs[1..]) |operand, operand_src| { 24721 const operand_ty = sema.typeOf(operand); 24722 try sema.checkNumericType(block, operand_src, operand_ty); 24723 if (operand_ty.zigTypeTag(zcu) == .vector) { 24724 return sema.failWithOwnedErrorMsg(block, msg: { 24725 const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)}); 24726 errdefer msg.destroy(zcu.gpa); 24727 try sema.errNote(operand_src, msg, "vector operand here", .{}); 24728 break :msg msg; 24729 }); 24730 } 24731 } 24732 break :vec_len null; 24733 } 24734 }; 24735 24736 // Now we want to look at the scalar types. If any is a float, our result will be a float. This 24737 // union is in "priority" order: `float` overrides `comptime_float` overrides `int`. 24738 const TypeStrat = union(enum) { 24739 float: Type, 24740 comptime_float, 24741 int: struct { 24742 /// If this is still `true` at the end, we will just use a `comptime_int`. 24743 all_comptime_int: bool, 24744 // These two fields tells us about the *result* type, which is refined based on operand types. 24745 // e.g. `@max(u32, i64)` results in a `u63`, because the result is >=0 and <=maxInt(i64). 24746 result_min: Value, 24747 result_max: Value, 24748 // These two fields tell us the *intermediate* type to use for actually computing the min/max. 24749 // e.g. `@max(u32, i64)` uses an intermediate `i64`, because it can fit all our operands. 24750 operand_min: Value, 24751 operand_max: Value, 24752 }, 24753 none, 24754 }; 24755 var cur_strat: TypeStrat = .none; 24756 for (operands) |operand| { 24757 const operand_scalar_ty = sema.typeOf(operand).scalarType(zcu); 24758 const want_strat: TypeStrat = switch (operand_scalar_ty.zigTypeTag(zcu)) { 24759 .comptime_int => s: { 24760 const val = (try sema.resolveValueResolveLazy(operand)).?; 24761 if (val.isUndef(zcu)) break :s .none; 24762 break :s .{ .int = .{ 24763 .all_comptime_int = true, 24764 .result_min = val, 24765 .result_max = val, 24766 .operand_min = val, 24767 .operand_max = val, 24768 } }; 24769 }, 24770 .comptime_float => .comptime_float, 24771 .float => .{ .float = operand_scalar_ty }, 24772 .int => s: { 24773 // If the *value* is comptime-known, we will use that to get tighter bounds. If #3806 24774 // is accepted and implemented, so that integer literals have a tightly-bounded ranged 24775 // integer type (and `comptime_int` ceases to exist), this block should probably go away 24776 // (replaced with just the simple calls to `Type.minInt`/`Type.maxInt`) so that we only 24777 // use the input *types* to determine the result type. 24778 const min: Value, const max: Value = bounds: { 24779 if (try sema.resolveValueResolveLazy(operand)) |operand_val| { 24780 if (vector_len) |len| { 24781 var min = try operand_val.elemValue(pt, 0); 24782 var max = min; 24783 for (1..@intCast(len)) |elem_idx| { 24784 const elem_val = try operand_val.elemValue(pt, elem_idx); 24785 min = Value.numberMin(min, elem_val, zcu); 24786 max = Value.numberMax(max, elem_val, zcu); 24787 } 24788 if (!min.isUndef(zcu) and !max.isUndef(zcu)) { 24789 break :bounds .{ min, max }; 24790 } 24791 } else { 24792 if (!operand_val.isUndef(zcu)) { 24793 break :bounds .{ operand_val, operand_val }; 24794 } 24795 } 24796 } 24797 break :bounds .{ 24798 try operand_scalar_ty.minInt(pt, operand_scalar_ty), 24799 try operand_scalar_ty.maxInt(pt, operand_scalar_ty), 24800 }; 24801 }; 24802 break :s .{ .int = .{ 24803 .all_comptime_int = false, 24804 .result_min = min, 24805 .result_max = max, 24806 .operand_min = min, 24807 .operand_max = max, 24808 } }; 24809 }, 24810 else => unreachable, 24811 }; 24812 if (@intFromEnum(want_strat) < @intFromEnum(cur_strat)) { 24813 // `want_strat` overrides `cur_strat`. 24814 cur_strat = want_strat; 24815 } else if (@intFromEnum(want_strat) == @intFromEnum(cur_strat)) { 24816 // The behavior depends on the tag. 24817 switch (cur_strat) { 24818 .none, .comptime_float => {}, // no payload, so nop 24819 .float => |cur_float| { 24820 const want_float = want_strat.float; 24821 // Select the larger bit size. If the bit size is the same, select whichever is not c_longdouble. 24822 const cur_bits = cur_float.floatBits(zcu.getTarget()); 24823 const want_bits = want_float.floatBits(zcu.getTarget()); 24824 if (want_bits > cur_bits or 24825 (want_bits == cur_bits and 24826 cur_float.toIntern() == .c_longdouble_type and 24827 want_float.toIntern() != .c_longdouble_type)) 24828 { 24829 cur_strat = want_strat; 24830 } 24831 }, 24832 .int => |*cur_int| { 24833 const want_int = want_strat.int; 24834 if (!want_int.all_comptime_int) cur_int.all_comptime_int = false; 24835 cur_int.result_min = opFunc(cur_int.result_min, want_int.result_min, zcu); 24836 cur_int.result_max = opFunc(cur_int.result_max, want_int.result_max, zcu); 24837 cur_int.operand_min = Value.numberMin(cur_int.operand_min, want_int.operand_min, zcu); 24838 cur_int.operand_max = Value.numberMax(cur_int.operand_max, want_int.operand_max, zcu); 24839 }, 24840 } 24841 } 24842 } 24843 24844 // Use `cur_strat` to actually resolve the result type (and intermediate type). 24845 const result_scalar_ty: Type, const intermediate_scalar_ty: Type = switch (cur_strat) { 24846 .float => |ty| .{ ty, ty }, 24847 .comptime_float => .{ .comptime_float, .comptime_float }, 24848 .int => |int| if (int.all_comptime_int) .{ 24849 .comptime_int, 24850 .comptime_int, 24851 } else .{ 24852 try pt.intFittingRange(int.result_min, int.result_max), 24853 try pt.intFittingRange(int.operand_min, int.operand_max), 24854 }, 24855 .none => .{ .comptime_int, .comptime_int }, // all undef comptime ints 24856 }; 24857 const result_ty: Type = if (vector_len) |l| try pt.vectorType(.{ 24858 .len = @intCast(l), 24859 .child = result_scalar_ty.toIntern(), 24860 }) else result_scalar_ty; 24861 const intermediate_ty: Type = if (vector_len) |l| try pt.vectorType(.{ 24862 .len = @intCast(l), 24863 .child = intermediate_scalar_ty.toIntern(), 24864 }) else intermediate_scalar_ty; 24865 24866 // This value, if not `null`, will have type `intermediate_ty`. 24867 const comptime_part: ?Value = ct: { 24868 // Contains the comptime-known scalar result values. 24869 // Values are scalars with no particular type. 24870 // `elems.len` is `vector_len orelse 1`. 24871 const elems: []InternPool.Index = try sema.arena.alloc( 24872 InternPool.Index, 24873 try sema.usizeCast(block, src, vector_len orelse 1), 24874 ); 24875 // If `false`, we've not seen any comptime-known operand yet, so `elems` contains `undefined`. 24876 // Otherwise, `elems` is populated with the comptime-known results so far. 24877 var elems_populated = false; 24878 // Populated when we see a runtime-known operand. 24879 var opt_runtime_src: ?LazySrcLoc = null; 24880 24881 for (operands, operand_srcs) |operand, operand_src| { 24882 const operand_val = try sema.resolveValueResolveLazy(operand) orelse { 24883 if (opt_runtime_src == null) opt_runtime_src = operand_src; 24884 continue; 24885 }; 24886 if (vector_len) |len| { 24887 // Vector case; apply `opFunc` to each element. 24888 if (elems_populated) { 24889 for (elems, 0..@intCast(len)) |*elem, elem_idx| { 24890 const new_elem = try operand_val.elemValue(pt, elem_idx); 24891 elem.* = opFunc(.fromInterned(elem.*), new_elem, zcu).toIntern(); 24892 } 24893 } else { 24894 elems_populated = true; 24895 for (elems, 0..@intCast(len)) |*elem_out, elem_idx| { 24896 elem_out.* = (try operand_val.elemValue(pt, elem_idx)).toIntern(); 24897 } 24898 } 24899 } else { 24900 // Scalar case; just apply `opFunc`. 24901 if (elems_populated) { 24902 elems[0] = opFunc(.fromInterned(elems[0]), operand_val, zcu).toIntern(); 24903 } else { 24904 elems_populated = true; 24905 elems[0] = operand_val.toIntern(); 24906 } 24907 } 24908 } 24909 const runtime_src = opt_runtime_src orelse { 24910 // The result is comptime-known. Coerce each element to its scalar type. 24911 assert(elems_populated); 24912 for (elems) |*elem| { 24913 if (Value.fromInterned(elem.*).isUndef(zcu)) { 24914 elem.* = (try pt.undefValue(result_scalar_ty)).toIntern(); 24915 } else { 24916 // This coercion will always succeed, because `result_scalar_ty` can definitely hold the result. 24917 const coerced_ref = try sema.coerce(block, result_scalar_ty, Air.internedToRef(elem.*), .unneeded); 24918 elem.* = coerced_ref.toInterned().?; 24919 } 24920 } 24921 if (vector_len == null) return Air.internedToRef(elems[0]); 24922 return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern()); 24923 }; 24924 _ = runtime_src; 24925 // The result is runtime-known. 24926 // Coerce each element to the intermediate scalar type, unless there were no comptime-known operands. 24927 if (!elems_populated) break :ct null; 24928 for (elems) |*elem| { 24929 if (Value.fromInterned(elem.*).isUndef(zcu)) { 24930 elem.* = (try pt.undefValue(intermediate_scalar_ty)).toIntern(); 24931 } else { 24932 // This coercion will always succeed, because `intermediate_scalar_ty` can definitely hold all operands. 24933 const coerced_ref = try sema.coerce(block, intermediate_scalar_ty, Air.internedToRef(elem.*), .unneeded); 24934 elem.* = coerced_ref.toInterned().?; 24935 } 24936 } 24937 break :ct if (vector_len != null) 24938 try pt.aggregateValue(intermediate_ty, elems) 24939 else 24940 .fromInterned(elems[0]); 24941 }; 24942 24943 // Time to emit the runtime operations. All runtime-known peers are coerced to `intermediate_ty`, and we cast down to `result_ty` at the end. 24944 24945 // `.none` indicates no result so far. 24946 var cur_result: Air.Inst.Ref = if (comptime_part) |val| Air.internedToRef(val.toIntern()) else .none; 24947 for (operands, operand_srcs) |operand, operand_src| { 24948 if (try sema.isComptimeKnown(operand)) continue; // already in `comptime_part` 24949 // This coercion could fail; e.g. coercing a runtime integer peer to a `comptime_float` in a case like `@min(runtime_int, 1.5)`. 24950 const operand_coerced = try sema.coerce(block, intermediate_ty, operand, operand_src); 24951 if (cur_result == .none) { 24952 cur_result = operand_coerced; 24953 } else { 24954 cur_result = try block.addBinOp(air_tag, cur_result, operand_coerced); 24955 } 24956 } 24957 24958 assert(cur_result != .none); 24959 assert(sema.typeOf(cur_result).toIntern() == intermediate_ty.toIntern()); 24960 24961 // If there is a comptime-known undef operand, we actually return comptime-known undef -- but we had to do the runtime stuff to check for coercion errors. 24962 if (comptime_part) |val| { 24963 if (val.isUndef(zcu)) { 24964 return pt.undefRef(result_ty); 24965 } 24966 } 24967 24968 if (result_ty.toIntern() == intermediate_ty.toIntern()) { 24969 // No final cast needed; we're all done. 24970 return cur_result; 24971 } 24972 24973 // A final cast is needed. The only case where `intermediate_ty` is different is for integers, 24974 // where we have refined the range, so we should be doing an intcast. 24975 assert(intermediate_scalar_ty.zigTypeTag(zcu) == .int); 24976 assert(result_scalar_ty.zigTypeTag(zcu) == .int); 24977 return block.addTyOp(.intcast, result_ty, cur_result); 24978 } 24979 24980 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { 24981 const pt = sema.pt; 24982 const zcu = pt.zcu; 24983 const ptr_ty = sema.typeOf(ptr); 24984 const info = ptr_ty.ptrInfo(zcu); 24985 if (info.flags.size == .one) { 24986 // Already an array pointer. 24987 return ptr; 24988 } 24989 const new_ty = try pt.ptrTypeSema(.{ 24990 .child = (try pt.arrayType(.{ 24991 .len = len, 24992 .sentinel = info.sentinel, 24993 .child = info.child, 24994 })).toIntern(), 24995 .flags = .{ 24996 .alignment = info.flags.alignment, 24997 .is_const = info.flags.is_const, 24998 .is_volatile = info.flags.is_volatile, 24999 .is_allowzero = info.flags.is_allowzero, 25000 .address_space = info.flags.address_space, 25001 }, 25002 }); 25003 const non_slice_ptr = if (info.flags.size == .slice) 25004 try block.addTyOp(.slice_ptr, ptr_ty.slicePtrFieldType(zcu), ptr) 25005 else 25006 ptr; 25007 return block.addBitCast(new_ty, non_slice_ptr); 25008 } 25009 25010 fn zirMemcpy( 25011 sema: *Sema, 25012 block: *Block, 25013 inst: Zir.Inst.Index, 25014 air_tag: Air.Inst.Tag, 25015 check_aliasing: bool, 25016 ) CompileError!void { 25017 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 25018 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 25019 const src = block.nodeOffset(inst_data.src_node); 25020 const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0); 25021 const src_src = block.builtinCallArgSrc(inst_data.src_node, 1); 25022 const dest_ptr = try sema.resolveInst(extra.lhs); 25023 const src_ptr = try sema.resolveInst(extra.rhs); 25024 const dest_ty = sema.typeOf(dest_ptr); 25025 const src_ty = sema.typeOf(src_ptr); 25026 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); 25027 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); 25028 const pt = sema.pt; 25029 const zcu = pt.zcu; 25030 25031 if (dest_ty.isConstPtr(zcu)) { 25032 return sema.fail(block, dest_src, "cannot copy to constant pointer", .{}); 25033 } 25034 25035 if (dest_len == .none and src_len == .none) { 25036 const msg = msg: { 25037 const msg = try sema.errMsg(src, "unknown copy length", .{}); 25038 errdefer msg.destroy(sema.gpa); 25039 try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{ 25040 dest_ty.fmt(pt), 25041 }); 25042 try sema.errNote(src_src, msg, "source type '{f}' provides no length", .{ 25043 src_ty.fmt(pt), 25044 }); 25045 break :msg msg; 25046 }; 25047 return sema.failWithOwnedErrorMsg(block, msg); 25048 } 25049 25050 const dest_elem_ty = dest_ty.indexablePtrElem(zcu); 25051 const src_elem_ty = src_ty.indexablePtrElem(zcu); 25052 25053 const imc = try sema.coerceInMemoryAllowed( 25054 block, 25055 dest_elem_ty, 25056 src_elem_ty, 25057 false, 25058 zcu.getTarget(), 25059 dest_src, 25060 src_src, 25061 null, 25062 ); 25063 if (imc != .ok) return sema.failWithOwnedErrorMsg(block, msg: { 25064 const msg = try sema.errMsg( 25065 src, 25066 "pointer element type '{f}' cannot coerce into element type '{f}'", 25067 .{ src_elem_ty.fmt(pt), dest_elem_ty.fmt(pt) }, 25068 ); 25069 errdefer msg.destroy(sema.gpa); 25070 try imc.report(sema, src, msg); 25071 break :msg msg; 25072 }); 25073 25074 var len_val: ?Value = null; 25075 25076 if (dest_len != .none and src_len != .none) check: { 25077 // If we can check at compile-time, no need for runtime safety. 25078 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 25079 len_val = dest_len_val; 25080 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 25081 if (!(try sema.valuesEqual(dest_len_val, src_len_val, .usize))) { 25082 const msg = msg: { 25083 const msg = try sema.errMsg(src, "non-matching copy lengths", .{}); 25084 errdefer msg.destroy(sema.gpa); 25085 try sema.errNote(dest_src, msg, "length {f} here", .{ 25086 dest_len_val.fmtValueSema(pt, sema), 25087 }); 25088 try sema.errNote(src_src, msg, "length {f} here", .{ 25089 src_len_val.fmtValueSema(pt, sema), 25090 }); 25091 break :msg msg; 25092 }; 25093 return sema.failWithOwnedErrorMsg(block, msg); 25094 } 25095 break :check; 25096 } 25097 } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 25098 len_val = src_len_val; 25099 } 25100 25101 if (block.wantSafety()) { 25102 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); 25103 try sema.addSafetyCheck(block, src, ok, .copy_len_mismatch); 25104 } 25105 } else if (dest_len != .none) { 25106 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 25107 len_val = dest_len_val; 25108 } 25109 } else if (src_len != .none) { 25110 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 25111 len_val = src_len_val; 25112 } 25113 } 25114 25115 zero_bit: { 25116 const src_comptime = try src_elem_ty.comptimeOnlySema(pt); 25117 const dest_comptime = try dest_elem_ty.comptimeOnlySema(pt); 25118 assert(src_comptime == dest_comptime); // IMC 25119 if (src_comptime) break :zero_bit; 25120 25121 const src_has_bits = try src_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); 25122 const dest_has_bits = try dest_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); 25123 assert(src_has_bits == dest_has_bits); // IMC 25124 if (src_has_bits) break :zero_bit; 25125 25126 // The element type is zero-bit. We've done all validation (aside from the aliasing check, 25127 // which we must skip) so we're done. 25128 return; 25129 } 25130 25131 const runtime_src = rs: { 25132 const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; 25133 const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src; 25134 25135 const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val; 25136 const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val; 25137 25138 const len_u64 = try len_val.?.toUnsignedIntSema(pt); 25139 25140 if (check_aliasing) { 25141 if (Value.doPointersOverlap( 25142 raw_src_ptr, 25143 raw_dest_ptr, 25144 len_u64, 25145 zcu, 25146 )) return sema.fail(block, src, "'@memcpy' arguments alias", .{}); 25147 } 25148 25149 if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src; 25150 25151 // Because comptime pointer access is a somewhat expensive operation, we implement @memcpy 25152 // as one load and store of an array, rather than N loads and stores of individual elements. 25153 25154 const array_ty = try pt.arrayType(.{ 25155 .child = dest_elem_ty.toIntern(), 25156 .len = len_u64, 25157 }); 25158 25159 const dest_array_ptr_ty = try pt.ptrType(info: { 25160 var info = dest_ty.ptrInfo(zcu); 25161 info.flags.size = .one; 25162 info.child = array_ty.toIntern(); 25163 info.sentinel = .none; 25164 break :info info; 25165 }); 25166 const src_array_ptr_ty = try pt.ptrType(info: { 25167 var info = src_ty.ptrInfo(zcu); 25168 info.flags.size = .one; 25169 info.child = array_ty.toIntern(); 25170 info.sentinel = .none; 25171 break :info info; 25172 }); 25173 25174 const coerced_dest_ptr = try pt.getCoerced(raw_dest_ptr, dest_array_ptr_ty); 25175 const coerced_src_ptr = try pt.getCoerced(raw_src_ptr, src_array_ptr_ty); 25176 25177 const array_val = try sema.pointerDeref(block, src_src, coerced_src_ptr, src_array_ptr_ty) orelse break :rs src_src; 25178 try sema.storePtrVal(block, dest_src, coerced_dest_ptr, array_val, array_ty); 25179 return; 25180 }; 25181 25182 // If the length is comptime-known, then upgrade src and destination types 25183 // into pointer-to-array. At this point we know they are both pointers 25184 // already. 25185 var new_dest_ptr = dest_ptr; 25186 var new_src_ptr = src_ptr; 25187 if (len_val) |val| { 25188 const len = try val.toUnsignedIntSema(pt); 25189 if (len == 0) { 25190 // This AIR instruction guarantees length > 0 if it is comptime-known. 25191 return; 25192 } 25193 new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len); 25194 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len); 25195 } 25196 25197 if (dest_len != .none) { 25198 // Change the src from slice to a many pointer, to avoid multiple ptr 25199 // slice extractions in AIR instructions. 25200 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 25201 if (new_src_ptr_ty.isSlice(zcu)) { 25202 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 25203 } 25204 } else if (dest_len == .none and len_val == null) { 25205 // Change the dest to a slice, since its type must have the length. 25206 const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr); 25207 new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, LazySrcLoc.unneeded, dest_src, dest_src, dest_src, false); 25208 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 25209 if (new_src_ptr_ty.isSlice(zcu)) { 25210 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 25211 } 25212 } 25213 25214 try sema.requireRuntimeBlock(block, src, runtime_src); 25215 try sema.validateRuntimeValue(block, dest_src, dest_ptr); 25216 try sema.validateRuntimeValue(block, src_src, src_ptr); 25217 25218 // Aliasing safety check. 25219 if (check_aliasing and block.wantSafety()) { 25220 const len = if (len_val) |v| 25221 Air.internedToRef(v.toIntern()) 25222 else if (dest_len != .none) 25223 dest_len 25224 else 25225 src_len; 25226 25227 // Extract raw pointer from dest slice. The AIR instructions could support them, but 25228 // it would cause redundant machine code instructions. 25229 const new_dest_ptr_ty = sema.typeOf(new_dest_ptr); 25230 const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(zcu)) 25231 try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty) 25232 else if (new_dest_ptr_ty.ptrSize(zcu) == .one) ptr: { 25233 var dest_manyptr_ty_key = zcu.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type; 25234 assert(dest_manyptr_ty_key.flags.size == .one); 25235 dest_manyptr_ty_key.child = dest_elem_ty.toIntern(); 25236 dest_manyptr_ty_key.flags.size = .many; 25237 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(dest_manyptr_ty_key), new_dest_ptr, dest_src); 25238 } else new_dest_ptr; 25239 25240 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 25241 const raw_src_ptr = if (new_src_ptr_ty.isSlice(zcu)) 25242 try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty) 25243 else if (new_src_ptr_ty.ptrSize(zcu) == .one) ptr: { 25244 var src_manyptr_ty_key = zcu.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type; 25245 assert(src_manyptr_ty_key.flags.size == .one); 25246 src_manyptr_ty_key.child = src_elem_ty.toIntern(); 25247 src_manyptr_ty_key.flags.size = .many; 25248 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(src_manyptr_ty_key), new_src_ptr, src_src); 25249 } else new_src_ptr; 25250 25251 // ok1: dest >= src + len 25252 // ok2: src >= dest + len 25253 const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src); 25254 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src); 25255 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len); 25256 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len); 25257 const ok = try block.addBinOp(.bool_or, ok1, ok2); 25258 try sema.addSafetyCheck(block, src, ok, .memcpy_alias); 25259 } 25260 25261 _ = try block.addInst(.{ 25262 .tag = air_tag, 25263 .data = .{ .bin_op = .{ 25264 .lhs = new_dest_ptr, 25265 .rhs = new_src_ptr, 25266 } }, 25267 }); 25268 } 25269 25270 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 25271 const pt = sema.pt; 25272 const zcu = pt.zcu; 25273 const gpa = sema.gpa; 25274 const ip = &zcu.intern_pool; 25275 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 25276 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 25277 const src = block.nodeOffset(inst_data.src_node); 25278 const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0); 25279 const value_src = block.builtinCallArgSrc(inst_data.src_node, 1); 25280 const dest_ptr = try sema.resolveInst(extra.lhs); 25281 const uncoerced_elem = try sema.resolveInst(extra.rhs); 25282 const dest_ptr_ty = sema.typeOf(dest_ptr); 25283 try checkMemOperand(sema, block, dest_src, dest_ptr_ty); 25284 25285 if (dest_ptr_ty.isConstPtr(zcu)) { 25286 return sema.fail(block, dest_src, "cannot memset constant pointer", .{}); 25287 } 25288 25289 const dest_elem_ty: Type = dest_elem_ty: { 25290 const ptr_info = dest_ptr_ty.ptrInfo(zcu); 25291 switch (ptr_info.flags.size) { 25292 .slice => break :dest_elem_ty .fromInterned(ptr_info.child), 25293 .one => { 25294 if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { 25295 break :dest_elem_ty Type.fromInterned(ptr_info.child).childType(zcu); 25296 } 25297 }, 25298 .many, .c => {}, 25299 } 25300 return sema.failWithOwnedErrorMsg(block, msg: { 25301 const msg = try sema.errMsg(src, "unknown @memset length", .{}); 25302 errdefer msg.destroy(sema.gpa); 25303 try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{ 25304 dest_ptr_ty.fmt(pt), 25305 }); 25306 break :msg msg; 25307 }); 25308 }; 25309 25310 const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); 25311 25312 const runtime_src = rs: { 25313 const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; 25314 const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), dest_src); 25315 const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src; 25316 const len_u64 = try len_val.toUnsignedIntSema(pt); 25317 const len = try sema.usizeCast(block, dest_src, len_u64); 25318 if (len == 0) { 25319 // This AIR instruction guarantees length > 0 if it is comptime-known. 25320 return; 25321 } 25322 25323 if (!sema.isComptimeMutablePtr(ptr_val)) break :rs dest_src; 25324 const elem_val = try sema.resolveValue(elem) orelse break :rs value_src; 25325 const array_ty = try pt.arrayType(.{ 25326 .child = dest_elem_ty.toIntern(), 25327 .len = len_u64, 25328 }); 25329 const array_val = try pt.aggregateSplatValue(array_ty, elem_val); 25330 const array_ptr_ty = ty: { 25331 var info = dest_ptr_ty.ptrInfo(zcu); 25332 info.flags.size = .one; 25333 info.child = array_ty.toIntern(); 25334 break :ty try pt.ptrType(info); 25335 }; 25336 const raw_ptr_val = if (dest_ptr_ty.isSlice(zcu)) ptr_val.slicePtr(zcu) else ptr_val; 25337 const array_ptr_val = try pt.getCoerced(raw_ptr_val, array_ptr_ty); 25338 return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty); 25339 }; 25340 25341 try sema.requireRuntimeBlock(block, src, runtime_src); 25342 try sema.validateRuntimeValue(block, dest_src, dest_ptr); 25343 try sema.validateRuntimeValue(block, value_src, elem); 25344 25345 _ = try block.addInst(.{ 25346 .tag = if (block.wantSafety()) .memset_safe else .memset, 25347 .data = .{ .bin_op = .{ 25348 .lhs = dest_ptr, 25349 .rhs = elem, 25350 } }, 25351 }); 25352 } 25353 25354 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 25355 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 25356 const src = block.nodeOffset(inst_data.src_node); 25357 return sema.failWithUseOfAsync(block, src); 25358 } 25359 25360 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 25361 const tracy = trace(@src()); 25362 defer tracy.end(); 25363 25364 const pt = sema.pt; 25365 const zcu = pt.zcu; 25366 const ip = &zcu.intern_pool; 25367 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 25368 const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); 25369 const target = zcu.getTarget(); 25370 25371 const cc_src = block.src(.{ .node_offset_fn_type_cc = inst_data.src_node }); 25372 const ret_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node }); 25373 const has_body = extra.data.body_len != 0; 25374 25375 var extra_index: usize = extra.end; 25376 25377 const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: { 25378 const body_len = sema.code.extra[extra_index]; 25379 extra_index += 1; 25380 const body = sema.code.bodySlice(extra_index, body_len); 25381 extra_index += body.len; 25382 25383 const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention); 25384 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{ .simple = .@"callconv" }); 25385 break :blk try sema.analyzeValueAsCallconv(block, cc_src, val); 25386 } else if (extra.data.bits.has_cc_ref) blk: { 25387 const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 25388 extra_index += 1; 25389 const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention); 25390 const uncoerced_cc = try sema.resolveInst(cc_ref); 25391 const coerced_cc = try sema.coerce(block, cc_ty, uncoerced_cc, cc_src); 25392 const cc_val = try sema.resolveConstDefinedValue(block, cc_src, coerced_cc, .{ .simple = .@"callconv" }); 25393 break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val); 25394 } else cc: { 25395 if (has_body) { 25396 const func_decl_nav = sema.owner.unwrap().nav_val; 25397 const func_decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(&zcu.intern_pool) orelse return error.AnalysisFail; 25398 const zir_decl = sema.code.getDeclaration(func_decl_inst); 25399 if (zir_decl.linkage == .@"export") { 25400 break :cc target.cCallingConvention() orelse { 25401 // This target has no default C calling convention. We sometimes trigger a similar 25402 // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency, 25403 // let's eval that now and just get the transitive error. (It's guaranteed to error 25404 // because it does the exact `cCallingConvention` call we just did.) 25405 const cc_type = try sema.getBuiltinType(cc_src, .CallingConvention); 25406 _ = try sema.namespaceLookupVal( 25407 block, 25408 LazySrcLoc.unneeded, 25409 cc_type.getNamespaceIndex(zcu), 25410 try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls), 25411 ); 25412 // The above should have errored. 25413 @panic("std.builtin is corrupt"); 25414 }; 25415 } 25416 } 25417 break :cc .auto; 25418 }; 25419 25420 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { 25421 const body_len = sema.code.extra[extra_index]; 25422 extra_index += 1; 25423 const body = sema.code.bodySlice(extra_index, body_len); 25424 extra_index += body.len; 25425 if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison; 25426 25427 const val = try sema.resolveGenericBody(block, ret_src, body, inst, .type, .{ .simple = .function_ret_ty }); 25428 const ty = val.toType(); 25429 break :blk ty; 25430 } else if (extra.data.bits.has_ret_ty_ref) blk: { 25431 const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 25432 extra_index += 1; 25433 if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison; 25434 25435 const ret_ty_air_ref = try sema.resolveInst(ret_ty_ref); 25436 const ret_ty_val = try sema.resolveConstDefinedValue(block, ret_src, ret_ty_air_ref, .{ .simple = .function_ret_ty }); 25437 break :blk ret_ty_val.toType(); 25438 } else .void; 25439 25440 const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { 25441 const x = sema.code.extra[extra_index]; 25442 extra_index += 1; 25443 break :blk x; 25444 } else 0; 25445 25446 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 25447 if (has_body) { 25448 extra_index += extra.data.body_len; 25449 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 25450 } 25451 25452 const is_var_args = extra.data.bits.is_var_args; 25453 const is_inferred_error = extra.data.bits.is_inferred_error; 25454 const is_noinline = extra.data.bits.is_noinline; 25455 25456 return sema.funcCommon( 25457 block, 25458 inst_data.src_node, 25459 inst, 25460 cc, 25461 ret_ty, 25462 is_var_args, 25463 is_inferred_error, 25464 has_body, 25465 src_locs, 25466 noalias_bits, 25467 is_noinline, 25468 ); 25469 } 25470 25471 fn zirCUndef( 25472 sema: *Sema, 25473 block: *Block, 25474 extended: Zir.Inst.Extended.InstData, 25475 ) CompileError!Air.Inst.Ref { 25476 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25477 const src = block.builtinCallArgSrc(extra.node, 0); 25478 25479 const name = try sema.resolveConstString(block, src, extra.operand, .{ .simple = .operand_cUndef_macro_name }); 25480 try block.c_import_buf.?.print("#undef {s}\n", .{name}); 25481 return .void_value; 25482 } 25483 25484 fn zirCInclude( 25485 sema: *Sema, 25486 block: *Block, 25487 extended: Zir.Inst.Extended.InstData, 25488 ) CompileError!Air.Inst.Ref { 25489 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25490 const src = block.builtinCallArgSrc(extra.node, 0); 25491 25492 const name = try sema.resolveConstString(block, src, extra.operand, .{ .simple = .operand_cInclude_file_name }); 25493 try block.c_import_buf.?.print("#include <{s}>\n", .{name}); 25494 return .void_value; 25495 } 25496 25497 fn zirCDefine( 25498 sema: *Sema, 25499 block: *Block, 25500 extended: Zir.Inst.Extended.InstData, 25501 ) CompileError!Air.Inst.Ref { 25502 const pt = sema.pt; 25503 const zcu = pt.zcu; 25504 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25505 const name_src = block.builtinCallArgSrc(extra.node, 0); 25506 const val_src = block.builtinCallArgSrc(extra.node, 1); 25507 25508 const name = try sema.resolveConstString(block, name_src, extra.lhs, .{ .simple = .operand_cDefine_macro_name }); 25509 const rhs = try sema.resolveInst(extra.rhs); 25510 if (sema.typeOf(rhs).zigTypeTag(zcu) != .void) { 25511 const value = try sema.resolveConstString(block, val_src, extra.rhs, .{ .simple = .operand_cDefine_macro_value }); 25512 try block.c_import_buf.?.print("#define {s} {s}\n", .{ name, value }); 25513 } else { 25514 try block.c_import_buf.?.print("#define {s}\n", .{name}); 25515 } 25516 return .void_value; 25517 } 25518 25519 fn zirWasmMemorySize( 25520 sema: *Sema, 25521 block: *Block, 25522 extended: Zir.Inst.Extended.InstData, 25523 ) CompileError!Air.Inst.Ref { 25524 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25525 const index_src = block.builtinCallArgSrc(extra.node, 0); 25526 const builtin_src = block.nodeOffset(extra.node); 25527 const target = sema.pt.zcu.getTarget(); 25528 if (!target.cpu.arch.isWasm()) { 25529 return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 25530 } 25531 25532 const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.operand, .u32, .{ .simple = .wasm_memory_index })); 25533 try sema.requireRuntimeBlock(block, builtin_src, null); 25534 return block.addInst(.{ 25535 .tag = .wasm_memory_size, 25536 .data = .{ .pl_op = .{ 25537 .operand = .none, 25538 .payload = index, 25539 } }, 25540 }); 25541 } 25542 25543 fn zirWasmMemoryGrow( 25544 sema: *Sema, 25545 block: *Block, 25546 extended: Zir.Inst.Extended.InstData, 25547 ) CompileError!Air.Inst.Ref { 25548 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25549 const builtin_src = block.nodeOffset(extra.node); 25550 const index_src = block.builtinCallArgSrc(extra.node, 0); 25551 const delta_src = block.builtinCallArgSrc(extra.node, 1); 25552 const target = sema.pt.zcu.getTarget(); 25553 if (!target.cpu.arch.isWasm()) { 25554 return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 25555 } 25556 25557 const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, .u32, .{ .simple = .wasm_memory_index })); 25558 const delta = try sema.coerce(block, .usize, try sema.resolveInst(extra.rhs), delta_src); 25559 25560 try sema.requireRuntimeBlock(block, builtin_src, null); 25561 return block.addInst(.{ 25562 .tag = .wasm_memory_grow, 25563 .data = .{ .pl_op = .{ 25564 .operand = delta, 25565 .payload = index, 25566 } }, 25567 }); 25568 } 25569 25570 fn resolvePrefetchOptions( 25571 sema: *Sema, 25572 block: *Block, 25573 src: LazySrcLoc, 25574 zir_ref: Zir.Inst.Ref, 25575 ) CompileError!std.builtin.PrefetchOptions { 25576 const pt = sema.pt; 25577 const zcu = pt.zcu; 25578 const gpa = sema.gpa; 25579 const ip = &zcu.intern_pool; 25580 const options_ty = try sema.getBuiltinType(src, .PrefetchOptions); 25581 const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src); 25582 25583 const rw_src = block.src(.{ .init_field_rw = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25584 const locality_src = block.src(.{ .init_field_locality = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25585 const cache_src = block.src(.{ .init_field_cache = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25586 25587 const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "rw", .no_embedded_nulls), rw_src); 25588 const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ .simple = .prefetch_options }); 25589 25590 const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "locality", .no_embedded_nulls), locality_src); 25591 const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ .simple = .prefetch_options }); 25592 25593 const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "cache", .no_embedded_nulls), cache_src); 25594 const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ .simple = .prefetch_options }); 25595 25596 return std.builtin.PrefetchOptions{ 25597 .rw = try sema.interpretBuiltinType(block, rw_src, rw_val, std.builtin.PrefetchOptions.Rw), 25598 .locality = @intCast(try locality_val.toUnsignedIntSema(pt)), 25599 .cache = try sema.interpretBuiltinType(block, cache_src, cache_val, std.builtin.PrefetchOptions.Cache), 25600 }; 25601 } 25602 25603 fn zirPrefetch( 25604 sema: *Sema, 25605 block: *Block, 25606 extended: Zir.Inst.Extended.InstData, 25607 ) CompileError!Air.Inst.Ref { 25608 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25609 const ptr_src = block.builtinCallArgSrc(extra.node, 0); 25610 const opts_src = block.builtinCallArgSrc(extra.node, 1); 25611 const ptr = try sema.resolveInst(extra.lhs); 25612 try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); 25613 25614 const options = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs); 25615 25616 if (!block.isComptime()) { 25617 _ = try block.addInst(.{ 25618 .tag = .prefetch, 25619 .data = .{ .prefetch = .{ 25620 .ptr = ptr, 25621 .rw = options.rw, 25622 .locality = options.locality, 25623 .cache = options.cache, 25624 } }, 25625 }); 25626 } 25627 25628 return .void_value; 25629 } 25630 25631 fn resolveExternOptions( 25632 sema: *Sema, 25633 block: *Block, 25634 src: LazySrcLoc, 25635 zir_ref: Zir.Inst.Ref, 25636 ) CompileError!struct { 25637 name: InternPool.NullTerminatedString, 25638 library_name: InternPool.OptionalNullTerminatedString, 25639 linkage: std.builtin.GlobalLinkage, 25640 visibility: std.builtin.SymbolVisibility, 25641 is_thread_local: bool, 25642 is_dll_import: bool, 25643 relocation: std.builtin.ExternOptions.Relocation, 25644 } { 25645 const pt = sema.pt; 25646 const zcu = pt.zcu; 25647 const gpa = sema.gpa; 25648 const ip = &zcu.intern_pool; 25649 const options_inst = try sema.resolveInst(zir_ref); 25650 const extern_options_ty = try sema.getBuiltinType(src, .ExternOptions); 25651 const options = try sema.coerce(block, extern_options_ty, options_inst, src); 25652 25653 const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25654 const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25655 const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25656 const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25657 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25658 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25659 const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25660 25661 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src); 25662 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options }); 25663 25664 const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "library_name", .no_embedded_nulls), library_src); 25665 const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ .simple = .extern_options }); 25666 25667 const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src); 25668 const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options }); 25669 const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage); 25670 25671 const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src); 25672 const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options }); 25673 const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility); 25674 25675 const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src); 25676 const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options }); 25677 25678 const library_name = if (library_name_val.optionalValue(zcu)) |library_name_payload| library_name: { 25679 const library_name = try sema.toConstString(block, library_src, Air.internedToRef(library_name_payload.toIntern()), .{ .simple = .extern_options }); 25680 if (library_name.len == 0) { 25681 return sema.fail(block, library_src, "library name cannot be empty", .{}); 25682 } 25683 try sema.handleExternLibName(block, library_src, library_name); 25684 break :library_name library_name; 25685 } else null; 25686 25687 const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src); 25688 const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options }); 25689 25690 const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "relocation", .no_embedded_nulls), relocation_src); 25691 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options }); 25692 const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation); 25693 25694 if (name.len == 0) { 25695 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{}); 25696 } 25697 25698 if (linkage != .weak and linkage != .strong) { 25699 return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{}); 25700 } 25701 25702 return .{ 25703 .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls), 25704 .library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls), 25705 .linkage = linkage, 25706 .visibility = visibility, 25707 .is_thread_local = is_thread_local_val.toBool(), 25708 .is_dll_import = is_dll_import_val.toBool(), 25709 .relocation = relocation, 25710 }; 25711 } 25712 25713 fn zirBuiltinExtern( 25714 sema: *Sema, 25715 block: *Block, 25716 extended: Zir.Inst.Extended.InstData, 25717 ) CompileError!Air.Inst.Ref { 25718 const pt = sema.pt; 25719 const zcu = pt.zcu; 25720 const ip = &zcu.intern_pool; 25721 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25722 const src = block.nodeOffset(extra.node); 25723 const ty_src = block.builtinCallArgSrc(extra.node, 0); 25724 const options_src = block.builtinCallArgSrc(extra.node, 1); 25725 25726 var ty = try sema.resolveType(block, ty_src, extra.lhs); 25727 if (!ty.isPtrAtRuntime(zcu)) { 25728 return sema.fail(block, ty_src, "expected (optional) pointer", .{}); 25729 } 25730 if (!try sema.validateExternType(ty, .other)) { 25731 const msg = msg: { 25732 const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ty.fmt(pt)}); 25733 errdefer msg.destroy(sema.gpa); 25734 try sema.explainWhyTypeIsNotExtern(msg, ty_src, ty, .other); 25735 break :msg msg; 25736 }; 25737 return sema.failWithOwnedErrorMsg(block, msg); 25738 } 25739 25740 const options = try sema.resolveExternOptions(block, options_src, extra.rhs); 25741 switch (options.linkage) { 25742 .internal => if (options.visibility != .default) { 25743 return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{}); 25744 }, 25745 .strong, .weak => {}, 25746 .link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}), 25747 } 25748 switch (options.relocation) { 25749 .any => {}, 25750 .pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}), 25751 } 25752 25753 // TODO: error for threadlocal functions, non-const functions, etc 25754 25755 if (options.linkage == .weak and !ty.ptrAllowsZero(zcu)) { 25756 ty = try pt.optionalType(ty.toIntern()); 25757 } 25758 const ptr_info = ty.ptrInfo(zcu); 25759 25760 const extern_val = try pt.getExtern(.{ 25761 .name = options.name, 25762 .ty = ptr_info.child, 25763 .lib_name = options.library_name, 25764 .linkage = options.linkage, 25765 .visibility = options.visibility, 25766 .is_threadlocal = options.is_thread_local, 25767 .is_dll_import = options.is_dll_import, 25768 .relocation = options.relocation, 25769 .is_const = ptr_info.flags.is_const, 25770 .alignment = ptr_info.flags.alignment, 25771 .@"addrspace" = ptr_info.flags.address_space, 25772 // This instruction is just for source locations. 25773 // `builtin_extern` doesn't provide enough information, and isn't currently tracked. 25774 // So, for now, just use our containing `declaration`. 25775 .zir_index = switch (sema.owner.unwrap()) { 25776 .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index, 25777 .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?, 25778 .memoized_state => unreachable, 25779 .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index, 25780 .func => |func| zir_index: { 25781 const func_info = zcu.funcInfo(func); 25782 const owner_func_info = if (func_info.generic_owner != .none) owner: { 25783 break :owner zcu.funcInfo(func_info.generic_owner); 25784 } else func_info; 25785 break :zir_index ip.getNav(owner_func_info.owner_nav).analysis.?.zir_index; 25786 }, 25787 }, 25788 .owner_nav = undefined, // ignored by `getExtern` 25789 .source = .builtin, 25790 }); 25791 25792 const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav); 25793 // We want to cast to `ty`, but that isn't necessarily an allowed coercion. 25794 if (try sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| { 25795 const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, ty); 25796 return Air.internedToRef(casted_ptr_val.toIntern()); 25797 } else { 25798 return block.addBitCast(ty, uncasted_ptr); 25799 } 25800 } 25801 25802 fn zirWorkItem( 25803 sema: *Sema, 25804 block: *Block, 25805 extended: Zir.Inst.Extended.InstData, 25806 zir_tag: Zir.Inst.Extended, 25807 ) CompileError!Air.Inst.Ref { 25808 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25809 const dimension_src = block.builtinCallArgSrc(extra.node, 0); 25810 const builtin_src = block.nodeOffset(extra.node); 25811 const target = sema.pt.zcu.getTarget(); 25812 25813 switch (target.cpu.arch) { 25814 // TODO: Allow for other GPU targets. 25815 .amdgcn, .spirv64, .spirv32, .nvptx, .nvptx64 => {}, 25816 else => { 25817 return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)}); 25818 }, 25819 } 25820 25821 const dimension: u32 = @intCast(try sema.resolveInt(block, dimension_src, extra.operand, .u32, .{ .simple = .work_group_dim_index })); 25822 try sema.requireRuntimeBlock(block, builtin_src, null); 25823 25824 return block.addInst(.{ 25825 .tag = switch (zir_tag) { 25826 .work_item_id => .work_item_id, 25827 .work_group_size => .work_group_size, 25828 .work_group_id => .work_group_id, 25829 else => unreachable, 25830 }, 25831 .data = .{ .pl_op = .{ 25832 .operand = .none, 25833 .payload = dimension, 25834 } }, 25835 }); 25836 } 25837 25838 fn zirInComptime( 25839 sema: *Sema, 25840 block: *Block, 25841 ) CompileError!Air.Inst.Ref { 25842 _ = sema; 25843 return if (block.isComptime()) .bool_true else .bool_false; 25844 } 25845 25846 fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 25847 const pt = sema.pt; 25848 const zcu = pt.zcu; 25849 const gpa = zcu.gpa; 25850 const ip = &zcu.intern_pool; 25851 25852 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 25853 const src = block.nodeOffset(src_node); 25854 const value: Zir.Inst.BuiltinValue = @enumFromInt(extended.small); 25855 25856 const ty = switch (value) { 25857 // zig fmt: off 25858 .atomic_order => try sema.getBuiltinType(src, .AtomicOrder), 25859 .atomic_rmw_op => try sema.getBuiltinType(src, .AtomicRmwOp), 25860 .calling_convention => try sema.getBuiltinType(src, .CallingConvention), 25861 .address_space => try sema.getBuiltinType(src, .AddressSpace), 25862 .float_mode => try sema.getBuiltinType(src, .FloatMode), 25863 .reduce_op => try sema.getBuiltinType(src, .ReduceOp), 25864 .call_modifier => try sema.getBuiltinType(src, .CallModifier), 25865 .prefetch_options => try sema.getBuiltinType(src, .PrefetchOptions), 25866 .export_options => try sema.getBuiltinType(src, .ExportOptions), 25867 .extern_options => try sema.getBuiltinType(src, .ExternOptions), 25868 .type_info => try sema.getBuiltinType(src, .Type), 25869 .branch_hint => try sema.getBuiltinType(src, .BranchHint), 25870 .clobbers => try sema.getBuiltinType(src, .@"assembly.Clobbers"), 25871 // zig fmt: on 25872 25873 // Values are handled here. 25874 .calling_convention_c => { 25875 const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); 25876 return try sema.namespaceLookupVal( 25877 block, 25878 src, 25879 callconv_ty.getNamespaceIndex(zcu), 25880 try ip.getOrPutString(gpa, pt.tid, "c", .no_embedded_nulls), 25881 ) orelse @panic("std.builtin is corrupt"); 25882 }, 25883 .calling_convention_inline => { 25884 comptime assert(@typeInfo(std.builtin.CallingConvention.Tag).@"enum".tag_type == u8); 25885 const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); 25886 const callconv_tag_ty = callconv_ty.unionTagType(zcu) orelse @panic("std.builtin is corrupt"); 25887 const inline_tag_val = try pt.enumValue( 25888 callconv_tag_ty, 25889 (try pt.intValue( 25890 .u8, 25891 @intFromEnum(std.builtin.CallingConvention.@"inline"), 25892 )).toIntern(), 25893 ); 25894 return sema.coerce(block, callconv_ty, Air.internedToRef(inline_tag_val.toIntern()), src); 25895 }, 25896 }; 25897 return Air.internedToRef(ty.toIntern()); 25898 } 25899 25900 fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 25901 const pt = sema.pt; 25902 const zcu = pt.zcu; 25903 25904 const lhs = try sema.resolveInst(@enumFromInt(extended.operand)); 25905 const lhs_ty = sema.typeOf(lhs); 25906 25907 const op: Zir.Inst.InplaceOp = @enumFromInt(extended.small); 25908 const ty: Type = switch (op) { 25909 .add_eq => ty: { 25910 const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty; 25911 switch (ptr_size) { 25912 .one, .slice => break :ty lhs_ty, // invalid, let it error 25913 .many, .c => break :ty .usize, // `[*]T + usize` 25914 } 25915 }, 25916 .sub_eq => ty: { 25917 const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty; 25918 switch (ptr_size) { 25919 .one, .slice => break :ty lhs_ty, // invalid, let it error 25920 .many, .c => break :ty .generic_poison, // could be `[*]T - [*]T` or `[*]T - usize` 25921 } 25922 }, 25923 }; 25924 return Air.internedToRef(ty.toIntern()); 25925 } 25926 25927 fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 25928 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25929 const uncoerced_hint = try sema.resolveInst(extra.operand); 25930 const operand_src = block.builtinCallArgSrc(extra.node, 0); 25931 25932 const hint_ty = try sema.getBuiltinType(operand_src, .BranchHint); 25933 const coerced_hint = try sema.coerce(block, hint_ty, uncoerced_hint, operand_src); 25934 const hint_val = try sema.resolveConstDefinedValue(block, operand_src, coerced_hint, .{ .simple = .operand_branchHint }); 25935 25936 // We only apply the first hint in a branch. 25937 // This allows user-provided hints to override implicit cold hints. 25938 if (sema.branch_hint == null) { 25939 sema.branch_hint = try sema.interpretBuiltinType(block, operand_src, hint_val, std.builtin.BranchHint); 25940 } 25941 } 25942 25943 fn zirFloatOpResultType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 25944 const pt = sema.pt; 25945 const zcu = pt.zcu; 25946 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25947 const operand_src = block.builtinCallArgSrc(extra.node, 0); 25948 25949 const raw_ty = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse return .generic_poison_type; 25950 const float_ty = raw_ty.optEuBaseType(zcu); 25951 25952 switch (float_ty.scalarType(zcu).zigTypeTag(zcu)) { 25953 .float, .comptime_float => {}, 25954 else => return sema.fail( 25955 block, 25956 operand_src, 25957 "expected vector of floats or float type, found '{f}'", 25958 .{float_ty.fmt(sema.pt)}, 25959 ), 25960 } 25961 25962 return .fromType(float_ty); 25963 } 25964 25965 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { 25966 if (block.isComptime()) { 25967 const msg, const fail_block = msg: { 25968 const msg = try sema.errMsg(src, "unable to evaluate comptime expression", .{}); 25969 errdefer msg.destroy(sema.gpa); 25970 25971 if (runtime_src) |some| { 25972 try sema.errNote(some, msg, "operation is runtime due to this operand", .{}); 25973 } 25974 25975 const fail_block = try block.explainWhyBlockIsComptime(msg); 25976 25977 break :msg .{ msg, fail_block }; 25978 }; 25979 return sema.failWithOwnedErrorMsg(fail_block, msg); 25980 } 25981 } 25982 25983 /// Emit a compile error if type cannot be used for a runtime variable. 25984 pub fn validateVarType( 25985 sema: *Sema, 25986 block: *Block, 25987 src: LazySrcLoc, 25988 var_ty: Type, 25989 is_extern: bool, 25990 ) CompileError!void { 25991 const pt = sema.pt; 25992 const zcu = pt.zcu; 25993 if (is_extern) { 25994 if (!try sema.validateExternType(var_ty, .other)) { 25995 const msg = msg: { 25996 const msg = try sema.errMsg(src, "extern variable cannot have type '{f}'", .{var_ty.fmt(pt)}); 25997 errdefer msg.destroy(sema.gpa); 25998 try sema.explainWhyTypeIsNotExtern(msg, src, var_ty, .other); 25999 break :msg msg; 26000 }; 26001 return sema.failWithOwnedErrorMsg(block, msg); 26002 } 26003 } else { 26004 if (var_ty.zigTypeTag(zcu) == .@"opaque") { 26005 return sema.fail( 26006 block, 26007 src, 26008 "non-extern variable with opaque type '{f}'", 26009 .{var_ty.fmt(pt)}, 26010 ); 26011 } 26012 } 26013 26014 if (!try var_ty.comptimeOnlySema(pt)) return; 26015 26016 const msg = msg: { 26017 const msg = try sema.errMsg(src, "variable of type '{f}' must be const or comptime", .{var_ty.fmt(pt)}); 26018 errdefer msg.destroy(sema.gpa); 26019 26020 try sema.explainWhyTypeIsComptime(msg, src, var_ty); 26021 if (var_ty.zigTypeTag(zcu) == .comptime_int or var_ty.zigTypeTag(zcu) == .comptime_float) { 26022 try sema.errNote(src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{}); 26023 } 26024 26025 break :msg msg; 26026 }; 26027 return sema.failWithOwnedErrorMsg(block, msg); 26028 } 26029 26030 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void); 26031 26032 fn explainWhyTypeIsComptime( 26033 sema: *Sema, 26034 msg: *Zcu.ErrorMsg, 26035 src_loc: LazySrcLoc, 26036 ty: Type, 26037 ) CompileError!void { 26038 var type_set = TypeSet{}; 26039 defer type_set.deinit(sema.gpa); 26040 26041 try ty.resolveFully(sema.pt); 26042 return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set); 26043 } 26044 26045 fn explainWhyTypeIsComptimeInner( 26046 sema: *Sema, 26047 msg: *Zcu.ErrorMsg, 26048 src_loc: LazySrcLoc, 26049 ty: Type, 26050 type_set: *TypeSet, 26051 ) CompileError!void { 26052 const pt = sema.pt; 26053 const zcu = pt.zcu; 26054 const ip = &zcu.intern_pool; 26055 switch (ty.zigTypeTag(zcu)) { 26056 .bool, 26057 .int, 26058 .float, 26059 .error_set, 26060 .@"enum", 26061 .frame, 26062 .@"anyframe", 26063 .void, 26064 => return, 26065 26066 .@"fn" => { 26067 try sema.errNote(src_loc, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)}); 26068 }, 26069 26070 .type => { 26071 try sema.errNote(src_loc, msg, "types are not available at runtime", .{}); 26072 }, 26073 26074 .comptime_float, 26075 .comptime_int, 26076 .enum_literal, 26077 .noreturn, 26078 .undefined, 26079 .null, 26080 => return, 26081 26082 .@"opaque" => { 26083 try sema.errNote(src_loc, msg, "opaque type '{f}' has undefined size", .{ty.fmt(pt)}); 26084 }, 26085 26086 .array, .vector => { 26087 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set); 26088 }, 26089 .pointer => { 26090 const elem_ty = ty.elemType2(zcu); 26091 if (elem_ty.zigTypeTag(zcu) == .@"fn") { 26092 const fn_info = zcu.typeToFunc(elem_ty).?; 26093 if (fn_info.is_generic) { 26094 try sema.errNote(src_loc, msg, "function is generic", .{}); 26095 } 26096 switch (fn_info.cc) { 26097 .@"inline" => try sema.errNote(src_loc, msg, "function has inline calling convention", .{}), 26098 else => {}, 26099 } 26100 if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) { 26101 try sema.errNote(src_loc, msg, "function has a comptime-only return type", .{}); 26102 } 26103 return; 26104 } 26105 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set); 26106 }, 26107 26108 .optional => { 26109 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(zcu), type_set); 26110 }, 26111 .error_union => { 26112 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(zcu), type_set); 26113 }, 26114 26115 .@"struct" => { 26116 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 26117 26118 if (zcu.typeToStruct(ty)) |struct_type| { 26119 for (0..struct_type.field_types.len) |i| { 26120 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 26121 const field_src: LazySrcLoc = .{ 26122 .base_node_inst = struct_type.zir_index, 26123 .offset = .{ .container_field_type = @intCast(i) }, 26124 }; 26125 26126 if (try field_ty.comptimeOnlySema(pt)) { 26127 try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{}); 26128 try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set); 26129 } 26130 } 26131 } 26132 // TODO tuples 26133 }, 26134 26135 .@"union" => { 26136 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 26137 26138 if (zcu.typeToUnion(ty)) |union_obj| { 26139 for (0..union_obj.field_types.len) |i| { 26140 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]); 26141 const field_src: LazySrcLoc = .{ 26142 .base_node_inst = union_obj.zir_index, 26143 .offset = .{ .container_field_type = @intCast(i) }, 26144 }; 26145 26146 if (try field_ty.comptimeOnlySema(pt)) { 26147 try sema.errNote(field_src, msg, "union requires comptime because of this field", .{}); 26148 try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set); 26149 } 26150 } 26151 } 26152 }, 26153 } 26154 } 26155 26156 const ExternPosition = enum { 26157 ret_ty, 26158 param_ty, 26159 union_field, 26160 struct_field, 26161 element, 26162 other, 26163 }; 26164 26165 /// Returns true if `ty` is allowed in extern types. 26166 /// Does *NOT* require `ty` to be resolved in any way. 26167 /// Calls `resolveLayout` for packed containers. 26168 fn validateExternType( 26169 sema: *Sema, 26170 ty: Type, 26171 position: ExternPosition, 26172 ) !bool { 26173 const pt = sema.pt; 26174 const zcu = pt.zcu; 26175 switch (ty.zigTypeTag(zcu)) { 26176 .type, 26177 .comptime_float, 26178 .comptime_int, 26179 .enum_literal, 26180 .undefined, 26181 .null, 26182 .error_union, 26183 .error_set, 26184 .frame, 26185 => return false, 26186 .void => return position == .union_field or position == .ret_ty or position == .struct_field or position == .element, 26187 .noreturn => return position == .ret_ty, 26188 .@"opaque", 26189 .bool, 26190 .float, 26191 .@"anyframe", 26192 => return true, 26193 .pointer => { 26194 if (ty.childType(zcu).zigTypeTag(zcu) == .@"fn") { 26195 return ty.isConstPtr(zcu) and try sema.validateExternType(ty.childType(zcu), .other); 26196 } 26197 return !(ty.isSlice(zcu) or try ty.comptimeOnlySema(pt)); 26198 }, 26199 .int => switch (ty.intInfo(zcu).bits) { 26200 0, 8, 16, 32, 64, 128 => return true, 26201 else => return false, 26202 }, 26203 .@"fn" => { 26204 if (position != .other) return false; 26205 // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI. 26206 // The goal is to experiment with more integrated CPU/GPU code. 26207 if (ty.fnCallingConvention(zcu) == .nvptx_kernel) { 26208 return true; 26209 } 26210 return !target_util.fnCallConvAllowsZigTypes(ty.fnCallingConvention(zcu)); 26211 }, 26212 .@"enum" => { 26213 return sema.validateExternType(ty.intTagType(zcu), position); 26214 }, 26215 .@"struct", .@"union" => switch (ty.containerLayout(zcu)) { 26216 .@"extern" => return true, 26217 .@"packed" => { 26218 const bit_size = try ty.bitSizeSema(pt); 26219 switch (bit_size) { 26220 0, 8, 16, 32, 64, 128 => return true, 26221 else => return false, 26222 } 26223 }, 26224 .auto => return !(try ty.hasRuntimeBitsSema(pt)), 26225 }, 26226 .array => { 26227 if (position == .ret_ty or position == .param_ty) return false; 26228 return sema.validateExternType(ty.elemType2(zcu), .element); 26229 }, 26230 .vector => return sema.validateExternType(ty.elemType2(zcu), .element), 26231 .optional => return ty.isPtrLikeOptional(zcu), 26232 } 26233 } 26234 26235 fn explainWhyTypeIsNotExtern( 26236 sema: *Sema, 26237 msg: *Zcu.ErrorMsg, 26238 src_loc: LazySrcLoc, 26239 ty: Type, 26240 position: ExternPosition, 26241 ) CompileError!void { 26242 const pt = sema.pt; 26243 const zcu = pt.zcu; 26244 switch (ty.zigTypeTag(zcu)) { 26245 .@"opaque", 26246 .bool, 26247 .float, 26248 .@"anyframe", 26249 => return, 26250 26251 .type, 26252 .comptime_float, 26253 .comptime_int, 26254 .enum_literal, 26255 .undefined, 26256 .null, 26257 .error_union, 26258 .error_set, 26259 .frame, 26260 => return, 26261 26262 .pointer => { 26263 if (ty.isSlice(zcu)) { 26264 try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); 26265 } else { 26266 const pointee_ty = ty.childType(zcu); 26267 if (!ty.isConstPtr(zcu) and pointee_ty.zigTypeTag(zcu) == .@"fn") { 26268 try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{}); 26269 } else if (try ty.comptimeOnlySema(pt)) { 26270 try sema.errNote(src_loc, msg, "pointer to comptime-only type '{f}'", .{pointee_ty.fmt(pt)}); 26271 try sema.explainWhyTypeIsComptime(msg, src_loc, ty); 26272 } 26273 try sema.explainWhyTypeIsNotExtern(msg, src_loc, pointee_ty, .other); 26274 } 26275 }, 26276 .void => try sema.errNote(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}), 26277 .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), 26278 .int => if (!std.math.isPowerOfTwo(ty.intInfo(zcu).bits)) { 26279 try sema.errNote(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{}); 26280 } else { 26281 try sema.errNote(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{}); 26282 }, 26283 .@"fn" => { 26284 if (position != .other) { 26285 try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 26286 try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 26287 return; 26288 } 26289 switch (ty.fnCallingConvention(zcu)) { 26290 .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}), 26291 .async => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}), 26292 .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}), 26293 else => return, 26294 } 26295 }, 26296 .@"enum" => { 26297 const tag_ty = ty.intTagType(zcu); 26298 try sema.errNote(src_loc, msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)}); 26299 try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position); 26300 }, 26301 .@"struct" => try sema.errNote(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}), 26302 .@"union" => try sema.errNote(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}), 26303 .array => { 26304 if (position == .ret_ty) { 26305 return sema.errNote(src_loc, msg, "arrays are not allowed as a return type", .{}); 26306 } else if (position == .param_ty) { 26307 return sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{}); 26308 } 26309 try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element); 26310 }, 26311 .vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element), 26312 .optional => try sema.errNote(src_loc, msg, "only pointer like optionals are extern compatible", .{}), 26313 } 26314 } 26315 26316 /// Returns true if `ty` is allowed in packed types. 26317 /// Does not require `ty` to be resolved in any way, but may resolve whether it is comptime-only. 26318 fn validatePackedType(sema: *Sema, ty: Type) !bool { 26319 const pt = sema.pt; 26320 const zcu = pt.zcu; 26321 return switch (ty.zigTypeTag(zcu)) { 26322 .type, 26323 .comptime_float, 26324 .comptime_int, 26325 .enum_literal, 26326 .undefined, 26327 .null, 26328 .error_union, 26329 .error_set, 26330 .frame, 26331 .noreturn, 26332 .@"opaque", 26333 .@"anyframe", 26334 .@"fn", 26335 .array, 26336 => false, 26337 .optional => return ty.isPtrLikeOptional(zcu), 26338 .void, 26339 .bool, 26340 .float, 26341 .int, 26342 .vector, 26343 => true, 26344 .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).tag_mode) { 26345 .auto => false, 26346 .explicit, .nonexhaustive => true, 26347 }, 26348 .pointer => !ty.isSlice(zcu) and !try ty.comptimeOnlySema(pt), 26349 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed", 26350 }; 26351 } 26352 26353 fn explainWhyTypeIsNotPacked( 26354 sema: *Sema, 26355 msg: *Zcu.ErrorMsg, 26356 src_loc: LazySrcLoc, 26357 ty: Type, 26358 ) CompileError!void { 26359 const pt = sema.pt; 26360 const zcu = pt.zcu; 26361 switch (ty.zigTypeTag(zcu)) { 26362 .void, 26363 .bool, 26364 .float, 26365 .int, 26366 .vector, 26367 .@"enum", 26368 => return, 26369 .type, 26370 .comptime_float, 26371 .comptime_int, 26372 .enum_literal, 26373 .undefined, 26374 .null, 26375 .frame, 26376 .noreturn, 26377 .@"opaque", 26378 .error_union, 26379 .error_set, 26380 .@"anyframe", 26381 .optional, 26382 .array, 26383 => try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}), 26384 .pointer => if (ty.isSlice(zcu)) { 26385 try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); 26386 } else { 26387 try sema.errNote(src_loc, msg, "comptime-only pointer has no guaranteed in-memory representation", .{}); 26388 try sema.explainWhyTypeIsComptime(msg, src_loc, ty); 26389 }, 26390 .@"fn" => { 26391 try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 26392 try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 26393 }, 26394 .@"struct" => try sema.errNote(src_loc, msg, "only packed structs layout are allowed in packed types", .{}), 26395 .@"union" => try sema.errNote(src_loc, msg, "only packed unions layout are allowed in packed types", .{}), 26396 } 26397 } 26398 26399 /// Backends depend on panic decls being available when lowering safety-checked 26400 /// instructions. This function ensures the panic function will be available to 26401 /// be called during that time. 26402 fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void { 26403 const zcu = sema.pt.zcu; 26404 26405 // If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers. 26406 // The backend will transform panics into traps instead. 26407 if (!zcu.backendSupportsFeature(.panic_fn)) return; 26408 26409 const fn_index = try sema.getPanicIdFunc(src, panic_id); 26410 const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(fn_index); 26411 try sema.addReferenceEntry(null, src, .wrap(.{ .func = orig_fn_index })); 26412 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); 26413 } 26414 26415 fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index { 26416 const zcu = sema.pt.zcu; 26417 try sema.ensureMemoizedStateResolved(src, .panic); 26418 const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin()); 26419 switch (sema.owner.unwrap()) { 26420 .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, 26421 .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true), 26422 } 26423 return panic_fn_index; 26424 } 26425 26426 fn addSafetyCheck( 26427 sema: *Sema, 26428 parent_block: *Block, 26429 src: LazySrcLoc, 26430 ok: Air.Inst.Ref, 26431 panic_id: Zcu.SimplePanicId, 26432 ) !void { 26433 const gpa = sema.gpa; 26434 assert(!parent_block.isComptime()); 26435 26436 var fail_block: Block = .{ 26437 .parent = parent_block, 26438 .sema = sema, 26439 .namespace = parent_block.namespace, 26440 .instructions = .{}, 26441 .inlining = parent_block.inlining, 26442 .comptime_reason = null, 26443 .src_base_inst = parent_block.src_base_inst, 26444 .type_name_ctx = parent_block.type_name_ctx, 26445 }; 26446 26447 defer fail_block.instructions.deinit(gpa); 26448 26449 try sema.safetyPanic(&fail_block, src, panic_id); 26450 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 26451 } 26452 26453 fn addSafetyCheckExtra( 26454 sema: *Sema, 26455 parent_block: *Block, 26456 ok: Air.Inst.Ref, 26457 fail_block: *Block, 26458 ) !void { 26459 const gpa = sema.gpa; 26460 26461 try parent_block.instructions.ensureUnusedCapacity(gpa, 1); 26462 26463 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 26464 1 + // The main block only needs space for the cond_br. 26465 @typeInfo(Air.CondBr).@"struct".fields.len + 26466 1 + // The ok branch of the cond_br only needs space for the br. 26467 fail_block.instructions.items.len); 26468 26469 try sema.air_instructions.ensureUnusedCapacity(gpa, 3); 26470 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 26471 const cond_br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1); 26472 const br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(cond_br_inst) + 1); 26473 sema.air_instructions.appendAssumeCapacity(.{ 26474 .tag = .block, 26475 .data = .{ .ty_pl = .{ 26476 .ty = .void_type, 26477 .payload = sema.addExtraAssumeCapacity(Air.Block{ 26478 .body_len = 1, 26479 }), 26480 } }, 26481 }); 26482 sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst)); 26483 26484 sema.air_instructions.appendAssumeCapacity(.{ 26485 .tag = .cond_br, 26486 .data = .{ 26487 .pl_op = .{ 26488 .operand = ok, 26489 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 26490 .then_body_len = 1, 26491 .else_body_len = @intCast(fail_block.instructions.items.len), 26492 .branch_hints = .{ 26493 // Safety check failure branch is cold. 26494 .true = .likely, 26495 .false = .cold, 26496 // Code coverage not wanted for panic branches. 26497 .then_cov = .none, 26498 .else_cov = .none, 26499 }, 26500 }), 26501 }, 26502 }, 26503 }); 26504 sema.air_extra.appendAssumeCapacity(@intFromEnum(br_inst)); 26505 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items)); 26506 26507 sema.air_instructions.appendAssumeCapacity(.{ 26508 .tag = .br, 26509 .data = .{ .br = .{ 26510 .block_inst = block_inst, 26511 .operand = .void_value, 26512 } }, 26513 }); 26514 26515 parent_block.instructions.appendAssumeCapacity(block_inst); 26516 } 26517 26518 fn addSafetyCheckUnwrapError( 26519 sema: *Sema, 26520 parent_block: *Block, 26521 src: LazySrcLoc, 26522 operand: Air.Inst.Ref, 26523 unwrap_err_tag: Air.Inst.Tag, 26524 is_non_err_tag: Air.Inst.Tag, 26525 ) !void { 26526 assert(!parent_block.isComptime()); 26527 const ok = try parent_block.addUnOp(is_non_err_tag, operand); 26528 const gpa = sema.gpa; 26529 26530 var fail_block: Block = .{ 26531 .parent = parent_block, 26532 .sema = sema, 26533 .namespace = parent_block.namespace, 26534 .instructions = .{}, 26535 .inlining = parent_block.inlining, 26536 .comptime_reason = null, 26537 .src_base_inst = parent_block.src_base_inst, 26538 .type_name_ctx = parent_block.type_name_ctx, 26539 }; 26540 26541 defer fail_block.instructions.deinit(gpa); 26542 26543 const err = try fail_block.addTyOp(unwrap_err_tag, .anyerror, operand); 26544 try safetyPanicUnwrapError(sema, &fail_block, src, err); 26545 26546 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 26547 } 26548 26549 fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.Inst.Ref) !void { 26550 const pt = sema.pt; 26551 const zcu = pt.zcu; 26552 if (!zcu.backendSupportsFeature(.panic_fn)) { 26553 _ = try block.addNoOp(.trap); 26554 } else { 26555 const panic_fn = try getBuiltin(sema, src, .@"panic.unwrapError"); 26556 try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{err}, .@"safety check"); 26557 } 26558 } 26559 26560 fn addSafetyCheckIndexOob( 26561 sema: *Sema, 26562 parent_block: *Block, 26563 src: LazySrcLoc, 26564 index: Air.Inst.Ref, 26565 len: Air.Inst.Ref, 26566 cmp_op: Air.Inst.Tag, 26567 ) !void { 26568 assert(!parent_block.isComptime()); 26569 const ok = try parent_block.addBinOp(cmp_op, index, len); 26570 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.outOfBounds", &.{ index, len }); 26571 } 26572 26573 fn addSafetyCheckInactiveUnionField( 26574 sema: *Sema, 26575 parent_block: *Block, 26576 src: LazySrcLoc, 26577 active_tag: Air.Inst.Ref, 26578 wanted_tag: Air.Inst.Ref, 26579 ) !void { 26580 assert(!parent_block.isComptime()); 26581 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); 26582 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.inactiveUnionField", &.{ active_tag, wanted_tag }); 26583 } 26584 26585 fn addSafetyCheckSentinelMismatch( 26586 sema: *Sema, 26587 parent_block: *Block, 26588 src: LazySrcLoc, 26589 maybe_sentinel: ?Value, 26590 sentinel_ty: Type, 26591 ptr: Air.Inst.Ref, 26592 sentinel_index: Air.Inst.Ref, 26593 ) !void { 26594 assert(!parent_block.isComptime()); 26595 const pt = sema.pt; 26596 const zcu = pt.zcu; 26597 const expected_sentinel_val = maybe_sentinel orelse return; 26598 const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern()); 26599 26600 const ptr_ty = sema.typeOf(ptr); 26601 const actual_sentinel = if (ptr_ty.isSlice(zcu)) 26602 try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) 26603 else blk: { 26604 const elem_ptr_ty = try ptr_ty.elemPtrType(null, pt); 26605 const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty); 26606 break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); 26607 }; 26608 26609 const ok = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: { 26610 const eql = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); 26611 break :ok try parent_block.addReduce(eql, .And); 26612 } else ok: { 26613 assert(sentinel_ty.isSelfComparable(zcu, true)); 26614 break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel); 26615 }; 26616 26617 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{ 26618 expected_sentinel, actual_sentinel, 26619 }); 26620 } 26621 26622 fn addSafetyCheckCall( 26623 sema: *Sema, 26624 parent_block: *Block, 26625 src: LazySrcLoc, 26626 ok: Air.Inst.Ref, 26627 comptime func_decl: Zcu.BuiltinDecl, 26628 args: []const Air.Inst.Ref, 26629 ) !void { 26630 assert(!parent_block.isComptime()); 26631 const gpa = sema.gpa; 26632 const pt = sema.pt; 26633 const zcu = pt.zcu; 26634 26635 var fail_block: Block = .{ 26636 .parent = parent_block, 26637 .sema = sema, 26638 .namespace = parent_block.namespace, 26639 .instructions = .{}, 26640 .inlining = parent_block.inlining, 26641 .comptime_reason = null, 26642 .src_base_inst = parent_block.src_base_inst, 26643 .type_name_ctx = parent_block.type_name_ctx, 26644 }; 26645 26646 defer fail_block.instructions.deinit(gpa); 26647 26648 if (!zcu.backendSupportsFeature(.panic_fn)) { 26649 _ = try fail_block.addNoOp(.trap); 26650 } else { 26651 const panic_fn = try getBuiltin(sema, src, func_decl); 26652 try sema.callBuiltin(&fail_block, src, Air.internedToRef(panic_fn), .auto, args, .@"safety check"); 26653 } 26654 26655 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 26656 } 26657 26658 /// This does not set `sema.branch_hint`. 26659 fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) CompileError!void { 26660 if (!sema.pt.zcu.backendSupportsFeature(.panic_fn)) { 26661 _ = try block.addNoOp(.trap); 26662 } else { 26663 const panic_fn = try sema.getPanicIdFunc(src, panic_id); 26664 try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{}, .@"safety check"); 26665 } 26666 } 26667 26668 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { 26669 sema.branch_count += 1; 26670 if (sema.branch_count > sema.branch_quota) { 26671 const msg = try sema.errMsg( 26672 src, 26673 "evaluation exceeded {d} backwards branches", 26674 .{sema.branch_quota}, 26675 ); 26676 try sema.errNote( 26677 src, 26678 msg, 26679 "use @setEvalBranchQuota() to raise the branch limit from {d}", 26680 .{sema.branch_quota}, 26681 ); 26682 return sema.failWithOwnedErrorMsg(block, msg); 26683 } 26684 } 26685 26686 fn fieldPtrLoad( 26687 sema: *Sema, 26688 block: *Block, 26689 src: LazySrcLoc, 26690 object_ptr: Air.Inst.Ref, 26691 field_name: InternPool.NullTerminatedString, 26692 field_name_src: LazySrcLoc, 26693 ) CompileError!Air.Inst.Ref { 26694 const pt = sema.pt; 26695 const zcu = pt.zcu; 26696 const object_ptr_ty = sema.typeOf(object_ptr); 26697 const pointee_ty = object_ptr_ty.childType(zcu); 26698 if (try typeHasOnePossibleValue(sema, pointee_ty)) |opv| { 26699 const object: Air.Inst.Ref = .fromValue(opv); 26700 return fieldVal(sema, block, src, object, field_name, field_name_src); 26701 } 26702 26703 if (try sema.resolveDefinedValue(block, src, object_ptr)) |object_ptr_val| { 26704 if (try sema.pointerDeref(block, src, object_ptr_val, object_ptr_ty)) |object_val| { 26705 const object: Air.Inst.Ref = .fromValue(object_val); 26706 return fieldVal(sema, block, src, object, field_name, field_name_src); 26707 } 26708 } 26709 const field_ptr = try sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); 26710 return analyzeLoad(sema, block, src, field_ptr, field_name_src); 26711 } 26712 26713 fn fieldVal( 26714 sema: *Sema, 26715 block: *Block, 26716 src: LazySrcLoc, 26717 object: Air.Inst.Ref, 26718 field_name: InternPool.NullTerminatedString, 26719 field_name_src: LazySrcLoc, 26720 ) CompileError!Air.Inst.Ref { 26721 // When editing this function, note that there is corresponding logic to be edited 26722 // in `fieldPtr`. This function takes a value and returns a value. 26723 26724 const pt = sema.pt; 26725 const zcu = pt.zcu; 26726 const ip = &zcu.intern_pool; 26727 const object_src = src; // TODO better source location 26728 const object_ty = sema.typeOf(object); 26729 26730 // Zig allows dereferencing a single pointer during field lookup. Note that 26731 // we don't actually need to generate the dereference some field lookups, like the 26732 // length of arrays and other comptime operations. 26733 const is_pointer_to = object_ty.isSinglePointer(zcu); 26734 26735 const inner_ty = if (is_pointer_to) 26736 object_ty.childType(zcu) 26737 else 26738 object_ty; 26739 26740 switch (inner_ty.zigTypeTag(zcu)) { 26741 .array => { 26742 if (field_name.eqlSlice("len", ip)) { 26743 return Air.internedToRef((try pt.intValue(.usize, inner_ty.arrayLen(zcu))).toIntern()); 26744 } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { 26745 const ptr_info = object_ty.ptrInfo(zcu); 26746 const result_ty = try pt.ptrTypeSema(.{ 26747 .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(), 26748 .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none, 26749 .flags = .{ 26750 .size = .many, 26751 .alignment = ptr_info.flags.alignment, 26752 .is_const = ptr_info.flags.is_const, 26753 .is_volatile = ptr_info.flags.is_volatile, 26754 .is_allowzero = ptr_info.flags.is_allowzero, 26755 .address_space = ptr_info.flags.address_space, 26756 .vector_index = ptr_info.flags.vector_index, 26757 }, 26758 .packed_offset = ptr_info.packed_offset, 26759 }); 26760 return sema.coerce(block, result_ty, object, src); 26761 } else { 26762 return sema.fail( 26763 block, 26764 field_name_src, 26765 "no member named '{f}' in '{f}'", 26766 .{ field_name.fmt(ip), object_ty.fmt(pt) }, 26767 ); 26768 } 26769 }, 26770 .pointer => { 26771 const ptr_info = inner_ty.ptrInfo(zcu); 26772 if (ptr_info.flags.size == .slice) { 26773 if (field_name.eqlSlice("ptr", ip)) { 26774 const slice = if (is_pointer_to) 26775 try sema.analyzeLoad(block, src, object, object_src) 26776 else 26777 object; 26778 return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); 26779 } else if (field_name.eqlSlice("len", ip)) { 26780 const slice = if (is_pointer_to) 26781 try sema.analyzeLoad(block, src, object, object_src) 26782 else 26783 object; 26784 return sema.analyzeSliceLen(block, src, slice); 26785 } else { 26786 return sema.fail( 26787 block, 26788 field_name_src, 26789 "no member named '{f}' in '{f}'", 26790 .{ field_name.fmt(ip), object_ty.fmt(pt) }, 26791 ); 26792 } 26793 } 26794 }, 26795 .type => { 26796 const dereffed_type = if (is_pointer_to) 26797 try sema.analyzeLoad(block, src, object, object_src) 26798 else 26799 object; 26800 26801 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?; 26802 const child_type = val.toType(); 26803 26804 switch (child_type.zigTypeTag(zcu)) { 26805 .error_set => { 26806 switch (ip.indexToKey(child_type.toIntern())) { 26807 .error_set_type => |error_set_type| blk: { 26808 if (error_set_type.nameIndex(ip, field_name) != null) break :blk; 26809 return sema.fail(block, src, "no error named '{f}' in '{f}'", .{ 26810 field_name.fmt(ip), child_type.fmt(pt), 26811 }); 26812 }, 26813 .inferred_error_set_type => { 26814 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 26815 }, 26816 .simple_type => |t| { 26817 assert(t == .anyerror); 26818 _ = try pt.getErrorValue(field_name); 26819 }, 26820 else => unreachable, 26821 } 26822 26823 const error_set_type = if (!child_type.isAnyError(zcu)) 26824 child_type 26825 else 26826 try pt.singleErrorSetType(field_name); 26827 return Air.internedToRef((try pt.intern(.{ .err = .{ 26828 .ty = error_set_type.toIntern(), 26829 .name = field_name, 26830 } }))); 26831 }, 26832 .@"union" => { 26833 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26834 return inst; 26835 } 26836 try child_type.resolveFields(pt); 26837 if (child_type.unionTagType(zcu)) |enum_ty| { 26838 if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| { 26839 const field_index: u32 = @intCast(field_index_usize); 26840 return Air.internedToRef((try pt.enumValueFieldIndex(enum_ty, field_index)).toIntern()); 26841 } 26842 } 26843 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26844 }, 26845 .@"enum" => { 26846 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26847 return inst; 26848 } 26849 const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse 26850 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26851 const field_index: u32 = @intCast(field_index_usize); 26852 const enum_val = try pt.enumValueFieldIndex(child_type, field_index); 26853 return Air.internedToRef(enum_val.toIntern()); 26854 }, 26855 .@"struct", .@"opaque" => { 26856 if (!child_type.isTuple(zcu) and child_type.toIntern() != .anyopaque_type) { 26857 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26858 return inst; 26859 } 26860 } 26861 return sema.failWithBadMemberAccess(block, child_type, src, field_name); 26862 }, 26863 else => return sema.failWithOwnedErrorMsg(block, msg: { 26864 const msg = try sema.errMsg(src, "type '{f}' has no members", .{child_type.fmt(pt)}); 26865 errdefer msg.destroy(sema.gpa); 26866 if (child_type.isSlice(zcu)) try sema.errNote(src, msg, "slice values have 'len' and 'ptr' members", .{}); 26867 if (child_type.zigTypeTag(zcu) == .array) try sema.errNote(src, msg, "array values have 'len' member", .{}); 26868 break :msg msg; 26869 }), 26870 } 26871 }, 26872 .@"struct" => if (is_pointer_to) { 26873 // Avoid loading the entire struct by fetching a pointer and loading that 26874 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 26875 return sema.analyzeLoad(block, src, field_ptr, object_src); 26876 } else { 26877 return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty); 26878 }, 26879 .@"union" => if (is_pointer_to) { 26880 // Avoid loading the entire union by fetching a pointer and loading that 26881 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 26882 return sema.analyzeLoad(block, src, field_ptr, object_src); 26883 } else { 26884 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty); 26885 }, 26886 else => {}, 26887 } 26888 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 26889 } 26890 26891 fn fieldPtr( 26892 sema: *Sema, 26893 block: *Block, 26894 src: LazySrcLoc, 26895 object_ptr: Air.Inst.Ref, 26896 field_name: InternPool.NullTerminatedString, 26897 field_name_src: LazySrcLoc, 26898 initializing: bool, 26899 ) CompileError!Air.Inst.Ref { 26900 // When editing this function, note that there is corresponding logic to be edited 26901 // in `fieldVal`. This function takes a pointer and returns a pointer. 26902 26903 const pt = sema.pt; 26904 const zcu = pt.zcu; 26905 const ip = &zcu.intern_pool; 26906 const object_ptr_src = src; // TODO better source location 26907 const object_ptr_ty = sema.typeOf(object_ptr); 26908 const object_ty = switch (object_ptr_ty.zigTypeTag(zcu)) { 26909 .pointer => object_ptr_ty.childType(zcu), 26910 else => return sema.fail(block, object_ptr_src, "expected pointer, found '{f}'", .{object_ptr_ty.fmt(pt)}), 26911 }; 26912 26913 // Zig allows dereferencing a single pointer during field lookup. Note that 26914 // we don't actually need to generate the dereference some field lookups, like the 26915 // length of arrays and other comptime operations. 26916 const is_pointer_to = object_ty.isSinglePointer(zcu); 26917 26918 const inner_ty = if (is_pointer_to) 26919 object_ty.childType(zcu) 26920 else 26921 object_ty; 26922 26923 switch (inner_ty.zigTypeTag(zcu)) { 26924 .array => { 26925 if (field_name.eqlSlice("len", ip)) { 26926 const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu)); 26927 return uavRef(sema, int_val.toIntern()); 26928 } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { 26929 const ptr_info = object_ty.ptrInfo(zcu); 26930 const new_ptr_ty = try pt.ptrTypeSema(.{ 26931 .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(), 26932 .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none, 26933 .flags = .{ 26934 .size = .many, 26935 .alignment = ptr_info.flags.alignment, 26936 .is_const = ptr_info.flags.is_const, 26937 .is_volatile = ptr_info.flags.is_volatile, 26938 .is_allowzero = ptr_info.flags.is_allowzero, 26939 .address_space = ptr_info.flags.address_space, 26940 .vector_index = ptr_info.flags.vector_index, 26941 }, 26942 .packed_offset = ptr_info.packed_offset, 26943 }); 26944 const ptr_ptr_info = object_ptr_ty.ptrInfo(zcu); 26945 const result_ty = try pt.ptrTypeSema(.{ 26946 .child = new_ptr_ty.toIntern(), 26947 .sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 26948 .flags = .{ 26949 .alignment = ptr_ptr_info.flags.alignment, 26950 .is_const = ptr_ptr_info.flags.is_const, 26951 .is_volatile = ptr_ptr_info.flags.is_volatile, 26952 .is_allowzero = ptr_ptr_info.flags.is_allowzero, 26953 .address_space = ptr_ptr_info.flags.address_space, 26954 .vector_index = ptr_ptr_info.flags.vector_index, 26955 }, 26956 .packed_offset = ptr_ptr_info.packed_offset, 26957 }); 26958 return sema.bitCast(block, result_ty, object_ptr, src, null); 26959 } else { 26960 return sema.fail( 26961 block, 26962 field_name_src, 26963 "no member named '{f}' in '{f}'", 26964 .{ field_name.fmt(ip), object_ty.fmt(pt) }, 26965 ); 26966 } 26967 }, 26968 .pointer => if (inner_ty.isSlice(zcu)) { 26969 const inner_ptr = if (is_pointer_to) 26970 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 26971 else 26972 object_ptr; 26973 26974 const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty; 26975 26976 if (field_name.eqlSlice("ptr", ip)) { 26977 const slice_ptr_ty = inner_ty.slicePtrFieldType(zcu); 26978 26979 const result_ty = try pt.ptrTypeSema(.{ 26980 .child = slice_ptr_ty.toIntern(), 26981 .flags = .{ 26982 .is_const = !attr_ptr_ty.ptrIsMutable(zcu), 26983 .is_volatile = attr_ptr_ty.isVolatilePtr(zcu), 26984 .address_space = attr_ptr_ty.ptrAddressSpace(zcu), 26985 }, 26986 }); 26987 26988 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 26989 return Air.internedToRef((try val.ptrField(Value.slice_ptr_index, pt)).toIntern()); 26990 } 26991 try sema.requireRuntimeBlock(block, src, null); 26992 26993 const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); 26994 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); 26995 return field_ptr; 26996 } else if (field_name.eqlSlice("len", ip)) { 26997 const result_ty = try pt.ptrTypeSema(.{ 26998 .child = .usize_type, 26999 .flags = .{ 27000 .is_const = !attr_ptr_ty.ptrIsMutable(zcu), 27001 .is_volatile = attr_ptr_ty.isVolatilePtr(zcu), 27002 .address_space = attr_ptr_ty.ptrAddressSpace(zcu), 27003 }, 27004 }); 27005 27006 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 27007 return Air.internedToRef((try val.ptrField(Value.slice_len_index, pt)).toIntern()); 27008 } 27009 try sema.requireRuntimeBlock(block, src, null); 27010 27011 const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); 27012 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); 27013 return field_ptr; 27014 } else { 27015 return sema.fail( 27016 block, 27017 field_name_src, 27018 "no member named '{f}' in '{f}'", 27019 .{ field_name.fmt(ip), object_ty.fmt(pt) }, 27020 ); 27021 } 27022 }, 27023 .type => { 27024 _ = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, object_ptr, undefined); 27025 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); 27026 const inner = if (is_pointer_to) 27027 try sema.analyzeLoad(block, src, result, object_ptr_src) 27028 else 27029 result; 27030 27031 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?; 27032 const child_type = val.toType(); 27033 27034 switch (child_type.zigTypeTag(zcu)) { 27035 .error_set => { 27036 switch (ip.indexToKey(child_type.toIntern())) { 27037 .error_set_type => |error_set_type| blk: { 27038 if (error_set_type.nameIndex(ip, field_name) != null) { 27039 break :blk; 27040 } 27041 return sema.fail(block, src, "no error named '{f}' in '{f}'", .{ 27042 field_name.fmt(ip), child_type.fmt(pt), 27043 }); 27044 }, 27045 .inferred_error_set_type => { 27046 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 27047 }, 27048 .simple_type => |t| { 27049 assert(t == .anyerror); 27050 _ = try pt.getErrorValue(field_name); 27051 }, 27052 else => unreachable, 27053 } 27054 27055 const error_set_type = if (!child_type.isAnyError(zcu)) 27056 child_type 27057 else 27058 try pt.singleErrorSetType(field_name); 27059 return uavRef(sema, try pt.intern(.{ .err = .{ 27060 .ty = error_set_type.toIntern(), 27061 .name = field_name, 27062 } })); 27063 }, 27064 .@"union" => { 27065 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 27066 return inst; 27067 } 27068 try child_type.resolveFields(pt); 27069 if (child_type.unionTagType(zcu)) |enum_ty| { 27070 if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| { 27071 const field_index_u32: u32 = @intCast(field_index); 27072 const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32); 27073 return uavRef(sema, idx_val.toIntern()); 27074 } 27075 } 27076 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 27077 }, 27078 .@"enum" => { 27079 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 27080 return inst; 27081 } 27082 const field_index = child_type.enumFieldIndex(field_name, zcu) orelse { 27083 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 27084 }; 27085 const field_index_u32: u32 = @intCast(field_index); 27086 const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32); 27087 return uavRef(sema, idx_val.toIntern()); 27088 }, 27089 .@"struct", .@"opaque" => { 27090 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 27091 return inst; 27092 } 27093 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 27094 }, 27095 else => return sema.fail(block, src, "type '{f}' has no members", .{child_type.fmt(pt)}), 27096 } 27097 }, 27098 .@"struct" => { 27099 const inner_ptr = if (is_pointer_to) 27100 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 27101 else 27102 object_ptr; 27103 const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 27104 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); 27105 return field_ptr; 27106 }, 27107 .@"union" => { 27108 const inner_ptr = if (is_pointer_to) 27109 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 27110 else 27111 object_ptr; 27112 const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 27113 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); 27114 return field_ptr; 27115 }, 27116 else => {}, 27117 } 27118 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 27119 } 27120 27121 const ResolvedFieldCallee = union(enum) { 27122 /// The LHS of the call was an actual field with this value. 27123 direct: Air.Inst.Ref, 27124 /// This is a method call, with the function and first argument given. 27125 method: struct { 27126 func_inst: Air.Inst.Ref, 27127 arg0_inst: Air.Inst.Ref, 27128 }, 27129 }; 27130 27131 fn fieldCallBind( 27132 sema: *Sema, 27133 block: *Block, 27134 src: LazySrcLoc, 27135 raw_ptr: Air.Inst.Ref, 27136 field_name: InternPool.NullTerminatedString, 27137 field_name_src: LazySrcLoc, 27138 ) CompileError!ResolvedFieldCallee { 27139 // When editing this function, note that there is corresponding logic to be edited 27140 // in `fieldVal`. This function takes a pointer and returns a pointer. 27141 27142 const pt = sema.pt; 27143 const zcu = pt.zcu; 27144 const ip = &zcu.intern_pool; 27145 const raw_ptr_src = src; // TODO better source location 27146 const raw_ptr_ty = sema.typeOf(raw_ptr); 27147 const inner_ty = if (raw_ptr_ty.zigTypeTag(zcu) == .pointer and (raw_ptr_ty.ptrSize(zcu) == .one or raw_ptr_ty.ptrSize(zcu) == .c)) 27148 raw_ptr_ty.childType(zcu) 27149 else 27150 return sema.fail(block, raw_ptr_src, "expected single pointer, found '{f}'", .{raw_ptr_ty.fmt(pt)}); 27151 27152 // Optionally dereference a second pointer to get the concrete type. 27153 const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one; 27154 const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty; 27155 const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; 27156 const object_ptr = if (is_double_ptr) 27157 try sema.analyzeLoad(block, src, raw_ptr, src) 27158 else 27159 raw_ptr; 27160 27161 find_field: { 27162 switch (concrete_ty.zigTypeTag(zcu)) { 27163 .@"struct" => { 27164 try concrete_ty.resolveFields(pt); 27165 if (zcu.typeToStruct(concrete_ty)) |struct_type| { 27166 const field_index = struct_type.nameIndex(ip, field_name) orelse 27167 break :find_field; 27168 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); 27169 27170 return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr); 27171 } else if (concrete_ty.isTuple(zcu)) { 27172 if (field_name.eqlSlice("len", ip)) { 27173 return .{ .direct = try pt.intRef(.usize, concrete_ty.structFieldCount(zcu)) }; 27174 } 27175 if (field_name.toUnsigned(ip)) |field_index| { 27176 if (field_index >= concrete_ty.structFieldCount(zcu)) break :find_field; 27177 return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.fieldType(field_index, zcu), field_index, object_ptr); 27178 } 27179 } else { 27180 const max = concrete_ty.structFieldCount(zcu); 27181 for (0..max) |i_usize| { 27182 const i: u32 = @intCast(i_usize); 27183 if (field_name == concrete_ty.structFieldName(i, zcu).unwrap().?) { 27184 return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.fieldType(i, zcu), i, object_ptr); 27185 } 27186 } 27187 } 27188 }, 27189 .@"union" => { 27190 try concrete_ty.resolveFields(pt); 27191 const union_obj = zcu.typeToUnion(concrete_ty).?; 27192 _ = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse break :find_field; 27193 const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false); 27194 return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) }; 27195 }, 27196 .type => { 27197 const namespace = try sema.analyzeLoad(block, src, object_ptr, src); 27198 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) }; 27199 }, 27200 else => {}, 27201 } 27202 } 27203 27204 // If we get here, we need to look for a decl in the struct type instead. 27205 const found_nav = found_nav: { 27206 const namespace = concrete_ty.getNamespace(zcu).unwrap() orelse 27207 break :found_nav null; 27208 const nav_index = try sema.namespaceLookup(block, src, namespace, field_name) orelse 27209 break :found_nav null; 27210 27211 const decl_val = try sema.analyzeNavVal(block, src, nav_index); 27212 const decl_type = sema.typeOf(decl_val); 27213 if (zcu.typeToFunc(decl_type)) |func_type| f: { 27214 if (func_type.param_types.len == 0) break :f; 27215 27216 const first_param_type: Type = .fromInterned(func_type.param_types.get(ip)[0]); 27217 if (first_param_type.isGenericPoison() or 27218 (first_param_type.zigTypeTag(zcu) == .pointer and 27219 (first_param_type.ptrSize(zcu) == .one or 27220 first_param_type.ptrSize(zcu) == .c) and 27221 first_param_type.childType(zcu).eql(concrete_ty, zcu))) 27222 { 27223 // Note that if the param type is generic poison, we know that it must 27224 // specifically be `anytype` since it's the first parameter, meaning we 27225 // can safely assume it can be a pointer. 27226 // TODO: bound fn calls on rvalues should probably 27227 // generate a by-value argument somehow. 27228 return .{ .method = .{ 27229 .func_inst = decl_val, 27230 .arg0_inst = object_ptr, 27231 } }; 27232 } else if (first_param_type.eql(concrete_ty, zcu)) { 27233 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 27234 return .{ .method = .{ 27235 .func_inst = decl_val, 27236 .arg0_inst = deref, 27237 } }; 27238 } else if (first_param_type.zigTypeTag(zcu) == .optional) { 27239 const child = first_param_type.optionalChild(zcu); 27240 if (child.eql(concrete_ty, zcu)) { 27241 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 27242 return .{ .method = .{ 27243 .func_inst = decl_val, 27244 .arg0_inst = deref, 27245 } }; 27246 } else if (child.zigTypeTag(zcu) == .pointer and 27247 child.ptrSize(zcu) == .one and 27248 child.childType(zcu).eql(concrete_ty, zcu)) 27249 { 27250 return .{ .method = .{ 27251 .func_inst = decl_val, 27252 .arg0_inst = object_ptr, 27253 } }; 27254 } 27255 } else if (first_param_type.zigTypeTag(zcu) == .error_union and 27256 first_param_type.errorUnionPayload(zcu).eql(concrete_ty, zcu)) 27257 { 27258 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 27259 return .{ .method = .{ 27260 .func_inst = decl_val, 27261 .arg0_inst = deref, 27262 } }; 27263 } 27264 } 27265 break :found_nav nav_index; 27266 }; 27267 27268 const msg = msg: { 27269 const msg = try sema.errMsg(src, "no field or member function named '{f}' in '{f}'", .{ 27270 field_name.fmt(ip), 27271 concrete_ty.fmt(pt), 27272 }); 27273 errdefer msg.destroy(sema.gpa); 27274 try sema.addDeclaredHereNote(msg, concrete_ty); 27275 if (found_nav) |nav_index| { 27276 try sema.errNote( 27277 zcu.navSrcLoc(nav_index), 27278 msg, 27279 "'{f}' is not a member function", 27280 .{field_name.fmt(ip)}, 27281 ); 27282 } 27283 if (concrete_ty.zigTypeTag(zcu) == .error_union) { 27284 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 27285 } 27286 if (is_double_ptr) { 27287 try sema.errNote(src, msg, "method invocation only supports up to one level of implicit pointer dereferencing", .{}); 27288 try sema.errNote(src, msg, "use '.*' to dereference pointer", .{}); 27289 } 27290 break :msg msg; 27291 }; 27292 return sema.failWithOwnedErrorMsg(block, msg); 27293 } 27294 27295 fn finishFieldCallBind( 27296 sema: *Sema, 27297 block: *Block, 27298 src: LazySrcLoc, 27299 ptr_ty: Type, 27300 field_ty: Type, 27301 field_index: u32, 27302 object_ptr: Air.Inst.Ref, 27303 ) CompileError!ResolvedFieldCallee { 27304 const pt = sema.pt; 27305 const zcu = pt.zcu; 27306 const ptr_field_ty = try pt.ptrTypeSema(.{ 27307 .child = field_ty.toIntern(), 27308 .flags = .{ 27309 .is_const = !ptr_ty.ptrIsMutable(zcu), 27310 .address_space = ptr_ty.ptrAddressSpace(zcu), 27311 }, 27312 }); 27313 27314 const container_ty = ptr_ty.childType(zcu); 27315 if (container_ty.zigTypeTag(zcu) == .@"struct") { 27316 if (container_ty.structFieldIsComptime(field_index, zcu)) { 27317 try container_ty.resolveStructFieldInits(pt); 27318 const default_val = (try container_ty.structFieldValueComptime(pt, field_index)).?; 27319 return .{ .direct = Air.internedToRef(default_val.toIntern()) }; 27320 } 27321 } 27322 27323 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| { 27324 const ptr_val = try struct_ptr_val.ptrField(field_index, pt); 27325 const pointer = Air.internedToRef(ptr_val.toIntern()); 27326 return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) }; 27327 } 27328 27329 try sema.requireRuntimeBlock(block, src, null); 27330 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); 27331 return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) }; 27332 } 27333 27334 fn namespaceLookup( 27335 sema: *Sema, 27336 block: *Block, 27337 src: LazySrcLoc, 27338 namespace: InternPool.NamespaceIndex, 27339 decl_name: InternPool.NullTerminatedString, 27340 ) CompileError!?InternPool.Nav.Index { 27341 const pt = sema.pt; 27342 const zcu = pt.zcu; 27343 const gpa = sema.gpa; 27344 if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| { 27345 if (!lookup.accessible) { 27346 return sema.failWithOwnedErrorMsg(block, msg: { 27347 const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{ 27348 decl_name.fmt(&zcu.intern_pool), 27349 }); 27350 errdefer msg.destroy(gpa); 27351 try sema.errNote(zcu.navSrcLoc(lookup.nav), msg, "declared here", .{}); 27352 break :msg msg; 27353 }); 27354 } 27355 return lookup.nav; 27356 } 27357 return null; 27358 } 27359 27360 fn namespaceLookupRef( 27361 sema: *Sema, 27362 block: *Block, 27363 src: LazySrcLoc, 27364 namespace: InternPool.NamespaceIndex, 27365 decl_name: InternPool.NullTerminatedString, 27366 ) CompileError!?Air.Inst.Ref { 27367 const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null; 27368 return try sema.analyzeNavRef(block, src, nav); 27369 } 27370 27371 fn namespaceLookupVal( 27372 sema: *Sema, 27373 block: *Block, 27374 src: LazySrcLoc, 27375 namespace: InternPool.NamespaceIndex, 27376 decl_name: InternPool.NullTerminatedString, 27377 ) CompileError!?Air.Inst.Ref { 27378 const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null; 27379 return try sema.analyzeNavVal(block, src, nav); 27380 } 27381 27382 fn structFieldPtr( 27383 sema: *Sema, 27384 block: *Block, 27385 src: LazySrcLoc, 27386 struct_ptr: Air.Inst.Ref, 27387 field_name: InternPool.NullTerminatedString, 27388 field_name_src: LazySrcLoc, 27389 struct_ty: Type, 27390 initializing: bool, 27391 ) CompileError!Air.Inst.Ref { 27392 const pt = sema.pt; 27393 const zcu = pt.zcu; 27394 const ip = &zcu.intern_pool; 27395 assert(struct_ty.zigTypeTag(zcu) == .@"struct"); 27396 27397 try struct_ty.resolveFields(pt); 27398 try struct_ty.resolveLayout(pt); 27399 27400 if (struct_ty.isTuple(zcu)) { 27401 if (field_name.eqlSlice("len", ip)) { 27402 const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu)); 27403 return sema.analyzeRef(block, src, len_inst); 27404 } 27405 const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); 27406 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); 27407 } 27408 27409 const struct_type = zcu.typeToStruct(struct_ty).?; 27410 27411 const field_index = struct_type.nameIndex(ip, field_name) orelse 27412 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); 27413 27414 return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty); 27415 } 27416 27417 fn structFieldPtrByIndex( 27418 sema: *Sema, 27419 block: *Block, 27420 src: LazySrcLoc, 27421 struct_ptr: Air.Inst.Ref, 27422 field_index: u32, 27423 struct_ty: Type, 27424 ) CompileError!Air.Inst.Ref { 27425 const pt = sema.pt; 27426 const zcu = pt.zcu; 27427 const ip = &zcu.intern_pool; 27428 27429 const struct_type = zcu.typeToStruct(struct_ty).?; 27430 const field_is_comptime = struct_type.fieldIsComptime(ip, field_index); 27431 27432 // Comptime fields are handled later 27433 if (!field_is_comptime) { 27434 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { 27435 const val = try struct_ptr_val.ptrField(field_index, pt); 27436 return Air.internedToRef(val.toIntern()); 27437 } 27438 } 27439 27440 const field_ty = struct_type.field_types.get(ip)[field_index]; 27441 const struct_ptr_ty = sema.typeOf(struct_ptr); 27442 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu); 27443 27444 var ptr_ty_data: InternPool.Key.PtrType = .{ 27445 .child = field_ty, 27446 .flags = .{ 27447 .is_const = struct_ptr_ty_info.flags.is_const, 27448 .is_volatile = struct_ptr_ty_info.flags.is_volatile, 27449 .address_space = struct_ptr_ty_info.flags.address_space, 27450 }, 27451 }; 27452 27453 const parent_align = if (struct_ptr_ty_info.flags.alignment != .none) 27454 struct_ptr_ty_info.flags.alignment 27455 else 27456 try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt); 27457 27458 if (struct_type.layout == .@"packed") { 27459 assert(!field_is_comptime); 27460 switch (struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt)) { 27461 .bit_ptr => |packed_offset| { 27462 ptr_ty_data.flags.alignment = parent_align; 27463 ptr_ty_data.packed_offset = packed_offset; 27464 }, 27465 .byte_ptr => |ptr_info| { 27466 ptr_ty_data.flags.alignment = ptr_info.alignment; 27467 }, 27468 } 27469 } else if (struct_type.layout == .@"extern") { 27470 assert(!field_is_comptime); 27471 // For extern structs, field alignment might be bigger than type's 27472 // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the 27473 // second field is aligned as u32. 27474 const field_offset = struct_ty.structFieldOffset(field_index, zcu); 27475 ptr_ty_data.flags.alignment = if (parent_align == .none) 27476 .none 27477 else 27478 @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset))); 27479 } else { 27480 // Our alignment is capped at the field alignment. 27481 const field_align = try Type.fromInterned(field_ty).structFieldAlignmentSema( 27482 struct_type.fieldAlign(ip, field_index), 27483 struct_type.layout, 27484 pt, 27485 ); 27486 ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none) 27487 field_align 27488 else 27489 field_align.min(parent_align); 27490 } 27491 27492 const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data); 27493 27494 if (field_is_comptime) { 27495 try struct_ty.resolveStructFieldInits(pt); 27496 const val = try pt.intern(.{ .ptr = .{ 27497 .ty = ptr_field_ty.toIntern(), 27498 .base_addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] }, 27499 .byte_offset = 0, 27500 } }); 27501 return Air.internedToRef(val); 27502 } 27503 27504 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); 27505 } 27506 27507 fn structFieldVal( 27508 sema: *Sema, 27509 block: *Block, 27510 struct_byval: Air.Inst.Ref, 27511 field_name: InternPool.NullTerminatedString, 27512 field_name_src: LazySrcLoc, 27513 struct_ty: Type, 27514 ) CompileError!Air.Inst.Ref { 27515 const pt = sema.pt; 27516 const zcu = pt.zcu; 27517 const ip = &zcu.intern_pool; 27518 assert(struct_ty.zigTypeTag(zcu) == .@"struct"); 27519 27520 try struct_ty.resolveFields(pt); 27521 27522 switch (ip.indexToKey(struct_ty.toIntern())) { 27523 .struct_type => { 27524 const struct_type = ip.loadStructType(struct_ty.toIntern()); 27525 27526 const field_index = struct_type.nameIndex(ip, field_name) orelse 27527 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); 27528 if (struct_type.fieldIsComptime(ip, field_index)) { 27529 try struct_ty.resolveStructFieldInits(pt); 27530 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]); 27531 } 27532 27533 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); 27534 if (try sema.typeHasOnePossibleValue(field_ty)) |field_val| 27535 return Air.internedToRef(field_val.toIntern()); 27536 27537 if (try sema.resolveValue(struct_byval)) |struct_val| { 27538 if (struct_val.isUndef(zcu)) return pt.undefRef(field_ty); 27539 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { 27540 return Air.internedToRef(opv.toIntern()); 27541 } 27542 return Air.internedToRef((try struct_val.fieldValue(pt, field_index)).toIntern()); 27543 } 27544 27545 try field_ty.resolveLayout(pt); 27546 return block.addStructFieldVal(struct_byval, field_index, field_ty); 27547 }, 27548 .tuple_type => { 27549 return sema.tupleFieldVal(block, struct_byval, field_name, field_name_src, struct_ty); 27550 }, 27551 else => unreachable, 27552 } 27553 } 27554 27555 fn tupleFieldVal( 27556 sema: *Sema, 27557 block: *Block, 27558 tuple_byval: Air.Inst.Ref, 27559 field_name: InternPool.NullTerminatedString, 27560 field_name_src: LazySrcLoc, 27561 tuple_ty: Type, 27562 ) CompileError!Air.Inst.Ref { 27563 const pt = sema.pt; 27564 const zcu = pt.zcu; 27565 if (field_name.eqlSlice("len", &zcu.intern_pool)) { 27566 return pt.intRef(.usize, tuple_ty.structFieldCount(zcu)); 27567 } 27568 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src); 27569 return sema.tupleFieldValByIndex(block, tuple_byval, field_index, tuple_ty); 27570 } 27571 27572 /// Asserts that `field_name` is not "len". 27573 fn tupleFieldIndex( 27574 sema: *Sema, 27575 block: *Block, 27576 tuple_ty: Type, 27577 field_name: InternPool.NullTerminatedString, 27578 field_name_src: LazySrcLoc, 27579 ) CompileError!u32 { 27580 const pt = sema.pt; 27581 const ip = &pt.zcu.intern_pool; 27582 assert(!field_name.eqlSlice("len", ip)); 27583 if (field_name.toUnsigned(ip)) |field_index| { 27584 if (field_index < tuple_ty.structFieldCount(pt.zcu)) return field_index; 27585 return sema.fail(block, field_name_src, "index '{f}' out of bounds of tuple '{f}'", .{ 27586 field_name.fmt(ip), tuple_ty.fmt(pt), 27587 }); 27588 } 27589 27590 return sema.fail(block, field_name_src, "no field named '{f}' in tuple '{f}'", .{ 27591 field_name.fmt(ip), tuple_ty.fmt(pt), 27592 }); 27593 } 27594 27595 fn tupleFieldValByIndex( 27596 sema: *Sema, 27597 block: *Block, 27598 tuple_byval: Air.Inst.Ref, 27599 field_index: u32, 27600 tuple_ty: Type, 27601 ) CompileError!Air.Inst.Ref { 27602 const pt = sema.pt; 27603 const zcu = pt.zcu; 27604 const field_ty = tuple_ty.fieldType(field_index, zcu); 27605 27606 if (tuple_ty.structFieldIsComptime(field_index, zcu)) 27607 try tuple_ty.resolveStructFieldInits(pt); 27608 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| { 27609 return Air.internedToRef(default_value.toIntern()); 27610 } 27611 27612 if (try sema.resolveValue(tuple_byval)) |tuple_val| { 27613 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { 27614 return Air.internedToRef(opv.toIntern()); 27615 } 27616 return switch (zcu.intern_pool.indexToKey(tuple_val.toIntern())) { 27617 .undef => pt.undefRef(field_ty), 27618 .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) { 27619 .bytes => |bytes| try pt.intValue(.u8, bytes.at(field_index, &zcu.intern_pool)), 27620 .elems => |elems| Value.fromInterned(elems[field_index]), 27621 .repeated_elem => |elem| Value.fromInterned(elem), 27622 }.toIntern()), 27623 else => unreachable, 27624 }; 27625 } 27626 27627 try field_ty.resolveLayout(pt); 27628 return block.addStructFieldVal(tuple_byval, field_index, field_ty); 27629 } 27630 27631 fn unionFieldPtr( 27632 sema: *Sema, 27633 block: *Block, 27634 src: LazySrcLoc, 27635 union_ptr: Air.Inst.Ref, 27636 field_name: InternPool.NullTerminatedString, 27637 field_name_src: LazySrcLoc, 27638 union_ty: Type, 27639 initializing: bool, 27640 ) CompileError!Air.Inst.Ref { 27641 const pt = sema.pt; 27642 const zcu = pt.zcu; 27643 const ip = &zcu.intern_pool; 27644 27645 assert(union_ty.zigTypeTag(zcu) == .@"union"); 27646 27647 const union_ptr_ty = sema.typeOf(union_ptr); 27648 const union_ptr_info = union_ptr_ty.ptrInfo(zcu); 27649 try union_ty.resolveFields(pt); 27650 const union_obj = zcu.typeToUnion(union_ty).?; 27651 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 27652 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 27653 const ptr_field_ty = try pt.ptrTypeSema(.{ 27654 .child = field_ty.toIntern(), 27655 .flags = .{ 27656 .is_const = union_ptr_info.flags.is_const, 27657 .is_volatile = union_ptr_info.flags.is_volatile, 27658 .address_space = union_ptr_info.flags.address_space, 27659 .alignment = if (union_obj.flagsUnordered(ip).layout == .auto) blk: { 27660 const union_align = if (union_ptr_info.flags.alignment != .none) 27661 union_ptr_info.flags.alignment 27662 else 27663 try union_ty.abiAlignmentSema(pt); 27664 const field_align = try union_ty.fieldAlignmentSema(field_index, pt); 27665 break :blk union_align.min(field_align); 27666 } else union_ptr_info.flags.alignment, 27667 }, 27668 .packed_offset = union_ptr_info.packed_offset, 27669 }); 27670 const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?); 27671 27672 if (initializing and field_ty.zigTypeTag(zcu) == .noreturn) { 27673 const msg = msg: { 27674 const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{}); 27675 errdefer msg.destroy(sema.gpa); 27676 27677 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ 27678 field_name.fmt(ip), 27679 }); 27680 try sema.addDeclaredHereNote(msg, union_ty); 27681 break :msg msg; 27682 }; 27683 return sema.failWithOwnedErrorMsg(block, msg); 27684 } 27685 27686 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { 27687 switch (union_obj.flagsUnordered(ip).layout) { 27688 .auto => if (initializing) { 27689 if (!sema.isComptimeMutablePtr(union_ptr_val)) { 27690 // The initialization is a runtime operation. 27691 break :ct; 27692 } 27693 // Store to the union to initialize the tag. 27694 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27695 const payload_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 27696 const new_union_val = try pt.unionValue(union_ty, field_tag, try pt.undefValue(payload_ty)); 27697 try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty); 27698 } else { 27699 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse 27700 break :ct; 27701 if (union_val.isUndef(zcu)) { 27702 return sema.failWithUseOfUndef(block, src, null); 27703 } 27704 const un = ip.indexToKey(union_val.toIntern()).un; 27705 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27706 const tag_matches = un.tag == field_tag.toIntern(); 27707 if (!tag_matches) { 27708 const msg = msg: { 27709 const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?; 27710 const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu); 27711 const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ 27712 field_name.fmt(ip), 27713 active_field_name.fmt(ip), 27714 }); 27715 errdefer msg.destroy(sema.gpa); 27716 try sema.addDeclaredHereNote(msg, union_ty); 27717 break :msg msg; 27718 }; 27719 return sema.failWithOwnedErrorMsg(block, msg); 27720 } 27721 }, 27722 .@"packed", .@"extern" => {}, 27723 } 27724 const field_ptr_val = try union_ptr_val.ptrField(field_index, pt); 27725 return Air.internedToRef(field_ptr_val.toIntern()); 27726 } 27727 27728 // If the union has a tag, we must either set or or safety check it depending on `initializing`. 27729 tag: { 27730 if (union_ty.containerLayout(zcu) != .auto) break :tag; 27731 const tag_ty: Type = .fromInterned(union_obj.enum_tag_ty); 27732 if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag; 27733 // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but 27734 // only emit a safety check if it's available at runtime (i.e. it's safety-tagged). 27735 const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index); 27736 if (initializing) { 27737 const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag)); 27738 try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store 27739 } else if (block.wantSafety() and union_obj.hasTag(ip)) { 27740 // The tag exists at runtime (safety tag), so emit a safety check. 27741 // TODO would it be better if get_union_tag supported pointers to unions? 27742 const union_val = try block.addTyOp(.load, union_ty, union_ptr); 27743 const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val); 27744 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag)); 27745 } 27746 } 27747 if (field_ty.zigTypeTag(zcu) == .noreturn) { 27748 _ = try block.addNoOp(.unreach); 27749 return .unreachable_value; 27750 } 27751 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); 27752 } 27753 27754 fn unionFieldVal( 27755 sema: *Sema, 27756 block: *Block, 27757 src: LazySrcLoc, 27758 union_byval: Air.Inst.Ref, 27759 field_name: InternPool.NullTerminatedString, 27760 field_name_src: LazySrcLoc, 27761 union_ty: Type, 27762 ) CompileError!Air.Inst.Ref { 27763 const pt = sema.pt; 27764 const zcu = pt.zcu; 27765 const ip = &zcu.intern_pool; 27766 assert(union_ty.zigTypeTag(zcu) == .@"union"); 27767 27768 try union_ty.resolveFields(pt); 27769 const union_obj = zcu.typeToUnion(union_ty).?; 27770 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 27771 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 27772 const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?); 27773 27774 if (try sema.resolveValue(union_byval)) |union_val| { 27775 if (union_val.isUndef(zcu)) return pt.undefRef(field_ty); 27776 27777 const un = ip.indexToKey(union_val.toIntern()).un; 27778 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27779 const tag_matches = un.tag == field_tag.toIntern(); 27780 switch (union_obj.flagsUnordered(ip).layout) { 27781 .auto => { 27782 if (tag_matches) { 27783 return Air.internedToRef(un.val); 27784 } else { 27785 const msg = msg: { 27786 const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?; 27787 const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu); 27788 const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ 27789 field_name.fmt(ip), active_field_name.fmt(ip), 27790 }); 27791 errdefer msg.destroy(sema.gpa); 27792 try sema.addDeclaredHereNote(msg, union_ty); 27793 break :msg msg; 27794 }; 27795 return sema.failWithOwnedErrorMsg(block, msg); 27796 } 27797 }, 27798 .@"extern" => if (tag_matches) { 27799 // Fast path - no need to use bitcast logic. 27800 return Air.internedToRef(un.val); 27801 } else if (try sema.bitCastVal(union_val, field_ty, 0, 0, 0)) |field_val| { 27802 return Air.internedToRef(field_val.toIntern()); 27803 }, 27804 .@"packed" => if (tag_matches) { 27805 // Fast path - no need to use bitcast logic. 27806 return Air.internedToRef(un.val); 27807 } else if (try sema.bitCastVal(union_val, field_ty, 0, try union_ty.bitSizeSema(pt), 0)) |field_val| { 27808 return Air.internedToRef(field_val.toIntern()); 27809 }, 27810 } 27811 } 27812 27813 if (union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and 27814 union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1) 27815 { 27816 const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27817 const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); 27818 const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_byval); 27819 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag); 27820 } 27821 27822 if (field_ty.zigTypeTag(zcu) == .noreturn) { 27823 _ = try block.addNoOp(.unreach); 27824 return .unreachable_value; 27825 } 27826 27827 if (try sema.typeHasOnePossibleValue(field_ty)) |field_only_value| { 27828 return Air.internedToRef(field_only_value.toIntern()); 27829 } 27830 27831 try field_ty.resolveLayout(pt); 27832 return block.addStructFieldVal(union_byval, field_index, field_ty); 27833 } 27834 27835 fn elemPtr( 27836 sema: *Sema, 27837 block: *Block, 27838 src: LazySrcLoc, 27839 indexable_ptr: Air.Inst.Ref, 27840 elem_index: Air.Inst.Ref, 27841 elem_index_src: LazySrcLoc, 27842 init: bool, 27843 oob_safety: bool, 27844 ) CompileError!Air.Inst.Ref { 27845 const pt = sema.pt; 27846 const zcu = pt.zcu; 27847 const indexable_ptr_src = src; // TODO better source location 27848 const indexable_ptr_ty = sema.typeOf(indexable_ptr); 27849 27850 const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(zcu)) { 27851 .pointer => indexable_ptr_ty.childType(zcu), 27852 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}), 27853 }; 27854 try sema.checkIndexable(block, src, indexable_ty); 27855 27856 const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) { 27857 .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), 27858 .@"struct" => blk: { 27859 // Tuple field access. 27860 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); 27861 const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); 27862 break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); 27863 }, 27864 else => { 27865 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); 27866 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); 27867 }, 27868 }; 27869 27870 try sema.checkKnownAllocPtr(block, indexable_ptr, elem_ptr); 27871 return elem_ptr; 27872 } 27873 27874 /// Asserts that the type of indexable is pointer. 27875 fn elemPtrOneLayerOnly( 27876 sema: *Sema, 27877 block: *Block, 27878 src: LazySrcLoc, 27879 indexable: Air.Inst.Ref, 27880 elem_index: Air.Inst.Ref, 27881 elem_index_src: LazySrcLoc, 27882 init: bool, 27883 oob_safety: bool, 27884 ) CompileError!Air.Inst.Ref { 27885 const indexable_src = src; // TODO better source location 27886 const indexable_ty = sema.typeOf(indexable); 27887 const pt = sema.pt; 27888 const zcu = pt.zcu; 27889 27890 try sema.checkIndexable(block, src, indexable_ty); 27891 27892 switch (indexable_ty.ptrSize(zcu)) { 27893 .slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27894 .many, .c => { 27895 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 27896 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 27897 ct: { 27898 const ptr_val = maybe_ptr_val orelse break :ct; 27899 const index_val = maybe_index_val orelse break :ct; 27900 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 27901 const elem_ptr = try ptr_val.ptrElem(index, pt); 27902 return Air.internedToRef(elem_ptr.toIntern()); 27903 } 27904 27905 try sema.checkLogicalPtrOperation(block, src, indexable_ty); 27906 const result_ty = try indexable_ty.elemPtrType(null, pt); 27907 27908 try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_ty, indexable_src); 27909 try sema.validateRuntimeValue(block, indexable_src, indexable); 27910 27911 if (!try result_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) { 27912 // zero-bit child type; just bitcast the pointer 27913 return block.addBitCast(result_ty, indexable); 27914 } 27915 27916 return block.addPtrElemPtr(indexable, elem_index, result_ty); 27917 }, 27918 .one => { 27919 const child_ty = indexable_ty.childType(zcu); 27920 const elem_ptr = switch (child_ty.zigTypeTag(zcu)) { 27921 .array, .vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety), 27922 .@"struct" => blk: { 27923 assert(child_ty.isTuple(zcu)); 27924 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); 27925 const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); 27926 break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); 27927 }, 27928 else => unreachable, // Guaranteed by checkIndexable 27929 }; 27930 try sema.checkKnownAllocPtr(block, indexable, elem_ptr); 27931 return elem_ptr; 27932 }, 27933 } 27934 } 27935 27936 fn elemVal( 27937 sema: *Sema, 27938 block: *Block, 27939 src: LazySrcLoc, 27940 indexable: Air.Inst.Ref, 27941 elem_index_uncasted: Air.Inst.Ref, 27942 elem_index_src: LazySrcLoc, 27943 oob_safety: bool, 27944 ) CompileError!Air.Inst.Ref { 27945 const indexable_src = src; // TODO better source location 27946 const indexable_ty = sema.typeOf(indexable); 27947 const pt = sema.pt; 27948 const zcu = pt.zcu; 27949 27950 try sema.checkIndexable(block, src, indexable_ty); 27951 27952 // TODO in case of a vector of pointers, we need to detect whether the element 27953 // index is a scalar or vector instead of unconditionally casting to usize. 27954 const elem_index = try sema.coerce(block, .usize, elem_index_uncasted, elem_index_src); 27955 27956 switch (indexable_ty.zigTypeTag(zcu)) { 27957 .pointer => switch (indexable_ty.ptrSize(zcu)) { 27958 .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27959 .many, .c => { 27960 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 27961 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 27962 const elem_ty = indexable_ty.elemType2(zcu); 27963 27964 ct: { 27965 const indexable_val = maybe_indexable_val orelse break :ct; 27966 const index_val = maybe_index_val orelse break :ct; 27967 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 27968 const many_ptr_ty = try pt.manyConstPtrType(elem_ty); 27969 const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty); 27970 const elem_ptr_ty = try pt.singleConstPtrType(elem_ty); 27971 const elem_ptr_val = try many_ptr_val.ptrElem(index, pt); 27972 const elem_val = try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty) orelse break :ct; 27973 return Air.internedToRef((try pt.getCoerced(elem_val, elem_ty)).toIntern()); 27974 } 27975 27976 if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| { 27977 return Air.internedToRef(elem_only_value.toIntern()); 27978 } 27979 27980 try sema.checkLogicalPtrOperation(block, src, indexable_ty); 27981 return block.addBinOp(.ptr_elem_val, indexable, elem_index); 27982 }, 27983 .one => { 27984 arr_sent: { 27985 const inner_ty = indexable_ty.childType(zcu); 27986 if (inner_ty.zigTypeTag(zcu) != .array) break :arr_sent; 27987 const sentinel = inner_ty.sentinel(zcu) orelse break :arr_sent; 27988 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; 27989 const index = try sema.usizeCast(block, src, try index_val.toUnsignedIntSema(pt)); 27990 if (index != inner_ty.arrayLen(zcu)) break :arr_sent; 27991 return Air.internedToRef(sentinel.toIntern()); 27992 } 27993 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); 27994 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); 27995 }, 27996 }, 27997 .array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27998 .vector => { 27999 // TODO: If the index is a vector, the result should be a vector. 28000 return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety); 28001 }, 28002 .@"struct" => { 28003 // Tuple field access. 28004 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); 28005 const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); 28006 return sema.tupleField(block, indexable_src, indexable, elem_index_src, index); 28007 }, 28008 else => unreachable, 28009 } 28010 } 28011 28012 /// Called when the index or indexable is runtime known. 28013 fn validateRuntimeElemAccess( 28014 sema: *Sema, 28015 block: *Block, 28016 elem_index_src: LazySrcLoc, 28017 elem_ty: Type, 28018 parent_ty: Type, 28019 parent_src: LazySrcLoc, 28020 ) CompileError!void { 28021 const pt = sema.pt; 28022 const zcu = pt.zcu; 28023 28024 if (try elem_ty.comptimeOnlySema(sema.pt)) { 28025 const msg = msg: { 28026 const msg = try sema.errMsg( 28027 elem_index_src, 28028 "values of type '{f}' must be comptime-known, but index value is runtime-known", 28029 .{parent_ty.fmt(sema.pt)}, 28030 ); 28031 errdefer msg.destroy(sema.gpa); 28032 28033 try sema.explainWhyTypeIsComptime(msg, parent_src, parent_ty); 28034 28035 break :msg msg; 28036 }; 28037 return sema.failWithOwnedErrorMsg(block, msg); 28038 } 28039 28040 if (zcu.intern_pool.indexToKey(parent_ty.toIntern()) == .ptr_type) { 28041 const target = zcu.getTarget(); 28042 const as = parent_ty.ptrAddressSpace(zcu); 28043 if (target_util.arePointersLogical(target, as)) { 28044 return sema.fail(block, elem_index_src, "cannot access element of logical pointer '{f}'", .{parent_ty.fmt(pt)}); 28045 } 28046 } 28047 } 28048 28049 fn tupleFieldPtr( 28050 sema: *Sema, 28051 block: *Block, 28052 tuple_ptr_src: LazySrcLoc, 28053 tuple_ptr: Air.Inst.Ref, 28054 field_index_src: LazySrcLoc, 28055 field_index: u32, 28056 init: bool, 28057 ) CompileError!Air.Inst.Ref { 28058 const pt = sema.pt; 28059 const zcu = pt.zcu; 28060 const tuple_ptr_ty = sema.typeOf(tuple_ptr); 28061 const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu); 28062 const tuple_ty: Type = .fromInterned(tuple_ptr_info.child); 28063 try tuple_ty.resolveFields(pt); 28064 const field_count = tuple_ty.structFieldCount(zcu); 28065 28066 if (field_count == 0) { 28067 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); 28068 } 28069 28070 if (field_index >= field_count) { 28071 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 28072 field_index, field_count, 28073 }); 28074 } 28075 28076 const field_ty = tuple_ty.fieldType(field_index, zcu); 28077 const ptr_field_ty = try pt.ptrTypeSema(.{ 28078 .child = field_ty.toIntern(), 28079 .flags = .{ 28080 .is_const = tuple_ptr_info.flags.is_const, 28081 .is_volatile = tuple_ptr_info.flags.is_volatile, 28082 .address_space = tuple_ptr_info.flags.address_space, 28083 .alignment = a: { 28084 if (tuple_ptr_info.flags.alignment == .none) break :a .none; 28085 // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned. 28086 const tuple_align = tuple_ptr_info.flags.alignment; 28087 const field_align = try field_ty.abiAlignmentSema(pt); 28088 break :a tuple_align.min(field_align); 28089 }, 28090 }, 28091 }); 28092 28093 if (tuple_ty.structFieldIsComptime(field_index, zcu)) 28094 try tuple_ty.resolveStructFieldInits(pt); 28095 28096 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_val| { 28097 return Air.internedToRef((try pt.intern(.{ .ptr = .{ 28098 .ty = ptr_field_ty.toIntern(), 28099 .base_addr = .{ .comptime_field = default_val.toIntern() }, 28100 .byte_offset = 0, 28101 } }))); 28102 } 28103 28104 if (try sema.resolveValue(tuple_ptr)) |tuple_ptr_val| { 28105 const field_ptr_val = try tuple_ptr_val.ptrField(field_index, pt); 28106 return Air.internedToRef(field_ptr_val.toIntern()); 28107 } 28108 28109 if (!init) { 28110 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src); 28111 } 28112 28113 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); 28114 } 28115 28116 fn tupleField( 28117 sema: *Sema, 28118 block: *Block, 28119 tuple_src: LazySrcLoc, 28120 tuple: Air.Inst.Ref, 28121 field_index_src: LazySrcLoc, 28122 field_index: u32, 28123 ) CompileError!Air.Inst.Ref { 28124 const pt = sema.pt; 28125 const zcu = pt.zcu; 28126 const tuple_ty = sema.typeOf(tuple); 28127 try tuple_ty.resolveFields(pt); 28128 const field_count = tuple_ty.structFieldCount(zcu); 28129 28130 if (field_count == 0) { 28131 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); 28132 } 28133 28134 if (field_index >= field_count) { 28135 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 28136 field_index, field_count, 28137 }); 28138 } 28139 28140 const field_ty = tuple_ty.fieldType(field_index, zcu); 28141 28142 if (tuple_ty.structFieldIsComptime(field_index, zcu)) 28143 try tuple_ty.resolveStructFieldInits(pt); 28144 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| { 28145 return Air.internedToRef(default_value.toIntern()); // comptime field 28146 } 28147 28148 if (try sema.resolveValue(tuple)) |tuple_val| { 28149 if (tuple_val.isUndef(zcu)) return pt.undefRef(field_ty); 28150 return Air.internedToRef((try tuple_val.fieldValue(pt, field_index)).toIntern()); 28151 } 28152 28153 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); 28154 28155 try field_ty.resolveLayout(pt); 28156 return block.addStructFieldVal(tuple, field_index, field_ty); 28157 } 28158 28159 fn elemValArray( 28160 sema: *Sema, 28161 block: *Block, 28162 src: LazySrcLoc, 28163 array_src: LazySrcLoc, 28164 array: Air.Inst.Ref, 28165 elem_index_src: LazySrcLoc, 28166 elem_index: Air.Inst.Ref, 28167 oob_safety: bool, 28168 ) CompileError!Air.Inst.Ref { 28169 const pt = sema.pt; 28170 const zcu = pt.zcu; 28171 const array_ty = sema.typeOf(array); 28172 const array_sent = array_ty.sentinel(zcu); 28173 const array_len = array_ty.arrayLen(zcu); 28174 const array_len_s = array_len + @intFromBool(array_sent != null); 28175 const elem_ty = array_ty.childType(zcu); 28176 28177 if (array_len_s == 0) { 28178 return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); 28179 } 28180 28181 const maybe_undef_array_val = try sema.resolveValue(array); 28182 // index must be defined since it can access out of bounds 28183 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 28184 28185 if (maybe_index_val) |index_val| { 28186 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 28187 if (array_sent) |s| { 28188 if (index == array_len) { 28189 return Air.internedToRef(s.toIntern()); 28190 } 28191 } 28192 if (index >= array_len_s) { 28193 const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else ""; 28194 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 28195 } 28196 } 28197 if (maybe_undef_array_val) |array_val| { 28198 if (array_val.isUndef(zcu)) { 28199 return pt.undefRef(elem_ty); 28200 } 28201 if (maybe_index_val) |index_val| { 28202 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 28203 const elem_val = try array_val.elemValue(pt, index); 28204 return Air.internedToRef(elem_val.toIntern()); 28205 } 28206 } 28207 28208 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src); 28209 try sema.validateRuntimeValue(block, array_src, array); 28210 28211 if (oob_safety and block.wantSafety()) { 28212 // Runtime check is only needed if unable to comptime check. 28213 if (maybe_index_val == null) { 28214 const len_inst = try pt.intRef(.usize, array_len); 28215 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt; 28216 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); 28217 } 28218 } 28219 28220 if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_val| 28221 return Air.internedToRef(elem_val.toIntern()); 28222 28223 return block.addBinOp(.array_elem_val, array, elem_index); 28224 } 28225 28226 fn elemPtrArray( 28227 sema: *Sema, 28228 block: *Block, 28229 src: LazySrcLoc, 28230 array_ptr_src: LazySrcLoc, 28231 array_ptr: Air.Inst.Ref, 28232 elem_index_src: LazySrcLoc, 28233 elem_index: Air.Inst.Ref, 28234 init: bool, 28235 oob_safety: bool, 28236 ) CompileError!Air.Inst.Ref { 28237 const pt = sema.pt; 28238 const zcu = pt.zcu; 28239 const array_ptr_ty = sema.typeOf(array_ptr); 28240 const array_ty = array_ptr_ty.childType(zcu); 28241 const array_sent = array_ty.sentinel(zcu) != null; 28242 const array_len = array_ty.arrayLen(zcu); 28243 const array_len_s = array_len + @intFromBool(array_sent); 28244 28245 if (array_len_s == 0) { 28246 return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{}); 28247 } 28248 28249 const maybe_undef_array_ptr_val = try sema.resolveValue(array_ptr); 28250 // The index must not be undefined since it can be out of bounds. 28251 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 28252 const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt)); 28253 if (index >= array_len_s) { 28254 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; 28255 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 28256 } 28257 break :o index; 28258 } else null; 28259 28260 const elem_ptr_ty = try array_ptr_ty.elemPtrType(offset, pt); 28261 28262 if (maybe_undef_array_ptr_val) |array_ptr_val| { 28263 if (array_ptr_val.isUndef(zcu)) { 28264 return pt.undefRef(elem_ptr_ty); 28265 } 28266 if (offset) |index| { 28267 const elem_ptr = try array_ptr_val.ptrElem(index, pt); 28268 return Air.internedToRef(elem_ptr.toIntern()); 28269 } 28270 } 28271 28272 if (!init) { 28273 try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(zcu), array_ty, array_ptr_src); 28274 try sema.validateRuntimeValue(block, array_ptr_src, array_ptr); 28275 } 28276 28277 if (offset == null and array_ty.zigTypeTag(zcu) == .vector) { 28278 return sema.fail(block, elem_index_src, "vector index not comptime known", .{}); 28279 } 28280 28281 // Runtime check is only needed if unable to comptime check. 28282 if (oob_safety and block.wantSafety() and offset == null) { 28283 const len_inst = try pt.intRef(.usize, array_len); 28284 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; 28285 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); 28286 } 28287 28288 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); 28289 } 28290 28291 fn elemValSlice( 28292 sema: *Sema, 28293 block: *Block, 28294 src: LazySrcLoc, 28295 slice_src: LazySrcLoc, 28296 slice: Air.Inst.Ref, 28297 elem_index_src: LazySrcLoc, 28298 elem_index: Air.Inst.Ref, 28299 oob_safety: bool, 28300 ) CompileError!Air.Inst.Ref { 28301 const pt = sema.pt; 28302 const zcu = pt.zcu; 28303 const slice_ty = sema.typeOf(slice); 28304 const slice_sent = slice_ty.sentinel(zcu) != null; 28305 const elem_ty = slice_ty.elemType2(zcu); 28306 var runtime_src = slice_src; 28307 28308 // slice must be defined since it can dereferenced as null 28309 const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); 28310 // index must be defined since it can index out of bounds 28311 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 28312 28313 if (maybe_slice_val) |slice_val| { 28314 runtime_src = elem_index_src; 28315 const slice_len = try slice_val.sliceLen(pt); 28316 const slice_len_s = slice_len + @intFromBool(slice_sent); 28317 if (slice_len_s == 0) { 28318 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 28319 } 28320 if (maybe_index_val) |index_val| { 28321 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 28322 if (index >= slice_len_s) { 28323 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 28324 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 28325 } 28326 const elem_ptr_ty = try slice_ty.elemPtrType(index, pt); 28327 const elem_ptr_val = try slice_val.ptrElem(index, pt); 28328 if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { 28329 return Air.internedToRef(elem_val.toIntern()); 28330 } 28331 runtime_src = slice_src; 28332 } 28333 } 28334 28335 if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| { 28336 return Air.internedToRef(elem_only_value.toIntern()); 28337 } 28338 28339 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src); 28340 try sema.validateRuntimeValue(block, slice_src, slice); 28341 28342 if (oob_safety and block.wantSafety()) { 28343 const len_inst = if (maybe_slice_val) |slice_val| 28344 try pt.intRef(.usize, try slice_val.sliceLen(pt)) 28345 else 28346 try block.addTyOp(.slice_len, .usize, slice); 28347 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 28348 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); 28349 } 28350 return block.addBinOp(.slice_elem_val, slice, elem_index); 28351 } 28352 28353 fn elemPtrSlice( 28354 sema: *Sema, 28355 block: *Block, 28356 src: LazySrcLoc, 28357 slice_src: LazySrcLoc, 28358 slice: Air.Inst.Ref, 28359 elem_index_src: LazySrcLoc, 28360 elem_index: Air.Inst.Ref, 28361 oob_safety: bool, 28362 ) CompileError!Air.Inst.Ref { 28363 const pt = sema.pt; 28364 const zcu = pt.zcu; 28365 const slice_ty = sema.typeOf(slice); 28366 const slice_sent = slice_ty.sentinel(zcu) != null; 28367 28368 const maybe_undef_slice_val = try sema.resolveValue(slice); 28369 // The index must not be undefined since it can be out of bounds. 28370 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 28371 const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt)); 28372 break :o index; 28373 } else null; 28374 28375 const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt); 28376 28377 if (maybe_undef_slice_val) |slice_val| { 28378 if (slice_val.isUndef(zcu)) { 28379 return pt.undefRef(elem_ptr_ty); 28380 } 28381 const slice_len = try slice_val.sliceLen(pt); 28382 const slice_len_s = slice_len + @intFromBool(slice_sent); 28383 if (slice_len_s == 0) { 28384 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 28385 } 28386 if (offset) |index| { 28387 if (index >= slice_len_s) { 28388 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 28389 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 28390 } 28391 const elem_ptr_val = try slice_val.ptrElem(index, pt); 28392 return Air.internedToRef(elem_ptr_val.toIntern()); 28393 } 28394 } 28395 28396 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src); 28397 try sema.validateRuntimeValue(block, slice_src, slice); 28398 28399 if (oob_safety and block.wantSafety()) { 28400 const len_inst = len: { 28401 if (maybe_undef_slice_val) |slice_val| 28402 if (!slice_val.isUndef(zcu)) 28403 break :len try pt.intRef(.usize, try slice_val.sliceLen(pt)); 28404 break :len try block.addTyOp(.slice_len, .usize, slice); 28405 }; 28406 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 28407 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); 28408 } 28409 if (!try slice_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) { 28410 // zero-bit child type; just extract the pointer and bitcast it 28411 const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice); 28412 return block.addBitCast(elem_ptr_ty, slice_ptr); 28413 } 28414 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); 28415 } 28416 28417 pub fn coerce( 28418 sema: *Sema, 28419 block: *Block, 28420 dest_ty_unresolved: Type, 28421 inst: Air.Inst.Ref, 28422 inst_src: LazySrcLoc, 28423 ) CompileError!Air.Inst.Ref { 28424 return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) { 28425 error.NotCoercible => unreachable, 28426 else => |e| return e, 28427 }; 28428 } 28429 28430 const CoersionError = CompileError || error{ 28431 /// When coerce is called recursively, this error should be returned instead of using `fail` 28432 /// to ensure correct types in compile errors. 28433 NotCoercible, 28434 }; 28435 28436 const CoerceOpts = struct { 28437 /// Should coerceExtra emit error messages. 28438 report_err: bool = true, 28439 /// Ignored if `report_err == false`. 28440 is_ret: bool = false, 28441 /// Should coercion to comptime_int emit an error message. 28442 no_cast_to_comptime_int: bool = false, 28443 28444 param_src: struct { 28445 func_inst: Air.Inst.Ref = .none, 28446 param_i: u32 = undefined, 28447 28448 fn get(info: @This(), sema: *Sema) !?LazySrcLoc { 28449 if (info.func_inst == .none) return null; 28450 const func_inst = try sema.funcDeclSrcInst(info.func_inst) orelse return null; 28451 return .{ 28452 .base_node_inst = func_inst, 28453 .offset = .{ .fn_proto_param_type = .{ 28454 .fn_proto_node_offset = .zero, 28455 .param_index = info.param_i, 28456 } }, 28457 }; 28458 } 28459 } = .{ .func_inst = .none, .param_i = undefined }, 28460 }; 28461 28462 fn coerceExtra( 28463 sema: *Sema, 28464 block: *Block, 28465 dest_ty: Type, 28466 inst: Air.Inst.Ref, 28467 inst_src: LazySrcLoc, 28468 opts: CoerceOpts, 28469 ) CoersionError!Air.Inst.Ref { 28470 if (dest_ty.isGenericPoison()) return inst; 28471 const pt = sema.pt; 28472 const zcu = pt.zcu; 28473 const ip = &zcu.intern_pool; 28474 const dest_ty_src = inst_src; // TODO better source location 28475 try dest_ty.resolveFields(pt); 28476 const inst_ty = sema.typeOf(inst); 28477 try inst_ty.resolveFields(pt); 28478 const target = zcu.getTarget(); 28479 // If the types are the same, we can return the operand. 28480 if (dest_ty.eql(inst_ty, zcu)) 28481 return inst; 28482 28483 const maybe_inst_val = try sema.resolveValue(inst); 28484 28485 var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); 28486 if (in_memory_result == .ok) { 28487 if (maybe_inst_val) |val| { 28488 return sema.coerceInMemory(val, dest_ty); 28489 } 28490 try sema.requireRuntimeBlock(block, inst_src, null); 28491 const new_val = try block.addBitCast(dest_ty, inst); 28492 try sema.checkKnownAllocPtr(block, inst, new_val); 28493 return new_val; 28494 } 28495 28496 switch (dest_ty.zigTypeTag(zcu)) { 28497 .optional => optional: { 28498 if (maybe_inst_val) |val| { 28499 // undefined sets the optional bit also to undefined. 28500 if (val.toIntern() == .undef) { 28501 return pt.undefRef(dest_ty); 28502 } 28503 28504 // null to ?T 28505 if (val.toIntern() == .null_value) { 28506 return Air.internedToRef((try pt.intern(.{ .opt = .{ 28507 .ty = dest_ty.toIntern(), 28508 .val = .none, 28509 } }))); 28510 } 28511 } 28512 28513 // cast from ?*T and ?[*]T to ?*anyopaque 28514 // but don't do it if the source type is a double pointer 28515 if (dest_ty.isPtrLikeOptional(zcu) and 28516 dest_ty.elemType2(zcu).toIntern() == .anyopaque_type and 28517 inst_ty.isPtrAtRuntime(zcu)) 28518 anyopaque_check: { 28519 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; 28520 const elem_ty = inst_ty.elemType2(zcu); 28521 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) { 28522 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 28523 .actual = inst_ty, 28524 .wanted = dest_ty, 28525 } }; 28526 break :optional; 28527 } 28528 // Let the logic below handle wrapping the optional now that 28529 // it has been checked to correctly coerce. 28530 if (!inst_ty.isPtrLikeOptional(zcu)) break :anyopaque_check; 28531 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28532 } 28533 28534 // T to ?T 28535 const child_type = dest_ty.optionalChild(zcu); 28536 const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 28537 error.NotCoercible => { 28538 if (in_memory_result == .no_match) { 28539 // Try to give more useful notes 28540 in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); 28541 } 28542 break :optional; 28543 }, 28544 else => |e| return e, 28545 }; 28546 return try sema.wrapOptional(block, dest_ty, intermediate, inst_src); 28547 }, 28548 .pointer => pointer: { 28549 const dest_info = dest_ty.ptrInfo(zcu); 28550 28551 // Function body to function pointer. 28552 if (inst_ty.zigTypeTag(zcu) == .@"fn") { 28553 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); 28554 const fn_nav = switch (zcu.intern_pool.indexToKey(fn_val.toIntern())) { 28555 .func => |f| f.owner_nav, 28556 .@"extern" => |e| e.owner_nav, 28557 else => unreachable, 28558 }; 28559 const inst_as_ptr = try sema.analyzeNavRef(block, inst_src, fn_nav); 28560 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); 28561 } 28562 28563 // *T to *[1]T 28564 single_item: { 28565 if (dest_info.flags.size != .one) break :single_item; 28566 if (!inst_ty.isSinglePointer(zcu)) break :single_item; 28567 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 28568 const ptr_elem_ty = inst_ty.childType(zcu); 28569 const array_ty: Type = .fromInterned(dest_info.child); 28570 if (array_ty.zigTypeTag(zcu) != .array) break :single_item; 28571 const array_elem_ty = array_ty.childType(zcu); 28572 if (array_ty.arrayLen(zcu) != 1) break :single_item; 28573 const dest_is_mut = !dest_info.flags.is_const; 28574 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) { 28575 .ok => {}, 28576 else => break :single_item, 28577 } 28578 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28579 } 28580 28581 // Coercions where the source is a single pointer to an array. 28582 src_array_ptr: { 28583 if (!inst_ty.isSinglePointer(zcu)) break :src_array_ptr; 28584 if (dest_info.flags.size == .one) break :src_array_ptr; // `*[n]T` -> `*T` isn't valid 28585 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 28586 const array_ty = inst_ty.childType(zcu); 28587 if (array_ty.zigTypeTag(zcu) != .array) break :src_array_ptr; 28588 const array_elem_type = array_ty.childType(zcu); 28589 const dest_is_mut = !dest_info.flags.is_const; 28590 28591 const dst_elem_type: Type = .fromInterned(dest_info.child); 28592 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val); 28593 switch (elem_res) { 28594 .ok => {}, 28595 else => { 28596 in_memory_result = .{ .ptr_child = .{ 28597 .child = try elem_res.dupe(sema.arena), 28598 .actual = array_elem_type, 28599 .wanted = dst_elem_type, 28600 } }; 28601 break :src_array_ptr; 28602 }, 28603 } 28604 28605 if (dest_info.sentinel != .none) { 28606 if (array_ty.sentinel(zcu)) |inst_sent| { 28607 if (Air.internedToRef(dest_info.sentinel) != 28608 try sema.coerceInMemory(inst_sent, dst_elem_type)) 28609 { 28610 in_memory_result = .{ .ptr_sentinel = .{ 28611 .actual = inst_sent, 28612 .wanted = Value.fromInterned(dest_info.sentinel), 28613 .ty = dst_elem_type, 28614 } }; 28615 break :src_array_ptr; 28616 } 28617 } else { 28618 in_memory_result = .{ .ptr_sentinel = .{ 28619 .actual = Value.@"unreachable", 28620 .wanted = Value.fromInterned(dest_info.sentinel), 28621 .ty = dst_elem_type, 28622 } }; 28623 break :src_array_ptr; 28624 } 28625 } 28626 28627 switch (dest_info.flags.size) { 28628 .slice => { 28629 // *[N]T to []T 28630 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src); 28631 }, 28632 .c => { 28633 // *[N]T to [*c]T 28634 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28635 }, 28636 .many => { 28637 // *[N]T to [*]T 28638 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28639 }, 28640 .one => unreachable, // early exit at top of block 28641 } 28642 } 28643 28644 // coercion from C pointer 28645 if (inst_ty.isCPtr(zcu)) src_c_ptr: { 28646 if (dest_info.flags.size == .slice) break :src_c_ptr; 28647 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr; 28648 // In this case we must add a safety check because the C pointer 28649 // could be null. 28650 const src_elem_ty = inst_ty.childType(zcu); 28651 const dest_is_mut = !dest_info.flags.is_const; 28652 const dst_elem_type: Type = .fromInterned(dest_info.child); 28653 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) { 28654 .ok => {}, 28655 else => break :src_c_ptr, 28656 } 28657 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28658 } 28659 28660 // cast from *T and [*]T to *anyopaque 28661 // but don't do it if the source type is a double pointer 28662 if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(zcu) == .pointer) to_anyopaque: { 28663 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 28664 const elem_ty = inst_ty.elemType2(zcu); 28665 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) { 28666 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 28667 .actual = inst_ty, 28668 .wanted = dest_ty, 28669 } }; 28670 break :pointer; 28671 } 28672 if (dest_ty.isSlice(zcu)) break :to_anyopaque; 28673 if (inst_ty.isSlice(zcu)) { 28674 in_memory_result = .{ .slice_to_anyopaque = .{ 28675 .actual = inst_ty, 28676 .wanted = dest_ty, 28677 } }; 28678 break :pointer; 28679 } 28680 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28681 } 28682 28683 switch (dest_info.flags.size) { 28684 // coercion to C pointer 28685 .c => switch (inst_ty.zigTypeTag(zcu)) { 28686 .null => return Air.internedToRef(try pt.intern(.{ .ptr = .{ 28687 .ty = dest_ty.toIntern(), 28688 .base_addr = .int, 28689 .byte_offset = 0, 28690 } })), 28691 .comptime_int => { 28692 const addr = sema.coerceExtra(block, .usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 28693 error.NotCoercible => break :pointer, 28694 else => |e| return e, 28695 }; 28696 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 28697 }, 28698 .int => { 28699 const ptr_size_ty: Type = switch (inst_ty.intInfo(zcu).signedness) { 28700 .signed => .isize, 28701 .unsigned => .usize, 28702 }; 28703 const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 28704 error.NotCoercible => { 28705 // Try to give more useful notes 28706 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); 28707 break :pointer; 28708 }, 28709 else => |e| return e, 28710 }; 28711 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 28712 }, 28713 .pointer => p: { 28714 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 28715 const inst_info = inst_ty.ptrInfo(zcu); 28716 switch (try sema.coerceInMemoryAllowed( 28717 block, 28718 .fromInterned(dest_info.child), 28719 .fromInterned(inst_info.child), 28720 !dest_info.flags.is_const, 28721 target, 28722 dest_ty_src, 28723 inst_src, 28724 maybe_inst_val, 28725 )) { 28726 .ok => {}, 28727 else => break :p, 28728 } 28729 if (inst_info.flags.size == .slice) { 28730 assert(dest_info.sentinel == .none); 28731 if (inst_info.sentinel == .none or 28732 inst_info.sentinel != (try pt.intValue(.fromInterned(inst_info.child), 0)).toIntern()) 28733 break :p; 28734 28735 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 28736 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 28737 } 28738 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28739 }, 28740 else => {}, 28741 }, 28742 .one => {}, 28743 .slice => to_slice: { 28744 if (inst_ty.zigTypeTag(zcu) == .array) { 28745 return sema.fail( 28746 block, 28747 inst_src, 28748 "array literal requires address-of operator (&) to coerce to slice type '{f}'", 28749 .{dest_ty.fmt(pt)}, 28750 ); 28751 } 28752 28753 if (!inst_ty.isSinglePointer(zcu)) break :to_slice; 28754 const inst_child_ty = inst_ty.childType(zcu); 28755 if (!inst_child_ty.isTuple(zcu)) break :to_slice; 28756 28757 // empty tuple to zero-length slice 28758 // note that this allows coercing to a mutable slice. 28759 if (inst_child_ty.structFieldCount(zcu) == 0) { 28760 const align_val = try dest_ty.ptrAlignmentSema(pt); 28761 return Air.internedToRef(try pt.intern(.{ .slice = .{ 28762 .ty = dest_ty.toIntern(), 28763 .ptr = try pt.intern(.{ .ptr = .{ 28764 .ty = dest_ty.slicePtrFieldType(zcu).toIntern(), 28765 .base_addr = .int, 28766 .byte_offset = align_val.toByteUnits().?, 28767 } }), 28768 .len = .zero_usize, 28769 } })); 28770 } 28771 28772 // pointer to tuple to slice 28773 if (!dest_info.flags.is_const) { 28774 const err_msg = err_msg: { 28775 const err_msg = try sema.errMsg(inst_src, "cannot cast pointer to tuple to '{f}'", .{dest_ty.fmt(pt)}); 28776 errdefer err_msg.destroy(sema.gpa); 28777 try sema.errNote(dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{}); 28778 break :err_msg err_msg; 28779 }; 28780 return sema.failWithOwnedErrorMsg(block, err_msg); 28781 } 28782 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); 28783 }, 28784 .many => p: { 28785 if (!inst_ty.isSlice(zcu)) break :p; 28786 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 28787 const inst_info = inst_ty.ptrInfo(zcu); 28788 28789 switch (try sema.coerceInMemoryAllowed( 28790 block, 28791 .fromInterned(dest_info.child), 28792 .fromInterned(inst_info.child), 28793 !dest_info.flags.is_const, 28794 target, 28795 dest_ty_src, 28796 inst_src, 28797 maybe_inst_val, 28798 )) { 28799 .ok => {}, 28800 else => break :p, 28801 } 28802 28803 if (dest_info.sentinel == .none or inst_info.sentinel == .none or 28804 Air.internedToRef(dest_info.sentinel) != 28805 try sema.coerceInMemory(Value.fromInterned(inst_info.sentinel), .fromInterned(dest_info.child))) 28806 break :p; 28807 28808 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 28809 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 28810 }, 28811 } 28812 }, 28813 .int, .comptime_int => switch (inst_ty.zigTypeTag(zcu)) { 28814 .float, .comptime_float => float: { 28815 const val = maybe_inst_val orelse { 28816 if (dest_ty.zigTypeTag(zcu) == .comptime_int) { 28817 if (!opts.report_err) return error.NotCoercible; 28818 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int }); 28819 } 28820 break :float; 28821 }; 28822 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact); 28823 return Air.internedToRef(result_val.toIntern()); 28824 }, 28825 .int, .comptime_int => { 28826 if (maybe_inst_val) |val| { 28827 // comptime-known integer to other number 28828 if (!(try sema.intFitsInType(val, dest_ty, null))) { 28829 if (!opts.report_err) return error.NotCoercible; 28830 return sema.fail(block, inst_src, "type '{f}' cannot represent integer value '{f}'", .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) }); 28831 } 28832 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 28833 .undef => try pt.undefRef(dest_ty), 28834 .int => |int| Air.internedToRef( 28835 try zcu.intern_pool.getCoercedInts(zcu.gpa, pt.tid, int, dest_ty.toIntern()), 28836 ), 28837 else => unreachable, 28838 }; 28839 } 28840 if (dest_ty.zigTypeTag(zcu) == .comptime_int) { 28841 if (!opts.report_err) return error.NotCoercible; 28842 if (opts.no_cast_to_comptime_int) return inst; 28843 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int }); 28844 } 28845 28846 // integer widening 28847 const dst_info = dest_ty.intInfo(zcu); 28848 const src_info = inst_ty.intInfo(zcu); 28849 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or 28850 // small enough unsigned ints can get casted to large enough signed ints 28851 (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) 28852 { 28853 try sema.requireRuntimeBlock(block, inst_src, null); 28854 return block.addTyOp(.intcast, dest_ty, inst); 28855 } 28856 }, 28857 else => {}, 28858 }, 28859 .float, .comptime_float => switch (inst_ty.zigTypeTag(zcu)) { 28860 .comptime_float => { 28861 const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); 28862 const result_val = try val.floatCast(dest_ty, pt); 28863 return Air.internedToRef(result_val.toIntern()); 28864 }, 28865 .float => { 28866 if (maybe_inst_val) |val| { 28867 const result_val = try val.floatCast(dest_ty, pt); 28868 if (!val.eql(try result_val.floatCast(inst_ty, pt), inst_ty, zcu)) { 28869 return sema.fail( 28870 block, 28871 inst_src, 28872 "type '{f}' cannot represent float value '{f}'", 28873 .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) }, 28874 ); 28875 } 28876 return Air.internedToRef(result_val.toIntern()); 28877 } else if (dest_ty.zigTypeTag(zcu) == .comptime_float) { 28878 if (!opts.report_err) return error.NotCoercible; 28879 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float }); 28880 } 28881 28882 // float widening 28883 const src_bits = inst_ty.floatBits(target); 28884 const dst_bits = dest_ty.floatBits(target); 28885 if (dst_bits >= src_bits) { 28886 try sema.requireRuntimeBlock(block, inst_src, null); 28887 return block.addTyOp(.fpext, dest_ty, inst); 28888 } 28889 }, 28890 .int, .comptime_int => int: { 28891 const val = maybe_inst_val orelse { 28892 if (dest_ty.zigTypeTag(zcu) == .comptime_float) { 28893 if (!opts.report_err) return error.NotCoercible; 28894 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float }); 28895 } 28896 break :int; 28897 }; 28898 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema); 28899 const fits: bool = switch (ip.indexToKey(result_val.toIntern())) { 28900 else => unreachable, 28901 .undef => true, 28902 .float => |float| fits: { 28903 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; 28904 const operand_big_int = val.toBigInt(&buffer, zcu); 28905 switch (float.storage) { 28906 inline else => |x| { 28907 if (!std.math.isFinite(x)) break :fits false; 28908 var result_big_int: std.math.big.int.Mutable = .{ 28909 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)), 28910 .len = undefined, 28911 .positive = undefined, 28912 }; 28913 switch (result_big_int.setFloat(x, .nearest_even)) { 28914 .inexact => break :fits false, 28915 .exact => {}, 28916 } 28917 break :fits result_big_int.toConst().eql(operand_big_int); 28918 }, 28919 } 28920 }, 28921 }; 28922 if (!fits) return sema.fail( 28923 block, 28924 inst_src, 28925 "type '{f}' cannot represent integer value '{f}'", 28926 .{ dest_ty.fmt(pt), val.fmtValue(pt) }, 28927 ); 28928 return .fromValue(result_val); 28929 }, 28930 else => {}, 28931 }, 28932 .@"enum" => switch (inst_ty.zigTypeTag(zcu)) { 28933 .enum_literal => { 28934 // enum literal to enum 28935 const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); 28936 const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal; 28937 const field_index = dest_ty.enumFieldIndex(string, zcu) orelse { 28938 return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{ 28939 string.fmt(&zcu.intern_pool), dest_ty.fmt(pt), 28940 }); 28941 }; 28942 return Air.internedToRef((try pt.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern()); 28943 }, 28944 .@"union" => blk: { 28945 // union to its own tag type 28946 const union_tag_ty = inst_ty.unionTagType(zcu) orelse break :blk; 28947 if (union_tag_ty.eql(dest_ty, zcu)) { 28948 return sema.unionToTag(block, dest_ty, inst, inst_src); 28949 } 28950 }, 28951 else => {}, 28952 }, 28953 .error_union => switch (inst_ty.zigTypeTag(zcu)) { 28954 .error_union => eu: { 28955 if (maybe_inst_val) |inst_val| { 28956 switch (inst_val.toIntern()) { 28957 .undef => return pt.undefRef(dest_ty), 28958 else => switch (zcu.intern_pool.indexToKey(inst_val.toIntern())) { 28959 .error_union => |error_union| switch (error_union.val) { 28960 .err_name => |err_name| { 28961 const error_set_ty = inst_ty.errorUnionSet(zcu); 28962 const error_set_val = Air.internedToRef((try pt.intern(.{ .err = .{ 28963 .ty = error_set_ty.toIntern(), 28964 .name = err_name, 28965 } }))); 28966 return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src); 28967 }, 28968 .payload => |payload| { 28969 const payload_val = Air.internedToRef(payload); 28970 return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) { 28971 error.NotCoercible => break :eu, 28972 else => |e| return e, 28973 }; 28974 }, 28975 }, 28976 else => unreachable, 28977 }, 28978 } 28979 } 28980 }, 28981 .error_set => { 28982 // E to E!T 28983 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src); 28984 }, 28985 else => eu: { 28986 // T to E!T 28987 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) { 28988 error.NotCoercible => { 28989 if (in_memory_result == .no_match) { 28990 const payload_type = dest_ty.errorUnionPayload(zcu); 28991 // Try to give more useful notes 28992 in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); 28993 } 28994 break :eu; 28995 }, 28996 else => |e| return e, 28997 }; 28998 }, 28999 }, 29000 .@"union" => switch (inst_ty.zigTypeTag(zcu)) { 29001 .@"enum", .enum_literal => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), 29002 else => {}, 29003 }, 29004 .array => switch (inst_ty.zigTypeTag(zcu)) { 29005 .array => array_to_array: { 29006 // Array coercions are allowed only if the child is IMC and the sentinel is unchanged or removed. 29007 if (.ok != try sema.coerceInMemoryAllowed( 29008 block, 29009 dest_ty.childType(zcu), 29010 inst_ty.childType(zcu), 29011 false, 29012 target, 29013 dest_ty_src, 29014 inst_src, 29015 maybe_inst_val, 29016 )) { 29017 break :array_to_array; 29018 } 29019 29020 if (dest_ty.sentinel(zcu)) |dest_sent| { 29021 const src_sent = inst_ty.sentinel(zcu) orelse break :array_to_array; 29022 if (dest_sent.toIntern() != (try pt.getCoerced(src_sent, dest_ty.childType(zcu))).toIntern()) { 29023 break :array_to_array; 29024 } 29025 } 29026 29027 return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src); 29028 }, 29029 .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 29030 .@"struct" => { 29031 if (inst_ty.isTuple(zcu)) { 29032 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 29033 } 29034 }, 29035 else => {}, 29036 }, 29037 .vector => switch (inst_ty.zigTypeTag(zcu)) { 29038 .array, .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 29039 .@"struct" => { 29040 if (inst_ty.isTuple(zcu)) { 29041 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 29042 } 29043 }, 29044 else => {}, 29045 }, 29046 .@"struct" => blk: { 29047 if (dest_ty.isTuple(zcu) and inst_ty.isTuple(zcu)) { 29048 return sema.coerceTupleToTuple(block, dest_ty, inst, inst_src) catch |err| switch (err) { 29049 error.NotCoercible => break :blk, 29050 else => |e| return e, 29051 }; 29052 } 29053 }, 29054 else => {}, 29055 } 29056 29057 const can_coerce_to = switch (dest_ty.zigTypeTag(zcu)) { 29058 .noreturn, .@"opaque" => false, 29059 else => true, 29060 }; 29061 29062 if (can_coerce_to) { 29063 // undefined to anything. We do this after the big switch above so that 29064 // special logic has a chance to run first, such as `*[N]T` to `[]T` which 29065 // should initialize the length field of the slice. 29066 if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty); 29067 } 29068 29069 if (!opts.report_err) return error.NotCoercible; 29070 29071 if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) { 29072 const msg = msg: { 29073 const msg = try sema.errMsg(inst_src, "function declared 'noreturn' returns", .{}); 29074 errdefer msg.destroy(sema.gpa); 29075 29076 const ret_ty_src: LazySrcLoc = .{ 29077 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), 29078 .offset = .{ .node_offset_fn_type_ret_ty = .zero }, 29079 }; 29080 try sema.errNote(ret_ty_src, msg, "'noreturn' declared here", .{}); 29081 break :msg msg; 29082 }; 29083 return sema.failWithOwnedErrorMsg(block, msg); 29084 } 29085 29086 const msg = msg: { 29087 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), inst_ty.fmt(pt) }); 29088 errdefer msg.destroy(sema.gpa); 29089 29090 if (!can_coerce_to) { 29091 try sema.errNote(inst_src, msg, "cannot coerce to '{f}'", .{dest_ty.fmt(pt)}); 29092 } 29093 29094 // E!T to T 29095 if (inst_ty.zigTypeTag(zcu) == .error_union and 29096 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok) 29097 { 29098 try sema.errNote(inst_src, msg, "cannot convert error union to payload type", .{}); 29099 try sema.errNote(inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); 29100 } 29101 29102 // ?T to T 29103 if (inst_ty.zigTypeTag(zcu) == .optional and 29104 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok) 29105 { 29106 try sema.errNote(inst_src, msg, "cannot convert optional to payload type", .{}); 29107 try sema.errNote(inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 29108 } 29109 29110 try in_memory_result.report(sema, inst_src, msg); 29111 29112 // Add notes about function return type 29113 if (opts.is_ret and 29114 !zcu.test_functions.contains(zcu.funcInfo(sema.func_index).owner_nav)) 29115 { 29116 const ret_ty_src: LazySrcLoc = .{ 29117 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), 29118 .offset = .{ .node_offset_fn_type_ret_ty = .zero }, 29119 }; 29120 if (inst_ty.isError(zcu) and !dest_ty.isError(zcu)) { 29121 try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{}); 29122 } else { 29123 try sema.errNote(ret_ty_src, msg, "function return type declared here", .{}); 29124 } 29125 } 29126 29127 if (try opts.param_src.get(sema)) |param_src| { 29128 try sema.errNote(param_src, msg, "parameter type declared here", .{}); 29129 } 29130 29131 // TODO maybe add "cannot store an error in type '{f}'" note 29132 29133 break :msg msg; 29134 }; 29135 return sema.failWithOwnedErrorMsg(block, msg); 29136 } 29137 29138 fn coerceInMemory( 29139 sema: *Sema, 29140 val: Value, 29141 dst_ty: Type, 29142 ) CompileError!Air.Inst.Ref { 29143 return Air.internedToRef((try sema.pt.getCoerced(val, dst_ty)).toIntern()); 29144 } 29145 29146 const InMemoryCoercionResult = union(enum) { 29147 ok, 29148 no_match: Pair, 29149 int_not_coercible: Int, 29150 comptime_int_not_coercible: TypeValuePair, 29151 error_union_payload: PairAndChild, 29152 array_len: IntPair, 29153 array_sentinel: Sentinel, 29154 array_elem: PairAndChild, 29155 vector_len: IntPair, 29156 vector_elem: PairAndChild, 29157 optional_shape: Pair, 29158 optional_child: PairAndChild, 29159 from_anyerror, 29160 missing_error: []const InternPool.NullTerminatedString, 29161 /// true if wanted is var args 29162 fn_var_args: bool, 29163 /// true if wanted is generic 29164 fn_generic: bool, 29165 fn_param_count: IntPair, 29166 fn_param_noalias: IntPair, 29167 fn_param_comptime: ComptimeParam, 29168 fn_param: Param, 29169 fn_cc: CC, 29170 fn_return_type: PairAndChild, 29171 ptr_child: PairAndChild, 29172 ptr_addrspace: AddressSpace, 29173 ptr_sentinel: Sentinel, 29174 ptr_size: Size, 29175 ptr_const: Pair, 29176 ptr_volatile: Pair, 29177 ptr_allowzero: Pair, 29178 ptr_bit_range: BitRange, 29179 ptr_alignment: AlignPair, 29180 double_ptr_to_anyopaque: Pair, 29181 slice_to_anyopaque: Pair, 29182 29183 const Pair = struct { 29184 actual: Type, 29185 wanted: Type, 29186 }; 29187 29188 const TypeValuePair = struct { 29189 actual: Value, 29190 wanted: Type, 29191 }; 29192 29193 const PairAndChild = struct { 29194 child: *InMemoryCoercionResult, 29195 actual: Type, 29196 wanted: Type, 29197 }; 29198 29199 const Param = struct { 29200 child: *InMemoryCoercionResult, 29201 actual: Type, 29202 wanted: Type, 29203 index: u64, 29204 }; 29205 29206 const ComptimeParam = struct { 29207 index: u64, 29208 wanted: bool, 29209 }; 29210 29211 const Sentinel = struct { 29212 // unreachable_value indicates no sentinel 29213 actual: Value, 29214 wanted: Value, 29215 ty: Type, 29216 }; 29217 29218 const Int = struct { 29219 actual_signedness: std.builtin.Signedness, 29220 wanted_signedness: std.builtin.Signedness, 29221 actual_bits: u16, 29222 wanted_bits: u16, 29223 }; 29224 29225 const IntPair = struct { 29226 actual: u64, 29227 wanted: u64, 29228 }; 29229 29230 const AlignPair = struct { 29231 actual: Alignment, 29232 wanted: Alignment, 29233 }; 29234 29235 const Size = struct { 29236 actual: std.builtin.Type.Pointer.Size, 29237 wanted: std.builtin.Type.Pointer.Size, 29238 }; 29239 29240 const AddressSpace = struct { 29241 actual: std.builtin.AddressSpace, 29242 wanted: std.builtin.AddressSpace, 29243 }; 29244 29245 const CC = struct { 29246 actual: std.builtin.CallingConvention, 29247 wanted: std.builtin.CallingConvention, 29248 }; 29249 29250 const BitRange = struct { 29251 actual_host: u16, 29252 wanted_host: u16, 29253 actual_offset: u16, 29254 wanted_offset: u16, 29255 }; 29256 29257 fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult { 29258 const res = try arena.create(InMemoryCoercionResult); 29259 res.* = child.*; 29260 return res; 29261 } 29262 29263 fn report(res: *const InMemoryCoercionResult, sema: *Sema, src: LazySrcLoc, msg: *Zcu.ErrorMsg) !void { 29264 const pt = sema.pt; 29265 var cur = res; 29266 while (true) switch (cur.*) { 29267 .ok => unreachable, 29268 .no_match => |types| { 29269 try sema.addDeclaredHereNote(msg, types.wanted); 29270 try sema.addDeclaredHereNote(msg, types.actual); 29271 break; 29272 }, 29273 .int_not_coercible => |int| { 29274 try sema.errNote(src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{ 29275 @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits, 29276 }); 29277 break; 29278 }, 29279 .comptime_int_not_coercible => |int| { 29280 try sema.errNote(src, msg, "type '{f}' cannot represent value '{f}'", .{ 29281 int.wanted.fmt(pt), int.actual.fmtValueSema(pt, sema), 29282 }); 29283 break; 29284 }, 29285 .error_union_payload => |pair| { 29286 try sema.errNote(src, msg, "error union payload '{f}' cannot cast into error union payload '{f}'", .{ 29287 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29288 }); 29289 cur = pair.child; 29290 }, 29291 .array_len => |lens| { 29292 try sema.errNote(src, msg, "array of length {d} cannot cast into an array of length {d}", .{ 29293 lens.actual, lens.wanted, 29294 }); 29295 break; 29296 }, 29297 .array_sentinel => |sentinel| { 29298 if (sentinel.actual.toIntern() != .unreachable_value) { 29299 try sema.errNote(src, msg, "array sentinel '{f}' cannot cast into array sentinel '{f}'", .{ 29300 sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema), 29301 }); 29302 } else { 29303 try sema.errNote(src, msg, "destination array requires '{f}' sentinel", .{ 29304 sentinel.wanted.fmtValueSema(pt, sema), 29305 }); 29306 } 29307 break; 29308 }, 29309 .array_elem => |pair| { 29310 try sema.errNote(src, msg, "array element type '{f}' cannot cast into array element type '{f}'", .{ 29311 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29312 }); 29313 cur = pair.child; 29314 }, 29315 .vector_len => |lens| { 29316 try sema.errNote(src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{ 29317 lens.actual, lens.wanted, 29318 }); 29319 break; 29320 }, 29321 .vector_elem => |pair| { 29322 try sema.errNote(src, msg, "vector element type '{f}' cannot cast into vector element type '{f}'", .{ 29323 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29324 }); 29325 cur = pair.child; 29326 }, 29327 .optional_shape => |pair| { 29328 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{ 29329 pair.actual.optionalChild(pt.zcu).fmt(pt), pair.wanted.optionalChild(pt.zcu).fmt(pt), 29330 }); 29331 break; 29332 }, 29333 .optional_child => |pair| { 29334 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{ 29335 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29336 }); 29337 cur = pair.child; 29338 }, 29339 .from_anyerror => { 29340 try sema.errNote(src, msg, "global error set cannot cast into a smaller set", .{}); 29341 break; 29342 }, 29343 .missing_error => |missing_errors| { 29344 for (missing_errors) |err| { 29345 try sema.errNote(src, msg, "'error.{f}' not a member of destination error set", .{err.fmt(&pt.zcu.intern_pool)}); 29346 } 29347 break; 29348 }, 29349 .fn_var_args => |wanted_var_args| { 29350 if (wanted_var_args) { 29351 try sema.errNote(src, msg, "non-variadic function cannot cast into a variadic function", .{}); 29352 } else { 29353 try sema.errNote(src, msg, "variadic function cannot cast into a non-variadic function", .{}); 29354 } 29355 break; 29356 }, 29357 .fn_generic => |wanted_generic| { 29358 if (wanted_generic) { 29359 try sema.errNote(src, msg, "non-generic function cannot cast into a generic function", .{}); 29360 } else { 29361 try sema.errNote(src, msg, "generic function cannot cast into a non-generic function", .{}); 29362 } 29363 break; 29364 }, 29365 .fn_param_count => |lens| { 29366 try sema.errNote(src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{ 29367 lens.actual, lens.wanted, 29368 }); 29369 break; 29370 }, 29371 .fn_param_noalias => |param| { 29372 var index: u6 = 0; 29373 var actual_noalias = false; 29374 while (true) : (index += 1) { 29375 const actual: u1 = @truncate(param.actual >> index); 29376 const wanted: u1 = @truncate(param.wanted >> index); 29377 if (actual != wanted) { 29378 actual_noalias = actual == 1; 29379 break; 29380 } 29381 } 29382 if (!actual_noalias) { 29383 try sema.errNote(src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index}); 29384 } else { 29385 try sema.errNote(src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index}); 29386 } 29387 break; 29388 }, 29389 .fn_param_comptime => |param| { 29390 if (param.wanted) { 29391 try sema.errNote(src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index}); 29392 } else { 29393 try sema.errNote(src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index}); 29394 } 29395 break; 29396 }, 29397 .fn_param => |param| { 29398 try sema.errNote(src, msg, "parameter {d} '{f}' cannot cast into '{f}'", .{ 29399 param.index, param.actual.fmt(pt), param.wanted.fmt(pt), 29400 }); 29401 cur = param.child; 29402 }, 29403 .fn_cc => |cc| { 29404 try sema.errNote(src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) }); 29405 break; 29406 }, 29407 .fn_return_type => |pair| { 29408 try sema.errNote(src, msg, "return type '{f}' cannot cast into return type '{f}'", .{ 29409 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29410 }); 29411 cur = pair.child; 29412 }, 29413 .ptr_child => |pair| { 29414 try sema.errNote(src, msg, "pointer type child '{f}' cannot cast into pointer type child '{f}'", .{ 29415 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29416 }); 29417 cur = pair.child; 29418 }, 29419 .ptr_addrspace => |@"addrspace"| { 29420 try sema.errNote(src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) }); 29421 break; 29422 }, 29423 .ptr_sentinel => |sentinel| { 29424 if (sentinel.actual.toIntern() != .unreachable_value) { 29425 try sema.errNote(src, msg, "pointer sentinel '{f}' cannot cast into pointer sentinel '{f}'", .{ 29426 sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema), 29427 }); 29428 } else { 29429 try sema.errNote(src, msg, "destination pointer requires '{f}' sentinel", .{ 29430 sentinel.wanted.fmtValueSema(pt, sema), 29431 }); 29432 } 29433 break; 29434 }, 29435 .ptr_size => |size| { 29436 try sema.errNote(src, msg, "a {s} cannot cast into a {s}", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) }); 29437 break; 29438 }, 29439 .ptr_allowzero => |pair| { 29440 const wanted_allow_zero = pair.wanted.ptrAllowsZero(pt.zcu); 29441 const actual_allow_zero = pair.actual.ptrAllowsZero(pt.zcu); 29442 if (actual_allow_zero and !wanted_allow_zero) { 29443 try sema.errNote(src, msg, "'{f}' could have null values which are illegal in type '{f}'", .{ 29444 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29445 }); 29446 } else { 29447 try sema.errNote(src, msg, "mutable '{f}' would allow illegal null values stored to type '{f}'", .{ 29448 pair.wanted.fmt(pt), pair.actual.fmt(pt), 29449 }); 29450 } 29451 break; 29452 }, 29453 .ptr_const => |pair| { 29454 const wanted_const = pair.wanted.isConstPtr(pt.zcu); 29455 const actual_const = pair.actual.isConstPtr(pt.zcu); 29456 if (actual_const and !wanted_const) { 29457 try sema.errNote(src, msg, "cast discards const qualifier", .{}); 29458 } else { 29459 try sema.errNote(src, msg, "mutable '{f}' would allow illegal const pointers stored to type '{f}'", .{ 29460 pair.wanted.fmt(pt), pair.actual.fmt(pt), 29461 }); 29462 } 29463 break; 29464 }, 29465 .ptr_volatile => |pair| { 29466 const wanted_volatile = pair.wanted.isVolatilePtr(pt.zcu); 29467 const actual_volatile = pair.actual.isVolatilePtr(pt.zcu); 29468 if (actual_volatile and !wanted_volatile) { 29469 try sema.errNote(src, msg, "cast discards volatile qualifier", .{}); 29470 } else { 29471 try sema.errNote(src, msg, "mutable '{f}' would allow illegal volatile pointers stored to type '{f}'", .{ 29472 pair.wanted.fmt(pt), pair.actual.fmt(pt), 29473 }); 29474 } 29475 break; 29476 }, 29477 .ptr_bit_range => |bit_range| { 29478 if (bit_range.actual_host != bit_range.wanted_host) { 29479 try sema.errNote(src, msg, "pointer host size '{d}' cannot cast into pointer host size '{d}'", .{ 29480 bit_range.actual_host, bit_range.wanted_host, 29481 }); 29482 } 29483 if (bit_range.actual_offset != bit_range.wanted_offset) { 29484 try sema.errNote(src, msg, "pointer bit offset '{d}' cannot cast into pointer bit offset '{d}'", .{ 29485 bit_range.actual_offset, bit_range.wanted_offset, 29486 }); 29487 } 29488 break; 29489 }, 29490 .ptr_alignment => |pair| { 29491 try sema.errNote(src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{ 29492 pair.actual.toByteUnits() orelse 0, pair.wanted.toByteUnits() orelse 0, 29493 }); 29494 break; 29495 }, 29496 .double_ptr_to_anyopaque => |pair| { 29497 try sema.errNote(src, msg, "cannot implicitly cast double pointer '{f}' to anyopaque pointer '{f}'", .{ 29498 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29499 }); 29500 break; 29501 }, 29502 .slice_to_anyopaque => |pair| { 29503 try sema.errNote(src, msg, "cannot implicitly cast slice '{f}' to anyopaque pointer '{f}'", .{ 29504 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29505 }); 29506 try sema.errNote(src, msg, "consider using '.ptr'", .{}); 29507 break; 29508 }, 29509 }; 29510 } 29511 }; 29512 29513 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 { 29514 return switch (size) { 29515 .one => "single pointer", 29516 .many => "many pointer", 29517 .c => "C pointer", 29518 .slice => "slice", 29519 }; 29520 } 29521 29522 /// If types `A` and `B` have identical representations in runtime memory, they are considered 29523 /// "in-memory coercible". This is a subset of normal coercions. Not only can `A` coerce to `B`, but 29524 /// also, coercions can happen through pointers. For instance, `*const A` can coerce to `*const B`. 29525 /// 29526 /// If this function is called, the coercion must be applied, or a compile error emitted if `.ok` 29527 /// is not returned. This is because this function may modify inferred error sets to make a 29528 /// coercion possible, even if `.ok` is not returned. 29529 pub fn coerceInMemoryAllowed( 29530 sema: *Sema, 29531 block: *Block, 29532 dest_ty: Type, 29533 src_ty: Type, 29534 /// If `true`, this query comes from an attempted coercion of the form `*Src` -> `*Dest`, where 29535 /// both pointers are mutable. If this coercion is allowed, one could store to the `*Dest` and 29536 /// load from the `*Src` to effectively perform an in-memory coercion from `Dest` to `Src`. 29537 /// Therefore, when `dest_is_mut`, the in-memory coercion must be valid in *both directions*. 29538 dest_is_mut: bool, 29539 target: *const std.Target, 29540 dest_src: LazySrcLoc, 29541 src_src: LazySrcLoc, 29542 src_val: ?Value, 29543 ) CompileError!InMemoryCoercionResult { 29544 const pt = sema.pt; 29545 const zcu = pt.zcu; 29546 29547 if (dest_ty.eql(src_ty, zcu)) 29548 return .ok; 29549 29550 const dest_tag = dest_ty.zigTypeTag(zcu); 29551 const src_tag = src_ty.zigTypeTag(zcu); 29552 29553 // Differently-named integers with the same number of bits. 29554 if (dest_tag == .int and src_tag == .int) { 29555 const dest_info = dest_ty.intInfo(zcu); 29556 const src_info = src_ty.intInfo(zcu); 29557 29558 if (dest_info.signedness == src_info.signedness and 29559 dest_info.bits == src_info.bits) 29560 { 29561 return .ok; 29562 } 29563 29564 if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or 29565 // small enough unsigned ints can get casted to large enough signed ints 29566 (dest_info.signedness == .signed and src_info.signedness == .unsigned and dest_info.bits <= src_info.bits) or 29567 (dest_info.signedness == .unsigned and src_info.signedness == .signed)) 29568 { 29569 return InMemoryCoercionResult{ .int_not_coercible = .{ 29570 .actual_signedness = src_info.signedness, 29571 .wanted_signedness = dest_info.signedness, 29572 .actual_bits = src_info.bits, 29573 .wanted_bits = dest_info.bits, 29574 } }; 29575 } 29576 } 29577 29578 // Comptime int to regular int. 29579 if (dest_tag == .int and src_tag == .comptime_int) { 29580 if (src_val) |val| { 29581 if (!(try sema.intFitsInType(val, dest_ty, null))) { 29582 return .{ .comptime_int_not_coercible = .{ .wanted = dest_ty, .actual = val } }; 29583 } 29584 } 29585 } 29586 29587 // Differently-named floats with the same number of bits. 29588 if (dest_tag == .float and src_tag == .float) { 29589 const dest_bits = dest_ty.floatBits(target); 29590 const src_bits = src_ty.floatBits(target); 29591 if (dest_bits == src_bits) { 29592 return .ok; 29593 } 29594 } 29595 29596 // Pointers / Pointer-like Optionals 29597 const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty); 29598 const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty); 29599 if (maybe_dest_ptr_ty) |dest_ptr_ty| { 29600 if (maybe_src_ptr_ty) |src_ptr_ty| { 29601 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src); 29602 } 29603 } 29604 29605 // Slices 29606 if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) { 29607 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); 29608 } 29609 29610 // Functions 29611 if (dest_tag == .@"fn" and src_tag == .@"fn") { 29612 return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); 29613 } 29614 29615 // Error Unions 29616 if (dest_tag == .error_union and src_tag == .error_union) { 29617 const dest_payload = dest_ty.errorUnionPayload(zcu); 29618 const src_payload = src_ty.errorUnionPayload(zcu); 29619 const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src, null); 29620 if (child != .ok) { 29621 return .{ .error_union_payload = .{ 29622 .child = try child.dupe(sema.arena), 29623 .actual = src_payload, 29624 .wanted = dest_payload, 29625 } }; 29626 } 29627 return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(zcu), src_ty.errorUnionSet(zcu), dest_is_mut, target, dest_src, src_src, null); 29628 } 29629 29630 // Error Sets 29631 if (dest_tag == .error_set and src_tag == .error_set) { 29632 const res1 = try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src); 29633 if (!dest_is_mut or res1 != .ok) return res1; 29634 // src -> dest is okay, but `dest_is_mut`, so it needs to be allowed in the other direction. 29635 const res2 = try sema.coerceInMemoryAllowedErrorSets(block, src_ty, dest_ty, src_src, dest_src); 29636 return res2; 29637 } 29638 29639 // Arrays 29640 if (dest_tag == .array and src_tag == .array) { 29641 const dest_info = dest_ty.arrayInfo(zcu); 29642 const src_info = src_ty.arrayInfo(zcu); 29643 if (dest_info.len != src_info.len) { 29644 return .{ .array_len = .{ 29645 .actual = src_info.len, 29646 .wanted = dest_info.len, 29647 } }; 29648 } 29649 29650 const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src, null); 29651 switch (child) { 29652 .ok => {}, 29653 .no_match => return child, 29654 else => { 29655 return .{ .array_elem = .{ 29656 .child = try child.dupe(sema.arena), 29657 .actual = src_info.elem_type, 29658 .wanted = dest_info.elem_type, 29659 } }; 29660 }, 29661 } 29662 const ok_sent = (dest_info.sentinel == null and src_info.sentinel == null) or 29663 (src_info.sentinel != null and 29664 dest_info.sentinel != null and 29665 dest_info.sentinel.?.eql( 29666 try pt.getCoerced(src_info.sentinel.?, dest_info.elem_type), 29667 dest_info.elem_type, 29668 zcu, 29669 )); 29670 if (!ok_sent) { 29671 return .{ .array_sentinel = .{ 29672 .actual = src_info.sentinel orelse Value.@"unreachable", 29673 .wanted = dest_info.sentinel orelse Value.@"unreachable", 29674 .ty = dest_info.elem_type, 29675 } }; 29676 } 29677 return .ok; 29678 } 29679 29680 // Vectors 29681 if (dest_tag == .vector and src_tag == .vector) { 29682 const dest_len = dest_ty.vectorLen(zcu); 29683 const src_len = src_ty.vectorLen(zcu); 29684 if (dest_len != src_len) { 29685 return .{ .vector_len = .{ 29686 .actual = src_len, 29687 .wanted = dest_len, 29688 } }; 29689 } 29690 29691 const dest_elem_ty = dest_ty.scalarType(zcu); 29692 const src_elem_ty = src_ty.scalarType(zcu); 29693 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null); 29694 if (child != .ok) { 29695 return .{ .vector_elem = .{ 29696 .child = try child.dupe(sema.arena), 29697 .actual = src_elem_ty, 29698 .wanted = dest_elem_ty, 29699 } }; 29700 } 29701 29702 return .ok; 29703 } 29704 29705 // Optionals 29706 if (dest_tag == .optional and src_tag == .optional) { 29707 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) { 29708 return .{ .optional_shape = .{ 29709 .actual = src_ty, 29710 .wanted = dest_ty, 29711 } }; 29712 } 29713 const dest_child_type = dest_ty.optionalChild(zcu); 29714 const src_child_type = src_ty.optionalChild(zcu); 29715 29716 const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src, null); 29717 if (child != .ok) { 29718 return .{ .optional_child = .{ 29719 .child = try child.dupe(sema.arena), 29720 .actual = src_child_type, 29721 .wanted = dest_child_type, 29722 } }; 29723 } 29724 29725 return .ok; 29726 } 29727 29728 // Tuples (with in-memory-coercible fields) 29729 if (dest_ty.isTuple(zcu) and src_ty.isTuple(zcu)) tuple: { 29730 if (dest_ty.structFieldCount(zcu) != src_ty.structFieldCount(zcu)) break :tuple; 29731 const field_count = dest_ty.structFieldCount(zcu); 29732 for (0..field_count) |field_idx| { 29733 if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple; 29734 if (dest_ty.fieldAlignment(field_idx, zcu) != src_ty.fieldAlignment(field_idx, zcu)) break :tuple; 29735 const dest_field_ty = dest_ty.fieldType(field_idx, zcu); 29736 const src_field_ty = src_ty.fieldType(field_idx, zcu); 29737 const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null); 29738 if (field != .ok) break :tuple; 29739 } 29740 return .ok; 29741 } 29742 29743 return .{ .no_match = .{ 29744 .actual = dest_ty, 29745 .wanted = src_ty, 29746 } }; 29747 } 29748 29749 fn coerceInMemoryAllowedErrorSets( 29750 sema: *Sema, 29751 block: *Block, 29752 dest_ty: Type, 29753 src_ty: Type, 29754 dest_src: LazySrcLoc, 29755 src_src: LazySrcLoc, 29756 ) !InMemoryCoercionResult { 29757 const pt = sema.pt; 29758 const zcu = pt.zcu; 29759 const gpa = sema.gpa; 29760 const ip = &zcu.intern_pool; 29761 29762 // Coercion to `anyerror`. Note that this check can return false negatives 29763 // in case the error sets did not get resolved. 29764 if (dest_ty.isAnyError(zcu)) { 29765 return .ok; 29766 } 29767 29768 if (dest_ty.toIntern() == .adhoc_inferred_error_set_type) { 29769 // We are trying to coerce an error set to the current function's 29770 // inferred error set. 29771 const dst_ies = sema.fn_ret_ty_ies.?; 29772 try dst_ies.addErrorSet(src_ty, ip, sema.arena); 29773 return .ok; 29774 } 29775 29776 if (ip.isInferredErrorSetType(dest_ty.toIntern())) { 29777 const dst_ies_func_index = ip.iesFuncIndex(dest_ty.toIntern()); 29778 if (sema.fn_ret_ty_ies) |dst_ies| { 29779 if (dst_ies.func == dst_ies_func_index) { 29780 // We are trying to coerce an error set to the current function's 29781 // inferred error set. 29782 try dst_ies.addErrorSet(src_ty, ip, sema.arena); 29783 return .ok; 29784 } 29785 } 29786 switch (try sema.resolveInferredErrorSet(block, dest_src, dest_ty.toIntern())) { 29787 // isAnyError might have changed from a false negative to a true 29788 // positive after resolution. 29789 .anyerror_type => return .ok, 29790 else => {}, 29791 } 29792 } 29793 29794 var missing_error_buf = std.array_list.Managed(InternPool.NullTerminatedString).init(gpa); 29795 defer missing_error_buf.deinit(); 29796 29797 switch (src_ty.toIntern()) { 29798 .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { 29799 .simple_type => unreachable, // filtered out above 29800 .error_set_type, .inferred_error_set_type => return .from_anyerror, 29801 else => unreachable, 29802 }, 29803 29804 else => switch (ip.indexToKey(src_ty.toIntern())) { 29805 .inferred_error_set_type => { 29806 const resolved_src_ty = try sema.resolveInferredErrorSet(block, src_src, src_ty.toIntern()); 29807 // src anyerror status might have changed after the resolution. 29808 if (resolved_src_ty == .anyerror_type) { 29809 // dest_ty.isAnyError(zcu) == true is already checked for at this point. 29810 return .from_anyerror; 29811 } 29812 29813 for (ip.indexToKey(resolved_src_ty).error_set_type.names.get(ip)) |key| { 29814 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) { 29815 try missing_error_buf.append(key); 29816 } 29817 } 29818 29819 if (missing_error_buf.items.len != 0) { 29820 return InMemoryCoercionResult{ 29821 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 29822 }; 29823 } 29824 29825 return .ok; 29826 }, 29827 .error_set_type => |error_set_type| { 29828 for (error_set_type.names.get(ip)) |name| { 29829 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) { 29830 try missing_error_buf.append(name); 29831 } 29832 } 29833 29834 if (missing_error_buf.items.len != 0) { 29835 return InMemoryCoercionResult{ 29836 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 29837 }; 29838 } 29839 29840 return .ok; 29841 }, 29842 else => unreachable, 29843 }, 29844 } 29845 } 29846 29847 fn coerceInMemoryAllowedFns( 29848 sema: *Sema, 29849 block: *Block, 29850 dest_ty: Type, 29851 src_ty: Type, 29852 /// If set, the coercion must be valid in both directions. 29853 dest_is_mut: bool, 29854 target: *const std.Target, 29855 dest_src: LazySrcLoc, 29856 src_src: LazySrcLoc, 29857 ) !InMemoryCoercionResult { 29858 const pt = sema.pt; 29859 const zcu = pt.zcu; 29860 const ip = &zcu.intern_pool; 29861 29862 const dest_info = zcu.typeToFunc(dest_ty).?; 29863 const src_info = zcu.typeToFunc(src_ty).?; 29864 29865 { 29866 if (dest_info.is_var_args != src_info.is_var_args) { 29867 return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; 29868 } 29869 29870 if (dest_info.is_generic != src_info.is_generic) { 29871 return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; 29872 } 29873 29874 const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and 29875 (!dest_is_mut or callconvCoerceAllowed(target, dest_info.cc, src_info.cc)); 29876 29877 if (!callconv_ok) { 29878 return .{ .fn_cc = .{ 29879 .actual = src_info.cc, 29880 .wanted = dest_info.cc, 29881 } }; 29882 } 29883 29884 if (!switch (src_info.return_type) { 29885 .generic_poison_type => true, 29886 .noreturn_type => !dest_is_mut, 29887 else => false, 29888 }) { 29889 const rt = try sema.coerceInMemoryAllowed( 29890 block, 29891 .fromInterned(dest_info.return_type), 29892 .fromInterned(src_info.return_type), 29893 dest_is_mut, 29894 target, 29895 dest_src, 29896 src_src, 29897 null, 29898 ); 29899 if (rt != .ok) return .{ .fn_return_type = .{ 29900 .child = try rt.dupe(sema.arena), 29901 .actual = .fromInterned(src_info.return_type), 29902 .wanted = .fromInterned(dest_info.return_type), 29903 } }; 29904 } 29905 } 29906 29907 const params_len = params_len: { 29908 if (dest_info.param_types.len != src_info.param_types.len) { 29909 return .{ .fn_param_count = .{ 29910 .actual = src_info.param_types.len, 29911 .wanted = dest_info.param_types.len, 29912 } }; 29913 } 29914 29915 if (dest_info.noalias_bits != src_info.noalias_bits) { 29916 return .{ .fn_param_noalias = .{ 29917 .actual = src_info.noalias_bits, 29918 .wanted = dest_info.noalias_bits, 29919 } }; 29920 } 29921 29922 break :params_len dest_info.param_types.len; 29923 }; 29924 29925 for (0..params_len) |param_i| { 29926 const dest_param_ty: Type = .fromInterned(dest_info.param_types.get(ip)[param_i]); 29927 const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]); 29928 29929 comptime_param: { 29930 const src_is_comptime = src_info.paramIsComptime(@intCast(param_i)); 29931 const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i)); 29932 if (src_is_comptime == dest_is_comptime) break :comptime_param; 29933 if (!dest_is_mut and src_is_comptime and !dest_is_comptime and try dest_param_ty.comptimeOnlySema(pt)) { 29934 // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only. 29935 // The function remains generic, and the parameter is going to be comptime-resolved either way, 29936 // so this just affects whether or not the argument is comptime-evaluated at the call site. 29937 break :comptime_param; 29938 } 29939 return .{ .fn_param_comptime = .{ 29940 .index = param_i, 29941 .wanted = dest_is_comptime, 29942 } }; 29943 } 29944 29945 if (!src_param_ty.isGenericPoison() and !dest_param_ty.isGenericPoison()) { 29946 // Note: Cast direction is reversed here. 29947 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, dest_is_mut, target, dest_src, src_src, null); 29948 if (param != .ok) { 29949 return .{ .fn_param = .{ 29950 .child = try param.dupe(sema.arena), 29951 .actual = src_param_ty, 29952 .wanted = dest_param_ty, 29953 .index = param_i, 29954 } }; 29955 } 29956 } 29957 } 29958 29959 return .ok; 29960 } 29961 29962 fn callconvCoerceAllowed( 29963 target: *const std.Target, 29964 src_cc: std.builtin.CallingConvention, 29965 dest_cc: std.builtin.CallingConvention, 29966 ) bool { 29967 const Tag = std.builtin.CallingConvention.Tag; 29968 if (@as(Tag, src_cc) != @as(Tag, dest_cc)) return false; 29969 29970 switch (src_cc) { 29971 inline else => |src_data, tag| { 29972 const dest_data = @field(dest_cc, @tagName(tag)); 29973 if (@TypeOf(src_data) != void) { 29974 const default_stack_align = target.stackAlignment(); 29975 const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align; 29976 const dest_stack_align = src_data.incoming_stack_alignment orelse default_stack_align; 29977 if (dest_stack_align < src_stack_align) return false; 29978 } 29979 switch (@TypeOf(src_data)) { 29980 void, std.builtin.CallingConvention.CommonOptions => {}, 29981 std.builtin.CallingConvention.X86RegparmOptions => { 29982 if (src_data.register_params != dest_data.register_params) return false; 29983 }, 29984 std.builtin.CallingConvention.ArmInterruptOptions => { 29985 if (src_data.type != dest_data.type) return false; 29986 }, 29987 std.builtin.CallingConvention.MipsInterruptOptions => { 29988 if (src_data.mode != dest_data.mode) return false; 29989 }, 29990 std.builtin.CallingConvention.RiscvInterruptOptions => { 29991 if (src_data.mode != dest_data.mode) return false; 29992 }, 29993 else => comptime unreachable, 29994 } 29995 }, 29996 } 29997 return true; 29998 } 29999 30000 fn coerceInMemoryAllowedPtrs( 30001 sema: *Sema, 30002 block: *Block, 30003 dest_ty: Type, 30004 src_ty: Type, 30005 dest_ptr_ty: Type, 30006 src_ptr_ty: Type, 30007 /// If set, the coercion must be valid in both directions. 30008 dest_is_mut: bool, 30009 target: *const std.Target, 30010 dest_src: LazySrcLoc, 30011 src_src: LazySrcLoc, 30012 ) !InMemoryCoercionResult { 30013 const pt = sema.pt; 30014 const zcu = pt.zcu; 30015 const dest_info = dest_ptr_ty.ptrInfo(zcu); 30016 const src_info = src_ptr_ty.ptrInfo(zcu); 30017 30018 const ok_ptr_size = src_info.flags.size == dest_info.flags.size or 30019 src_info.flags.size == .c or dest_info.flags.size == .c; 30020 if (!ok_ptr_size) { 30021 return InMemoryCoercionResult{ .ptr_size = .{ 30022 .actual = src_info.flags.size, 30023 .wanted = dest_info.flags.size, 30024 } }; 30025 } 30026 30027 const ok_const = src_info.flags.is_const == dest_info.flags.is_const or 30028 (!dest_is_mut and dest_info.flags.is_const); 30029 30030 if (!ok_const) return .{ .ptr_const = .{ 30031 .actual = src_ty, 30032 .wanted = dest_ty, 30033 } }; 30034 30035 const ok_volatile = src_info.flags.is_volatile == dest_info.flags.is_volatile or 30036 (!dest_is_mut and dest_info.flags.is_volatile); 30037 30038 if (!ok_volatile) return .{ .ptr_volatile = .{ 30039 .actual = src_ty, 30040 .wanted = dest_ty, 30041 } }; 30042 30043 const dest_allowzero = dest_ty.ptrAllowsZero(zcu); 30044 const src_allowzero = src_ty.ptrAllowsZero(zcu); 30045 const ok_allowzero = src_allowzero == dest_allowzero or 30046 (!dest_is_mut and dest_allowzero); 30047 30048 if (!ok_allowzero) return .{ .ptr_allowzero = .{ 30049 .actual = src_ty, 30050 .wanted = dest_ty, 30051 } }; 30052 30053 if (dest_info.flags.address_space != src_info.flags.address_space) { 30054 return .{ .ptr_addrspace = .{ 30055 .actual = src_info.flags.address_space, 30056 .wanted = dest_info.flags.address_space, 30057 } }; 30058 } 30059 30060 const dest_child: Type = .fromInterned(dest_info.child); 30061 const src_child: Type = .fromInterned(src_info.child); 30062 const child = try sema.coerceInMemoryAllowed( 30063 block, 30064 dest_child, 30065 src_child, 30066 // We must also include `dest_is_mut`. 30067 // Otherwise, this code is valid: 30068 // 30069 // const b: B = ...; 30070 // var pa: *const A = undefined; 30071 // const ppa: **const A = &pa; 30072 // const ppb: **const B = ppa; // <-- this is what that allows 30073 // ppb.* = &b; 30074 // const a: A = pa.*; 30075 // 30076 // ...effectively performing an in-memory coercion from B to A. 30077 dest_is_mut or !dest_info.flags.is_const, 30078 target, 30079 dest_src, 30080 src_src, 30081 null, 30082 ); 30083 if (child != .ok and !dest_is_mut) allow: { 30084 // As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice. 30085 // `*[n:s]T` cannot coerce in memory to `*[n]T` since they have different sizes. 30086 if (src_child.zigTypeTag(zcu) == .array and dest_child.zigTypeTag(zcu) == .array and 30087 src_child.arrayLen(zcu) == dest_child.arrayLen(zcu) and 30088 src_child.sentinel(zcu) != null and dest_child.sentinel(zcu) == null and 30089 .ok == try sema.coerceInMemoryAllowed(block, dest_child.childType(zcu), src_child.childType(zcu), !dest_info.flags.is_const, target, dest_src, src_src, null)) 30090 { 30091 break :allow; 30092 } 30093 return .{ .ptr_child = .{ 30094 .child = try child.dupe(sema.arena), 30095 .actual = .fromInterned(src_info.child), 30096 .wanted = .fromInterned(dest_info.child), 30097 } }; 30098 } 30099 30100 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or 30101 src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) 30102 { 30103 return .{ .ptr_bit_range = .{ 30104 .actual_host = src_info.packed_offset.host_size, 30105 .wanted_host = dest_info.packed_offset.host_size, 30106 .actual_offset = src_info.packed_offset.bit_offset, 30107 .wanted_offset = dest_info.packed_offset.bit_offset, 30108 } }; 30109 } 30110 30111 const sentinel_ok = ok: { 30112 const ss = src_info.sentinel; 30113 const ds = dest_info.sentinel; 30114 if (ss == .none and ds == .none) break :ok true; 30115 if (ss != .none and ds != .none) { 30116 if (ds == try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, ss, dest_info.child)) break :ok true; 30117 } 30118 if (src_info.flags.size == .c) break :ok true; 30119 if (!dest_is_mut and dest_info.sentinel == .none) break :ok true; 30120 break :ok false; 30121 }; 30122 30123 if (!sentinel_ok) { 30124 return .{ .ptr_sentinel = .{ 30125 .actual = switch (src_info.sentinel) { 30126 .none => Value.@"unreachable", 30127 else => Value.fromInterned(src_info.sentinel), 30128 }, 30129 .wanted = switch (dest_info.sentinel) { 30130 .none => Value.@"unreachable", 30131 else => Value.fromInterned(dest_info.sentinel), 30132 }, 30133 .ty = .fromInterned(dest_info.child), 30134 } }; 30135 } 30136 30137 // If both pointers have alignment 0, it means they both want ABI alignment. 30138 // In this case, if they share the same child type, no need to resolve 30139 // pointee type alignment. Otherwise both pointee types must have their alignment 30140 // resolved and we compare the alignment numerically. 30141 if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or 30142 dest_info.child != src_info.child) 30143 { 30144 const src_align = if (src_info.flags.alignment != .none) 30145 src_info.flags.alignment 30146 else 30147 try Type.fromInterned(src_info.child).abiAlignmentSema(pt); 30148 30149 const dest_align = if (dest_info.flags.alignment != .none) 30150 dest_info.flags.alignment 30151 else 30152 try Type.fromInterned(dest_info.child).abiAlignmentSema(pt); 30153 30154 if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) { 30155 return InMemoryCoercionResult{ .ptr_alignment = .{ 30156 .actual = src_align, 30157 .wanted = dest_align, 30158 } }; 30159 } 30160 } 30161 30162 return .ok; 30163 } 30164 30165 fn coerceVarArgParam( 30166 sema: *Sema, 30167 block: *Block, 30168 inst: Air.Inst.Ref, 30169 inst_src: LazySrcLoc, 30170 ) !Air.Inst.Ref { 30171 if (block.is_typeof) return inst; 30172 30173 const pt = sema.pt; 30174 const zcu = pt.zcu; 30175 const uncasted_ty = sema.typeOf(inst); 30176 const coerced = switch (uncasted_ty.zigTypeTag(zcu)) { 30177 // TODO consider casting to c_int/f64 if they fit 30178 .comptime_int, .comptime_float => return sema.fail( 30179 block, 30180 inst_src, 30181 "integer and float literals passed to variadic function must be casted to a fixed-size number type", 30182 .{}, 30183 ), 30184 .@"fn" => fn_ptr: { 30185 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); 30186 const fn_nav = zcu.funcInfo(fn_val.toIntern()).owner_nav; 30187 break :fn_ptr try sema.analyzeNavRef(block, inst_src, fn_nav); 30188 }, 30189 .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), 30190 .float => float: { 30191 const target = zcu.getTarget(); 30192 const double_bits = target.cTypeBitSize(.double); 30193 const inst_bits = uncasted_ty.floatBits(target); 30194 if (inst_bits >= double_bits) break :float inst; 30195 switch (double_bits) { 30196 32 => break :float try sema.coerce(block, .f32, inst, inst_src), 30197 64 => break :float try sema.coerce(block, .f64, inst, inst_src), 30198 else => unreachable, 30199 } 30200 }, 30201 else => if (uncasted_ty.isAbiInt(zcu)) int: { 30202 if (!try sema.validateExternType(uncasted_ty, .param_ty)) break :int inst; 30203 const target = zcu.getTarget(); 30204 const uncasted_info = uncasted_ty.intInfo(zcu); 30205 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { 30206 .signed => .int, 30207 .unsigned => .uint, 30208 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 30209 .signed => .c_int, 30210 .unsigned => .c_uint, 30211 }, inst, inst_src); 30212 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { 30213 .signed => .long, 30214 .unsigned => .ulong, 30215 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 30216 .signed => .c_long, 30217 .unsigned => .c_ulong, 30218 }, inst, inst_src); 30219 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { 30220 .signed => .longlong, 30221 .unsigned => .ulonglong, 30222 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 30223 .signed => .c_longlong, 30224 .unsigned => .c_ulonglong, 30225 }, inst, inst_src); 30226 break :int inst; 30227 } else inst, 30228 }; 30229 30230 const coerced_ty = sema.typeOf(coerced); 30231 if (!try sema.validateExternType(coerced_ty, .param_ty)) { 30232 const msg = msg: { 30233 const msg = try sema.errMsg(inst_src, "cannot pass '{f}' to variadic function", .{coerced_ty.fmt(pt)}); 30234 errdefer msg.destroy(sema.gpa); 30235 30236 try sema.explainWhyTypeIsNotExtern(msg, inst_src, coerced_ty, .param_ty); 30237 30238 try sema.addDeclaredHereNote(msg, coerced_ty); 30239 break :msg msg; 30240 }; 30241 return sema.failWithOwnedErrorMsg(block, msg); 30242 } 30243 return coerced; 30244 } 30245 30246 // TODO migrate callsites to use storePtr2 instead. 30247 fn storePtr( 30248 sema: *Sema, 30249 block: *Block, 30250 src: LazySrcLoc, 30251 ptr: Air.Inst.Ref, 30252 uncasted_operand: Air.Inst.Ref, 30253 ) CompileError!void { 30254 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store; 30255 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag); 30256 } 30257 30258 fn storePtr2( 30259 sema: *Sema, 30260 block: *Block, 30261 src: LazySrcLoc, 30262 ptr: Air.Inst.Ref, 30263 ptr_src: LazySrcLoc, 30264 uncasted_operand: Air.Inst.Ref, 30265 operand_src: LazySrcLoc, 30266 air_tag: Air.Inst.Tag, 30267 ) CompileError!void { 30268 const pt = sema.pt; 30269 const zcu = pt.zcu; 30270 const ptr_ty = sema.typeOf(ptr); 30271 if (ptr_ty.isConstPtr(zcu)) 30272 return sema.fail(block, ptr_src, "cannot assign to constant", .{}); 30273 30274 const elem_ty = ptr_ty.childType(zcu); 30275 30276 // To generate better code for tuples, we detect a tuple operand here, and 30277 // analyze field loads and stores directly. This avoids an extra allocation + memcpy 30278 // which would occur if we used `coerce`. 30279 // However, we avoid this mechanism if the destination element type is a tuple, 30280 // because the regular store will be better for this case. 30281 // If the destination type is a struct we don't want this mechanism to trigger, because 30282 // this code does not handle tuple-to-struct coercion which requires dealing with missing 30283 // fields. 30284 const operand_ty = sema.typeOf(uncasted_operand); 30285 if (operand_ty.isTuple(zcu) and elem_ty.zigTypeTag(zcu) == .array) { 30286 const field_count = operand_ty.structFieldCount(zcu); 30287 var i: u32 = 0; 30288 while (i < field_count) : (i += 1) { 30289 const elem_src = operand_src; // TODO better source location 30290 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); 30291 const elem_index = try pt.intRef(.usize, i); 30292 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true); 30293 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); 30294 } 30295 return; 30296 } 30297 30298 // TODO do the same thing for anon structs as for tuples above. 30299 // However, beware of the need to handle missing/extra fields. 30300 30301 const is_ret = air_tag == .ret_ptr; 30302 30303 // Detect if we are storing an array operand to a bitcasted vector pointer. 30304 // If so, we instead reach through the bitcasted pointer to the vector pointer, 30305 // bitcast the array operand to a vector, and then lower this as a store of 30306 // a vector value to a vector pointer. This generally results in better code, 30307 // as well as working around an LLVM bug: 30308 // https://github.com/ziglang/zig/issues/11154 30309 if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { 30310 const vector_ty = sema.typeOf(vector_ptr).childType(zcu); 30311 const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 30312 error.NotCoercible => unreachable, 30313 else => |e| return e, 30314 }; 30315 try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store); 30316 return; 30317 } 30318 30319 const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 30320 error.NotCoercible => unreachable, 30321 else => |e| return e, 30322 }; 30323 const maybe_operand_val = try sema.resolveValue(operand); 30324 30325 const runtime_src = rs: { 30326 const ptr_val = try sema.resolveDefinedValue(block, ptr_src, ptr) orelse break :rs ptr_src; 30327 if (!sema.isComptimeMutablePtr(ptr_val)) break :rs ptr_src; 30328 const operand_val = maybe_operand_val orelse return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); 30329 return sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); 30330 }; 30331 30332 // We're performing the store at runtime; as such, we need to make sure the pointee type 30333 // is not comptime-only. We can hit this case with a `@ptrFromInt` pointer. 30334 if (try elem_ty.comptimeOnlySema(pt)) { 30335 return sema.failWithOwnedErrorMsg(block, msg: { 30336 const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)}); 30337 errdefer msg.destroy(sema.gpa); 30338 try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{}); 30339 break :msg msg; 30340 }); 30341 } 30342 30343 // We do this after the possible comptime store above, for the case of field_ptr stores 30344 // to unions because we want the comptime tag to be set, even if the field type is void. 30345 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { 30346 return; 30347 } 30348 30349 try sema.requireRuntimeBlock(block, src, runtime_src); 30350 30351 if (ptr_ty.ptrInfo(zcu).flags.vector_index == .runtime) { 30352 const ptr_inst = ptr.toIndex().?; 30353 const air_tags = sema.air_instructions.items(.tag); 30354 if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) { 30355 const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl; 30356 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 30357 _ = try block.addInst(.{ 30358 .tag = .vector_store_elem, 30359 .data = .{ .vector_store_elem = .{ 30360 .vector_ptr = bin_op.lhs, 30361 .payload = try block.sema.addExtra(Air.Bin{ 30362 .lhs = bin_op.rhs, 30363 .rhs = operand, 30364 }), 30365 } }, 30366 }); 30367 return; 30368 } 30369 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{f}'", .{ 30370 ptr_ty.fmt(pt), 30371 }); 30372 } 30373 30374 const store_inst = if (is_ret) 30375 try block.addBinOp(.store, ptr, operand) 30376 else 30377 try block.addBinOp(air_tag, ptr, operand); 30378 30379 try sema.checkComptimeKnownStore(block, store_inst, operand_src); 30380 30381 return; 30382 } 30383 30384 /// Given an AIR store instruction, checks whether we are performing a 30385 /// comptime-known store to a local alloc, and updates `maybe_comptime_allocs` 30386 /// accordingly. 30387 /// Handles calling `validateRuntimeValue` if the store is runtime for any reason. 30388 fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref, store_src: LazySrcLoc) !void { 30389 const store_inst = store_inst_ref.toIndex().?; 30390 const inst_data = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 30391 const ptr = inst_data.lhs.toIndex() orelse return; 30392 const operand = inst_data.rhs; 30393 30394 known: { 30395 const maybe_base_alloc = sema.base_allocs.get(ptr) orelse break :known; 30396 const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse break :known; 30397 30398 if ((try sema.resolveValue(operand)) != null and 30399 block.runtime_index == maybe_comptime_alloc.runtime_index) 30400 { 30401 try maybe_comptime_alloc.stores.append(sema.arena, .{ 30402 .inst = store_inst, 30403 .src = store_src, 30404 }); 30405 return; 30406 } 30407 30408 // We're newly discovering that this alloc is runtime-known. 30409 try sema.markMaybeComptimeAllocRuntime(block, maybe_base_alloc); 30410 } 30411 30412 try sema.validateRuntimeValue(block, store_src, operand); 30413 } 30414 30415 /// Given an AIR instruction transforming a pointer (struct_field_ptr, 30416 /// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a 30417 /// local alloc, and updates `base_allocs` accordingly. 30418 fn checkKnownAllocPtr(sema: *Sema, block: *Block, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void { 30419 const base_ptr_inst = base_ptr.toIndex() orelse return; 30420 const new_ptr_inst = new_ptr.toIndex() orelse return; 30421 const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return; 30422 try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst); 30423 30424 switch (sema.air_instructions.items(.tag)[@intFromEnum(new_ptr_inst)]) { 30425 .optional_payload_ptr_set, .errunion_payload_ptr_set => { 30426 const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return; 30427 30428 // This is functionally a store, since it writes the optional payload bit. 30429 // Thus, if it is behind a runtime condition, we must mark the alloc as runtime appropriately. 30430 if (block.runtime_index != maybe_comptime_alloc.runtime_index) { 30431 return sema.markMaybeComptimeAllocRuntime(block, alloc_inst); 30432 } 30433 30434 try maybe_comptime_alloc.stores.append(sema.arena, .{ 30435 .inst = new_ptr_inst, 30436 .src = LazySrcLoc.unneeded, 30437 }); 30438 }, 30439 .ptr_elem_ptr => { 30440 const tmp_air = sema.getTmpAir(); 30441 const pl_idx = tmp_air.instructions.items(.data)[@intFromEnum(new_ptr_inst)].ty_pl.payload; 30442 const bin = tmp_air.extraData(Air.Bin, pl_idx).data; 30443 const index_ref = bin.rhs; 30444 30445 // If the index value is runtime-known, this pointer is also runtime-known, so 30446 // we must in turn make the alloc value runtime-known. 30447 if (null == try sema.resolveValue(index_ref)) { 30448 try sema.markMaybeComptimeAllocRuntime(block, alloc_inst); 30449 } 30450 }, 30451 else => {}, 30452 } 30453 } 30454 30455 fn markMaybeComptimeAllocRuntime(sema: *Sema, block: *Block, alloc_inst: Air.Inst.Index) CompileError!void { 30456 const maybe_comptime_alloc = (sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return).value; 30457 // Since the alloc has been determined to be runtime, we must check that 30458 // all other stores to it are permitted to be runtime values. 30459 const slice = maybe_comptime_alloc.stores.slice(); 30460 for (slice.items(.inst), slice.items(.src)) |other_inst, other_src| { 30461 if (other_src.offset == .unneeded) { 30462 switch (sema.air_instructions.items(.tag)[@intFromEnum(other_inst)]) { 30463 .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => continue, 30464 else => unreachable, // assertion failure 30465 } 30466 } 30467 const other_data = sema.air_instructions.items(.data)[@intFromEnum(other_inst)].bin_op; 30468 const other_operand = other_data.rhs; 30469 try sema.validateRuntimeValue(block, other_src, other_operand); 30470 } 30471 } 30472 30473 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector 30474 /// pointer. Only if the final element type matches the vector element type, and the 30475 /// lengths match. 30476 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { 30477 const pt = sema.pt; 30478 const zcu = pt.zcu; 30479 const array_ty = sema.typeOf(ptr).childType(zcu); 30480 if (array_ty.zigTypeTag(zcu) != .array) return null; 30481 var ptr_ref = ptr; 30482 var ptr_inst = ptr_ref.toIndex() orelse return null; 30483 const air_datas = sema.air_instructions.items(.data); 30484 const air_tags = sema.air_instructions.items(.tag); 30485 const vector_ty = while (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 30486 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 30487 if (!sema.isKnownZigType(ptr_ref, .pointer)) return null; 30488 const child_ty = sema.typeOf(ptr_ref).childType(zcu); 30489 if (child_ty.zigTypeTag(zcu) == .vector) break child_ty; 30490 ptr_inst = ptr_ref.toIndex() orelse return null; 30491 } else return null; 30492 30493 // We have a pointer-to-array and a pointer-to-vector. If the elements and 30494 // lengths match, return the result. 30495 if (array_ty.childType(zcu).eql(vector_ty.childType(zcu), zcu) and 30496 array_ty.arrayLen(zcu) == vector_ty.vectorLen(zcu)) 30497 { 30498 return ptr_ref; 30499 } else { 30500 return null; 30501 } 30502 } 30503 30504 /// Call when you have Value objects rather than Air instructions, and you want to 30505 /// assert the store must be done at comptime. 30506 fn storePtrVal( 30507 sema: *Sema, 30508 block: *Block, 30509 src: LazySrcLoc, 30510 ptr_val: Value, 30511 operand_val: Value, 30512 operand_ty: Type, 30513 ) !void { 30514 const pt = sema.pt; 30515 const zcu = pt.zcu; 30516 const ip = &zcu.intern_pool; 30517 // TODO: audit use sites to eliminate this coercion 30518 const coerced_operand_val = try pt.getCoerced(operand_val, operand_ty); 30519 // TODO: audit use sites to eliminate this coercion 30520 const ptr_ty = try pt.ptrType(info: { 30521 var info = ptr_val.typeOf(zcu).ptrInfo(zcu); 30522 info.child = operand_ty.toIntern(); 30523 break :info info; 30524 }); 30525 const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty); 30526 30527 switch (try sema.storeComptimePtr(block, src, coerced_ptr_val, coerced_operand_val)) { 30528 .success => {}, 30529 .runtime_store => unreachable, // use sites check this 30530 // TODO use failWithInvalidComptimeFieldStore 30531 .comptime_field_mismatch => return sema.fail( 30532 block, 30533 src, 30534 "value stored in comptime field does not match the default value of the field", 30535 .{}, 30536 ), 30537 .undef => return sema.failWithUseOfUndef(block, src, null), 30538 .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}), 30539 .null_payload => return sema.fail(block, src, "attempt to use null value", .{}), 30540 .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}), 30541 .needed_well_defined => |ty| return sema.fail( 30542 block, 30543 src, 30544 "comptime dereference requires '{f}' to have a well-defined layout", 30545 .{ty.fmt(pt)}, 30546 ), 30547 .out_of_bounds => |ty| return sema.fail( 30548 block, 30549 src, 30550 "dereference of '{f}' exceeds bounds of containing decl of type '{f}'", 30551 .{ ptr_ty.fmt(pt), ty.fmt(pt) }, 30552 ), 30553 .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}), 30554 } 30555 } 30556 30557 fn bitCast( 30558 sema: *Sema, 30559 block: *Block, 30560 dest_ty: Type, 30561 inst: Air.Inst.Ref, 30562 inst_src: LazySrcLoc, 30563 operand_src: ?LazySrcLoc, 30564 ) CompileError!Air.Inst.Ref { 30565 const pt = sema.pt; 30566 const zcu = pt.zcu; 30567 try dest_ty.resolveLayout(pt); 30568 30569 const old_ty = sema.typeOf(inst); 30570 try old_ty.resolveLayout(pt); 30571 30572 const dest_bits = dest_ty.bitSize(zcu); 30573 const old_bits = old_ty.bitSize(zcu); 30574 30575 if (old_bits != dest_bits) { 30576 return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{ 30577 dest_ty.fmt(pt), 30578 dest_bits, 30579 old_ty.fmt(pt), 30580 old_bits, 30581 }); 30582 } 30583 30584 if (try sema.resolveValue(inst)) |val| { 30585 if (val.isUndef(zcu)) 30586 return pt.undefRef(dest_ty); 30587 if (old_ty.zigTypeTag(zcu) == .error_set and dest_ty.zigTypeTag(zcu) == .error_set) { 30588 // Special case: we sometimes call `bitCast` on error set values, but they 30589 // don't have a well-defined layout, so we can't use `bitCastVal` on them. 30590 return Air.internedToRef((try pt.getCoerced(val, dest_ty)).toIntern()); 30591 } 30592 if (try sema.bitCastVal(val, dest_ty, 0, 0, 0)) |result_val| { 30593 return Air.internedToRef(result_val.toIntern()); 30594 } 30595 } 30596 try sema.requireRuntimeBlock(block, inst_src, operand_src); 30597 try sema.validateRuntimeValue(block, inst_src, inst); 30598 return block.addBitCast(dest_ty, inst); 30599 } 30600 30601 fn coerceArrayPtrToSlice( 30602 sema: *Sema, 30603 block: *Block, 30604 dest_ty: Type, 30605 inst: Air.Inst.Ref, 30606 inst_src: LazySrcLoc, 30607 ) CompileError!Air.Inst.Ref { 30608 const pt = sema.pt; 30609 const zcu = pt.zcu; 30610 if (try sema.resolveValue(inst)) |val| { 30611 const ptr_array_ty = sema.typeOf(inst); 30612 const array_ty = ptr_array_ty.childType(zcu); 30613 const slice_ptr_ty = dest_ty.slicePtrFieldType(zcu); 30614 const slice_ptr = try pt.getCoerced(val, slice_ptr_ty); 30615 const slice_val = try pt.intern(.{ .slice = .{ 30616 .ty = dest_ty.toIntern(), 30617 .ptr = slice_ptr.toIntern(), 30618 .len = (try pt.intValue(.usize, array_ty.arrayLen(zcu))).toIntern(), 30619 } }); 30620 return Air.internedToRef(slice_val); 30621 } 30622 try sema.requireRuntimeBlock(block, inst_src, null); 30623 return block.addTyOp(.array_to_slice, dest_ty, inst); 30624 } 30625 30626 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool { 30627 const pt = sema.pt; 30628 const zcu = pt.zcu; 30629 const dest_info = dest_ty.ptrInfo(zcu); 30630 const inst_info = inst_ty.ptrInfo(zcu); 30631 const len0 = (Type.fromInterned(inst_info.child).zigTypeTag(zcu) == .array and (Type.fromInterned(inst_info.child).arrayLenIncludingSentinel(zcu) == 0 or 30632 (Type.fromInterned(inst_info.child).arrayLen(zcu) == 0 and dest_info.sentinel == .none and dest_info.flags.size != .c and dest_info.flags.size != .many))) or 30633 (Type.fromInterned(inst_info.child).isTuple(zcu) and Type.fromInterned(inst_info.child).structFieldCount(zcu) == 0); 30634 30635 const ok_const = (!inst_info.flags.is_const or dest_info.flags.is_const) or len0; 30636 const ok_volatile = !inst_info.flags.is_volatile or dest_info.flags.is_volatile; 30637 if (!ok_const) { 30638 in_memory_result.* = .{ .ptr_const = .{ 30639 .actual = inst_ty, 30640 .wanted = dest_ty, 30641 } }; 30642 return false; 30643 } 30644 if (!ok_volatile) { 30645 in_memory_result.* = .{ .ptr_volatile = .{ 30646 .actual = inst_ty, 30647 .wanted = dest_ty, 30648 } }; 30649 return false; 30650 } 30651 30652 if (dest_info.flags.address_space != inst_info.flags.address_space) { 30653 in_memory_result.* = .{ .ptr_addrspace = .{ 30654 .actual = inst_info.flags.address_space, 30655 .wanted = dest_info.flags.address_space, 30656 } }; 30657 return false; 30658 } 30659 if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true; 30660 if (len0) return true; 30661 30662 const inst_align = if (inst_info.flags.alignment != .none) 30663 inst_info.flags.alignment 30664 else 30665 Type.fromInterned(inst_info.child).abiAlignment(zcu); 30666 30667 const dest_align = if (dest_info.flags.alignment != .none) 30668 dest_info.flags.alignment 30669 else 30670 Type.fromInterned(dest_info.child).abiAlignment(zcu); 30671 30672 if (dest_align.compare(.gt, inst_align)) { 30673 in_memory_result.* = .{ .ptr_alignment = .{ 30674 .actual = inst_align, 30675 .wanted = dest_align, 30676 } }; 30677 return false; 30678 } 30679 return true; 30680 } 30681 30682 fn coerceCompatiblePtrs( 30683 sema: *Sema, 30684 block: *Block, 30685 dest_ty: Type, 30686 inst: Air.Inst.Ref, 30687 inst_src: LazySrcLoc, 30688 ) !Air.Inst.Ref { 30689 const pt = sema.pt; 30690 const zcu = pt.zcu; 30691 const inst_ty = sema.typeOf(inst); 30692 if (try sema.resolveValue(inst)) |val| { 30693 if (!val.isUndef(zcu) and val.isNull(zcu) and !dest_ty.isAllowzeroPtr(zcu)) { 30694 return sema.fail(block, inst_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)}); 30695 } 30696 // The comptime Value representation is compatible with both types. 30697 return Air.internedToRef( 30698 (try pt.getCoerced(val, dest_ty)).toIntern(), 30699 ); 30700 } 30701 try sema.requireRuntimeBlock(block, inst_src, null); 30702 const inst_allows_zero = inst_ty.zigTypeTag(zcu) != .pointer or inst_ty.ptrAllowsZero(zcu); 30703 if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu) and 30704 (try dest_ty.elemType2(zcu).hasRuntimeBitsSema(pt) or dest_ty.elemType2(zcu).zigTypeTag(zcu) == .@"fn")) 30705 { 30706 try sema.checkLogicalPtrOperation(block, inst_src, inst_ty); 30707 const actual_ptr = if (inst_ty.isSlice(zcu)) 30708 try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) 30709 else 30710 inst; 30711 const ptr_int = try block.addBitCast(.usize, actual_ptr); 30712 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); 30713 const ok = if (inst_ty.isSlice(zcu)) ok: { 30714 const len = try sema.analyzeSliceLen(block, inst_src, inst); 30715 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 30716 break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero); 30717 } else is_non_zero; 30718 try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null); 30719 } 30720 const new_ptr = try sema.bitCast(block, dest_ty, inst, inst_src, null); 30721 try sema.checkKnownAllocPtr(block, inst, new_ptr); 30722 return new_ptr; 30723 } 30724 30725 fn coerceEnumToUnion( 30726 sema: *Sema, 30727 block: *Block, 30728 union_ty: Type, 30729 union_ty_src: LazySrcLoc, 30730 inst: Air.Inst.Ref, 30731 inst_src: LazySrcLoc, 30732 ) !Air.Inst.Ref { 30733 const pt = sema.pt; 30734 const zcu = pt.zcu; 30735 const ip = &zcu.intern_pool; 30736 const inst_ty = sema.typeOf(inst); 30737 30738 const tag_ty = union_ty.unionTagType(zcu) orelse { 30739 const msg = msg: { 30740 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ 30741 union_ty.fmt(pt), inst_ty.fmt(pt), 30742 }); 30743 errdefer msg.destroy(sema.gpa); 30744 try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{}); 30745 try sema.addDeclaredHereNote(msg, union_ty); 30746 break :msg msg; 30747 }; 30748 return sema.failWithOwnedErrorMsg(block, msg); 30749 }; 30750 30751 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src); 30752 if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { 30753 const field_index = union_ty.unionTagFieldIndex(val, pt.zcu) orelse { 30754 return sema.fail(block, inst_src, "union '{f}' has no tag with value '{f}'", .{ 30755 union_ty.fmt(pt), val.fmtValueSema(pt, sema), 30756 }); 30757 }; 30758 30759 const union_obj = zcu.typeToUnion(union_ty).?; 30760 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 30761 try field_ty.resolveFields(pt); 30762 if (field_ty.zigTypeTag(zcu) == .noreturn) { 30763 const msg = msg: { 30764 const msg = try sema.errMsg(inst_src, "cannot initialize 'noreturn' field of union", .{}); 30765 errdefer msg.destroy(sema.gpa); 30766 30767 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; 30768 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ 30769 field_name.fmt(ip), 30770 }); 30771 try sema.addDeclaredHereNote(msg, union_ty); 30772 break :msg msg; 30773 }; 30774 return sema.failWithOwnedErrorMsg(block, msg); 30775 } 30776 const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { 30777 const msg = msg: { 30778 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; 30779 const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{ 30780 inst_ty.fmt(pt), union_ty.fmt(pt), 30781 field_ty.fmt(pt), field_name.fmt(ip), 30782 }); 30783 errdefer msg.destroy(sema.gpa); 30784 30785 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ 30786 field_name.fmt(ip), 30787 }); 30788 try sema.addDeclaredHereNote(msg, union_ty); 30789 break :msg msg; 30790 }; 30791 return sema.failWithOwnedErrorMsg(block, msg); 30792 }; 30793 30794 return Air.internedToRef((try pt.unionValue(union_ty, val, opv)).toIntern()); 30795 } 30796 30797 try sema.requireRuntimeBlock(block, inst_src, null); 30798 30799 if (tag_ty.isNonexhaustiveEnum(zcu)) { 30800 const msg = msg: { 30801 const msg = try sema.errMsg(inst_src, "runtime coercion to union '{f}' from non-exhaustive enum", .{ 30802 union_ty.fmt(pt), 30803 }); 30804 errdefer msg.destroy(sema.gpa); 30805 try sema.addDeclaredHereNote(msg, tag_ty); 30806 break :msg msg; 30807 }; 30808 return sema.failWithOwnedErrorMsg(block, msg); 30809 } 30810 30811 const union_obj = zcu.typeToUnion(union_ty).?; 30812 { 30813 var msg: ?*Zcu.ErrorMsg = null; 30814 errdefer if (msg) |some| some.destroy(sema.gpa); 30815 30816 for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { 30817 if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .noreturn) { 30818 const err_msg = msg orelse try sema.errMsg( 30819 inst_src, 30820 "runtime coercion from enum '{f}' to union '{f}' which has a 'noreturn' field", 30821 .{ tag_ty.fmt(pt), union_ty.fmt(pt) }, 30822 ); 30823 msg = err_msg; 30824 30825 try sema.addFieldErrNote(union_ty, field_index, err_msg, "'noreturn' field here", .{}); 30826 } 30827 } 30828 if (msg) |some| { 30829 msg = null; 30830 try sema.addDeclaredHereNote(some, union_ty); 30831 return sema.failWithOwnedErrorMsg(block, some); 30832 } 30833 } 30834 30835 // If the union has all fields 0 bits, the union value is just the enum value. 30836 if (union_ty.unionHasAllZeroBitFieldTypes(zcu)) { 30837 return block.addBitCast(union_ty, enum_tag); 30838 } 30839 30840 const msg = msg: { 30841 const msg = try sema.errMsg( 30842 inst_src, 30843 "runtime coercion from enum '{f}' to union '{f}' which has non-void fields", 30844 .{ tag_ty.fmt(pt), union_ty.fmt(pt) }, 30845 ); 30846 errdefer msg.destroy(sema.gpa); 30847 30848 for (0..union_obj.field_types.len) |field_index| { 30849 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; 30850 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 30851 if (!(try field_ty.hasRuntimeBitsSema(pt))) continue; 30852 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has type '{f}'", .{ 30853 field_name.fmt(ip), 30854 field_ty.fmt(pt), 30855 }); 30856 } 30857 try sema.addDeclaredHereNote(msg, union_ty); 30858 break :msg msg; 30859 }; 30860 return sema.failWithOwnedErrorMsg(block, msg); 30861 } 30862 30863 /// If the lengths match, coerces element-wise. 30864 fn coerceArrayLike( 30865 sema: *Sema, 30866 block: *Block, 30867 dest_ty: Type, 30868 dest_ty_src: LazySrcLoc, 30869 inst: Air.Inst.Ref, 30870 inst_src: LazySrcLoc, 30871 ) !Air.Inst.Ref { 30872 const pt = sema.pt; 30873 const zcu = pt.zcu; 30874 const inst_ty = sema.typeOf(inst); 30875 const target = zcu.getTarget(); 30876 30877 // try coercion of the whole array 30878 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, null); 30879 if (in_memory_result == .ok) { 30880 if (try sema.resolveValue(inst)) |inst_val| { 30881 // These types share the same comptime value representation. 30882 return sema.coerceInMemory(inst_val, dest_ty); 30883 } 30884 try sema.requireRuntimeBlock(block, inst_src, null); 30885 return block.addBitCast(dest_ty, inst); 30886 } 30887 30888 // otherwise, try element by element 30889 const inst_len = inst_ty.arrayLen(zcu); 30890 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu)); 30891 if (dest_len != inst_len) { 30892 const msg = msg: { 30893 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ 30894 dest_ty.fmt(pt), inst_ty.fmt(pt), 30895 }); 30896 errdefer msg.destroy(sema.gpa); 30897 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len}); 30898 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len}); 30899 break :msg msg; 30900 }; 30901 return sema.failWithOwnedErrorMsg(block, msg); 30902 } 30903 30904 const dest_elem_ty = dest_ty.childType(zcu); 30905 if (dest_ty.isVector(zcu) and inst_ty.isVector(zcu) and (try sema.resolveValue(inst)) == null) { 30906 const inst_elem_ty = inst_ty.childType(zcu); 30907 switch (dest_elem_ty.zigTypeTag(zcu)) { 30908 .int => if (inst_elem_ty.isInt(zcu)) { 30909 // integer widening 30910 const dst_info = dest_elem_ty.intInfo(zcu); 30911 const src_info = inst_elem_ty.intInfo(zcu); 30912 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or 30913 // small enough unsigned ints can get casted to large enough signed ints 30914 (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) 30915 { 30916 try sema.requireRuntimeBlock(block, inst_src, null); 30917 return block.addTyOp(.intcast, dest_ty, inst); 30918 } 30919 }, 30920 .float => if (inst_elem_ty.isRuntimeFloat()) { 30921 // float widening 30922 const src_bits = inst_elem_ty.floatBits(target); 30923 const dst_bits = dest_elem_ty.floatBits(target); 30924 if (dst_bits >= src_bits) { 30925 try sema.requireRuntimeBlock(block, inst_src, null); 30926 return block.addTyOp(.fpext, dest_ty, inst); 30927 } 30928 }, 30929 else => {}, 30930 } 30931 } 30932 30933 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); 30934 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); 30935 var runtime_src: ?LazySrcLoc = null; 30936 30937 for (element_vals, element_refs, 0..) |*val, *ref, i| { 30938 const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern()); 30939 const src = inst_src; // TODO better source location 30940 const elem_src = inst_src; // TODO better source location 30941 const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true); 30942 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 30943 ref.* = coerced; 30944 if (runtime_src == null) { 30945 if (try sema.resolveValue(coerced)) |elem_val| { 30946 val.* = elem_val.toIntern(); 30947 } else { 30948 runtime_src = elem_src; 30949 } 30950 } 30951 } 30952 30953 if (runtime_src) |rs| { 30954 try sema.requireRuntimeBlock(block, inst_src, rs); 30955 return block.addAggregateInit(dest_ty, element_refs); 30956 } 30957 30958 return Air.internedToRef((try pt.aggregateValue(dest_ty, element_vals)).toIntern()); 30959 } 30960 30961 /// If the lengths match, coerces element-wise. 30962 fn coerceTupleToArray( 30963 sema: *Sema, 30964 block: *Block, 30965 dest_ty: Type, 30966 dest_ty_src: LazySrcLoc, 30967 inst: Air.Inst.Ref, 30968 inst_src: LazySrcLoc, 30969 ) !Air.Inst.Ref { 30970 const pt = sema.pt; 30971 const zcu = pt.zcu; 30972 const inst_ty = sema.typeOf(inst); 30973 const inst_len = inst_ty.arrayLen(zcu); 30974 const dest_len = dest_ty.arrayLen(zcu); 30975 30976 if (dest_len != inst_len) { 30977 const msg = msg: { 30978 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ 30979 dest_ty.fmt(pt), inst_ty.fmt(pt), 30980 }); 30981 errdefer msg.destroy(sema.gpa); 30982 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len}); 30983 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len}); 30984 break :msg msg; 30985 }; 30986 return sema.failWithOwnedErrorMsg(block, msg); 30987 } 30988 30989 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len); 30990 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems); 30991 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); 30992 const dest_elem_ty = dest_ty.childType(zcu); 30993 30994 var runtime_src: ?LazySrcLoc = null; 30995 for (element_vals, element_refs, 0..) |*val, *ref, i_usize| { 30996 const i: u32 = @intCast(i_usize); 30997 if (i_usize == inst_len) { 30998 const sentinel_val = dest_ty.sentinel(zcu).?; 30999 val.* = sentinel_val.toIntern(); 31000 ref.* = Air.internedToRef(sentinel_val.toIntern()); 31001 break; 31002 } 31003 const elem_src = inst_src; // TODO better source location 31004 const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i); 31005 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 31006 ref.* = coerced; 31007 if (runtime_src == null) { 31008 if (try sema.resolveValue(coerced)) |elem_val| { 31009 val.* = elem_val.toIntern(); 31010 } else { 31011 runtime_src = elem_src; 31012 } 31013 } 31014 } 31015 31016 if (runtime_src) |rs| { 31017 try sema.requireRuntimeBlock(block, inst_src, rs); 31018 return block.addAggregateInit(dest_ty, element_refs); 31019 } 31020 31021 return Air.internedToRef((try pt.aggregateValue(dest_ty, element_vals)).toIntern()); 31022 } 31023 31024 /// If the lengths match, coerces element-wise. 31025 fn coerceTupleToSlicePtrs( 31026 sema: *Sema, 31027 block: *Block, 31028 slice_ty: Type, 31029 slice_ty_src: LazySrcLoc, 31030 ptr_tuple: Air.Inst.Ref, 31031 tuple_src: LazySrcLoc, 31032 ) !Air.Inst.Ref { 31033 const pt = sema.pt; 31034 const zcu = pt.zcu; 31035 const tuple_ty = sema.typeOf(ptr_tuple).childType(zcu); 31036 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 31037 const slice_info = slice_ty.ptrInfo(zcu); 31038 const array_ty = try pt.arrayType(.{ 31039 .len = tuple_ty.structFieldCount(zcu), 31040 .sentinel = slice_info.sentinel, 31041 .child = slice_info.child, 31042 }); 31043 const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); 31044 if (slice_info.flags.alignment != .none) { 31045 return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 31046 } 31047 const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst); 31048 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src); 31049 } 31050 31051 /// If the lengths match, coerces element-wise. 31052 fn coerceTupleToArrayPtrs( 31053 sema: *Sema, 31054 block: *Block, 31055 ptr_array_ty: Type, 31056 array_ty_src: LazySrcLoc, 31057 ptr_tuple: Air.Inst.Ref, 31058 tuple_src: LazySrcLoc, 31059 ) !Air.Inst.Ref { 31060 const pt = sema.pt; 31061 const zcu = pt.zcu; 31062 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 31063 const ptr_info = ptr_array_ty.ptrInfo(zcu); 31064 const array_ty: Type = .fromInterned(ptr_info.child); 31065 const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src); 31066 if (ptr_info.flags.alignment != .none) { 31067 return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 31068 } 31069 const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst); 31070 return ptr_array; 31071 } 31072 31073 fn coerceTupleToTuple( 31074 sema: *Sema, 31075 block: *Block, 31076 tuple_ty: Type, 31077 inst: Air.Inst.Ref, 31078 inst_src: LazySrcLoc, 31079 ) !Air.Inst.Ref { 31080 const pt = sema.pt; 31081 const zcu = pt.zcu; 31082 const ip = &zcu.intern_pool; 31083 const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) { 31084 .tuple_type => |tuple_type| tuple_type.types.len, 31085 else => unreachable, 31086 }; 31087 const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); 31088 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); 31089 @memset(field_refs, .none); 31090 31091 const inst_ty = sema.typeOf(inst); 31092 const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) { 31093 .tuple_type => |tuple_type| tuple_type.types.len, 31094 else => unreachable, 31095 }; 31096 if (src_field_count > dest_field_count) return error.NotCoercible; 31097 31098 var runtime_src: ?LazySrcLoc = null; 31099 for (0..dest_field_count) |field_index_usize| { 31100 const field_i: u32 = @intCast(field_index_usize); 31101 const field_src = inst_src; // TODO better source location 31102 31103 const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { 31104 .tuple_type => |tuple_type| tuple_type.types.get(ip)[field_index_usize], 31105 .struct_type => ip.loadStructType(tuple_ty.toIntern()).field_types.get(ip)[field_index_usize], 31106 else => unreachable, 31107 }; 31108 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 31109 .tuple_type => |tuple_type| tuple_type.values.get(ip)[field_index_usize], 31110 .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, field_index_usize), 31111 else => unreachable, 31112 }; 31113 31114 const field_index: u32 = @intCast(field_index_usize); 31115 31116 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); 31117 const coerced = try sema.coerce(block, .fromInterned(field_ty), elem_ref, field_src); 31118 field_refs[field_index] = coerced; 31119 if (default_val != .none) { 31120 const init_val = (try sema.resolveValue(coerced)) orelse { 31121 return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); 31122 }; 31123 31124 if (!init_val.eql(Value.fromInterned(default_val), .fromInterned(field_ty), pt.zcu)) { 31125 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); 31126 } 31127 } 31128 if (runtime_src == null) { 31129 if (try sema.resolveValue(coerced)) |field_val| { 31130 field_vals[field_index] = field_val.toIntern(); 31131 } else { 31132 runtime_src = field_src; 31133 } 31134 } 31135 } 31136 31137 // Populate default field values and report errors for missing fields. 31138 var root_msg: ?*Zcu.ErrorMsg = null; 31139 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 31140 31141 for (field_refs, 0..) |*field_ref, i_usize| { 31142 const i: u32 = @intCast(i_usize); 31143 if (field_ref.* != .none) continue; 31144 31145 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 31146 .tuple_type => |tuple_type| tuple_type.values.get(ip)[i], 31147 .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, i), 31148 else => unreachable, 31149 }; 31150 31151 const field_src = inst_src; // TODO better source location 31152 if (default_val == .none) { 31153 const template = "missing tuple field: {d}"; 31154 if (root_msg) |msg| { 31155 try sema.errNote(field_src, msg, template, .{i}); 31156 } else { 31157 root_msg = try sema.errMsg(field_src, template, .{i}); 31158 } 31159 continue; 31160 } 31161 if (runtime_src == null) { 31162 field_vals[i] = default_val; 31163 } else { 31164 field_ref.* = Air.internedToRef(default_val); 31165 } 31166 } 31167 31168 if (root_msg) |msg| { 31169 try sema.addDeclaredHereNote(msg, tuple_ty); 31170 root_msg = null; 31171 return sema.failWithOwnedErrorMsg(block, msg); 31172 } 31173 31174 if (runtime_src) |rs| { 31175 try sema.requireRuntimeBlock(block, inst_src, rs); 31176 return block.addAggregateInit(tuple_ty, field_refs); 31177 } 31178 31179 return Air.internedToRef((try pt.aggregateValue(tuple_ty, field_vals)).toIntern()); 31180 } 31181 31182 fn analyzeNavVal( 31183 sema: *Sema, 31184 block: *Block, 31185 src: LazySrcLoc, 31186 nav_index: InternPool.Nav.Index, 31187 ) CompileError!Air.Inst.Ref { 31188 const ref = try sema.analyzeNavRefInner(block, src, nav_index, false); 31189 return sema.analyzeLoad(block, src, ref, src); 31190 } 31191 31192 fn addReferenceEntry( 31193 sema: *Sema, 31194 opt_block: ?*Block, 31195 src: LazySrcLoc, 31196 referenced_unit: AnalUnit, 31197 ) !void { 31198 const zcu = sema.pt.zcu; 31199 const ip = &zcu.intern_pool; 31200 switch (referenced_unit.unwrap()) { 31201 .func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced 31202 else => {}, 31203 } 31204 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return; 31205 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit); 31206 if (gop.found_existing) return; 31207 try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: { 31208 const block = opt_block orelse break :inline_frame .none; 31209 const inlining = block.inlining orelse break :inline_frame .none; 31210 const frame = try inlining.refFrame(zcu); 31211 break :inline_frame frame.toOptional(); 31212 }); 31213 } 31214 31215 pub fn addTypeReferenceEntry( 31216 sema: *Sema, 31217 src: LazySrcLoc, 31218 referenced_type: InternPool.Index, 31219 ) !void { 31220 const zcu = sema.pt.zcu; 31221 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return; 31222 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type); 31223 if (gop.found_existing) return; 31224 try zcu.addTypeReference(sema.owner, referenced_type, src); 31225 } 31226 31227 fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void { 31228 const pt = sema.pt; 31229 31230 const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); 31231 try sema.addReferenceEntry(null, src, unit); 31232 try sema.declareDependency(.{ .memoized_state = stage }); 31233 31234 if (pt.zcu.analysis_in_progress.contains(unit)) { 31235 return sema.failWithOwnedErrorMsg(null, try sema.errMsg(src, "dependency loop detected", .{})); 31236 } 31237 try pt.ensureMemoizedStateUpToDate(stage); 31238 } 31239 31240 pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void { 31241 const pt = sema.pt; 31242 const zcu = pt.zcu; 31243 const ip = &zcu.intern_pool; 31244 31245 const nav = ip.getNav(nav_index); 31246 if (nav.analysis == null) { 31247 assert(nav.status == .fully_resolved); 31248 return; 31249 } 31250 31251 try sema.declareDependency(switch (kind) { 31252 .type => .{ .nav_ty = nav_index }, 31253 .fully => .{ .nav_val = nav_index }, 31254 }); 31255 31256 // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate` 31257 // to make sure the value is up-to-date on incremental updates. 31258 31259 const anal_unit: AnalUnit = .wrap(switch (kind) { 31260 .type => .{ .nav_ty = nav_index }, 31261 .fully => .{ .nav_val = nav_index }, 31262 }); 31263 try sema.addReferenceEntry(block, src, anal_unit); 31264 31265 if (zcu.analysis_in_progress.contains(anal_unit)) { 31266 return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{ 31267 .base_node_inst = nav.analysis.?.zir_index, 31268 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 31269 }, "dependency loop detected", .{})); 31270 } 31271 31272 switch (kind) { 31273 .type => { 31274 try zcu.ensureNavValAnalysisQueued(nav_index); 31275 return pt.ensureNavTypeUpToDate(nav_index); 31276 }, 31277 .fully => return pt.ensureNavValUpToDate(nav_index), 31278 } 31279 } 31280 31281 fn optRefValue(sema: *Sema, opt_val: ?Value) !Value { 31282 const pt = sema.pt; 31283 const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque); 31284 return Value.fromInterned(try pt.intern(.{ .opt = .{ 31285 .ty = (try pt.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(), 31286 .val = if (opt_val) |val| (try pt.getCoerced( 31287 Value.fromInterned(try pt.refValue(val.toIntern())), 31288 ptr_anyopaque_ty, 31289 )).toIntern() else .none, 31290 } })); 31291 } 31292 31293 fn analyzeNavRef(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!Air.Inst.Ref { 31294 return sema.analyzeNavRefInner(block, src, nav_index, true); 31295 } 31296 31297 /// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed. 31298 /// If this pointer will be used directly, `is_ref` must be `true`. 31299 /// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`. 31300 fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref { 31301 const pt = sema.pt; 31302 const zcu = pt.zcu; 31303 const ip = &zcu.intern_pool; 31304 31305 try sema.ensureNavResolved(block, src, orig_nav_index, if (is_ref) .type else .fully); 31306 31307 const nav_index = nav: { 31308 if (ip.getNav(orig_nav_index).isExternOrFn(ip)) { 31309 // Getting a pointer to this `Nav` might mean we actually get a pointer to something else! 31310 // We need to resolve the value to know for sure. 31311 if (is_ref) try sema.ensureNavResolved(block, src, orig_nav_index, .fully); 31312 switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) { 31313 .func => |f| break :nav f.owner_nav, 31314 .@"extern" => |e| break :nav e.owner_nav, 31315 else => {}, 31316 } 31317 } 31318 break :nav orig_nav_index; 31319 }; 31320 31321 const nav_status = ip.getNav(nav_index).status; 31322 31323 const is_runtime = switch (nav_status) { 31324 .unresolved => unreachable, 31325 // dllimports go straight to `fully_resolved`; the only option is threadlocal 31326 .type_resolved => |r| r.is_threadlocal, 31327 .fully_resolved => |r| switch (ip.indexToKey(r.val)) { 31328 .@"extern" => |e| e.is_threadlocal or e.is_dll_import or switch (e.relocation) { 31329 .any => false, 31330 .pcrel => true, 31331 }, 31332 .variable => |v| v.is_threadlocal, 31333 else => false, 31334 }, 31335 }; 31336 31337 const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) { 31338 .unresolved => unreachable, 31339 .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, 31340 .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const }, 31341 }; 31342 const ptr_ty = try pt.ptrTypeSema(.{ 31343 .child = ty, 31344 .flags = .{ 31345 .alignment = alignment, 31346 .is_const = is_const, 31347 .address_space = @"addrspace", 31348 }, 31349 }); 31350 31351 if (is_runtime) { 31352 // This pointer is runtime-known; we need to emit an AIR instruction to create it. 31353 return block.addInst(.{ 31354 .tag = .runtime_nav_ptr, 31355 .data = .{ .ty_nav = .{ 31356 .ty = ptr_ty.toIntern(), 31357 .nav = nav_index, 31358 } }, 31359 }); 31360 } 31361 31362 if (is_ref) { 31363 try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index); 31364 } 31365 31366 return Air.internedToRef((try pt.intern(.{ .ptr = .{ 31367 .ty = ptr_ty.toIntern(), 31368 .base_addr = .{ .nav = nav_index }, 31369 .byte_offset = 0, 31370 } }))); 31371 } 31372 31373 fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void { 31374 const pt = sema.pt; 31375 const zcu = pt.zcu; 31376 const ip = &zcu.intern_pool; 31377 31378 // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function. 31379 // If it is, we can resolve the *value*, and queue analysis as needed. 31380 31381 try sema.ensureNavResolved(block, src, nav_index, .type); 31382 const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip)); 31383 if (nav_ty.zigTypeTag(zcu) != .@"fn") return; 31384 if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return; 31385 31386 try sema.ensureNavResolved(block, src, nav_index, .fully); 31387 const nav_val = zcu.navValue(nav_index); 31388 if (!ip.isFuncBody(nav_val.toIntern())) return; 31389 31390 const orig_fn_index = ip.unwrapCoercedFunc(nav_val.toIntern()); 31391 try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); 31392 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); 31393 } 31394 31395 fn analyzeRef( 31396 sema: *Sema, 31397 block: *Block, 31398 src: LazySrcLoc, 31399 operand: Air.Inst.Ref, 31400 ) CompileError!Air.Inst.Ref { 31401 const pt = sema.pt; 31402 const zcu = pt.zcu; 31403 const operand_ty = sema.typeOf(operand); 31404 31405 if (try sema.resolveValue(operand)) |val| { 31406 switch (zcu.intern_pool.indexToKey(val.toIntern())) { 31407 .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav), 31408 .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav), 31409 else => return uavRef(sema, val.toIntern()), 31410 } 31411 } 31412 31413 // No `requireRuntimeBlock`; it's okay to `ref` to a runtime value in a comptime context, 31414 // it's just that we can only use the *type* of the result, since the value is runtime-known. 31415 31416 const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local); 31417 const ptr_type = try pt.ptrTypeSema(.{ 31418 .child = operand_ty.toIntern(), 31419 .flags = .{ 31420 .is_const = true, 31421 .address_space = address_space, 31422 }, 31423 }); 31424 const mut_ptr_type = try pt.ptrTypeSema(.{ 31425 .child = operand_ty.toIntern(), 31426 .flags = .{ .address_space = address_space }, 31427 }); 31428 const alloc = try block.addTy(.alloc, mut_ptr_type); 31429 31430 // In a comptime context, the store would fail, since the operand is runtime-known. But that's 31431 // okay; we don't actually need this store to succeed, since we're creating a runtime value in a 31432 // comptime scope, so the value can never be used aside from to get its type. 31433 if (!block.isComptime()) { 31434 try sema.storePtr(block, src, alloc, operand); 31435 } 31436 31437 // Cast to the constant pointer type. We do this directly rather than going via `coerce` to 31438 // avoid errors in the `block.isComptime()` case. 31439 return block.addBitCast(ptr_type, alloc); 31440 } 31441 31442 fn analyzeLoad( 31443 sema: *Sema, 31444 block: *Block, 31445 src: LazySrcLoc, 31446 ptr: Air.Inst.Ref, 31447 ptr_src: LazySrcLoc, 31448 ) CompileError!Air.Inst.Ref { 31449 const pt = sema.pt; 31450 const zcu = pt.zcu; 31451 const ptr_ty = sema.typeOf(ptr); 31452 const elem_ty = switch (ptr_ty.zigTypeTag(zcu)) { 31453 .pointer => ptr_ty.childType(zcu), 31454 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}), 31455 }; 31456 if (elem_ty.zigTypeTag(zcu) == .@"opaque") { 31457 return sema.fail(block, ptr_src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)}); 31458 } 31459 31460 if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| { 31461 return Air.internedToRef(opv.toIntern()); 31462 } 31463 31464 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 31465 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| { 31466 return Air.internedToRef(elem_val.toIntern()); 31467 } 31468 } 31469 31470 return block.addTyOp(.load, elem_ty, ptr); 31471 } 31472 31473 fn analyzeSlicePtr( 31474 sema: *Sema, 31475 block: *Block, 31476 slice_src: LazySrcLoc, 31477 slice: Air.Inst.Ref, 31478 slice_ty: Type, 31479 ) CompileError!Air.Inst.Ref { 31480 const pt = sema.pt; 31481 const zcu = pt.zcu; 31482 const result_ty = slice_ty.slicePtrFieldType(zcu); 31483 if (try sema.resolveValue(slice)) |val| { 31484 if (val.isUndef(zcu)) return pt.undefRef(result_ty); 31485 return Air.internedToRef(val.slicePtr(zcu).toIntern()); 31486 } 31487 try sema.requireRuntimeBlock(block, slice_src, null); 31488 return block.addTyOp(.slice_ptr, result_ty, slice); 31489 } 31490 31491 fn analyzeOptionalSlicePtr( 31492 sema: *Sema, 31493 block: *Block, 31494 opt_slice_src: LazySrcLoc, 31495 opt_slice: Air.Inst.Ref, 31496 opt_slice_ty: Type, 31497 ) CompileError!Air.Inst.Ref { 31498 const pt = sema.pt; 31499 const zcu = pt.zcu; 31500 const slice_ty = opt_slice_ty.optionalChild(zcu); 31501 const result_ty = slice_ty.slicePtrFieldType(zcu); 31502 31503 if (try sema.resolveValue(opt_slice)) |opt_val| { 31504 if (opt_val.isUndef(zcu)) return pt.undefRef(result_ty); 31505 const slice_ptr: InternPool.Index = if (opt_val.optionalValue(zcu)) |val| 31506 val.slicePtr(zcu).toIntern() 31507 else 31508 .null_value; 31509 31510 return Air.internedToRef(slice_ptr); 31511 } 31512 31513 try sema.requireRuntimeBlock(block, opt_slice_src, null); 31514 31515 const slice = try block.addTyOp(.optional_payload, slice_ty, opt_slice); 31516 return block.addTyOp(.slice_ptr, result_ty, slice); 31517 } 31518 31519 fn analyzeSliceLen( 31520 sema: *Sema, 31521 block: *Block, 31522 src: LazySrcLoc, 31523 slice_inst: Air.Inst.Ref, 31524 ) CompileError!Air.Inst.Ref { 31525 const pt = sema.pt; 31526 const zcu = pt.zcu; 31527 if (try sema.resolveValue(slice_inst)) |slice_val| { 31528 if (slice_val.isUndef(zcu)) { 31529 return .undef_usize; 31530 } 31531 return pt.intRef(.usize, try slice_val.sliceLen(pt)); 31532 } 31533 try sema.requireRuntimeBlock(block, src, null); 31534 return block.addTyOp(.slice_len, .usize, slice_inst); 31535 } 31536 31537 fn analyzeIsNull( 31538 sema: *Sema, 31539 block: *Block, 31540 operand: Air.Inst.Ref, 31541 invert_logic: bool, 31542 ) CompileError!Air.Inst.Ref { 31543 const pt = sema.pt; 31544 const zcu = pt.zcu; 31545 const result_ty: Type = .bool; 31546 if (try sema.resolveValue(operand)) |opt_val| { 31547 if (opt_val.isUndef(zcu)) { 31548 return pt.undefRef(result_ty); 31549 } 31550 const is_null = opt_val.isNull(zcu); 31551 const bool_value = if (invert_logic) !is_null else is_null; 31552 return if (bool_value) .bool_true else .bool_false; 31553 } 31554 31555 if (sema.typeOf(operand).isNullFromType(zcu)) |is_null| { 31556 const result = is_null != invert_logic; 31557 return if (result) .bool_true else .bool_false; 31558 } 31559 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; 31560 return block.addUnOp(air_tag, operand); 31561 } 31562 31563 fn analyzePtrIsNonErrComptimeOnly( 31564 sema: *Sema, 31565 block: *Block, 31566 src: LazySrcLoc, 31567 operand: Air.Inst.Ref, 31568 ) CompileError!Air.Inst.Ref { 31569 const pt = sema.pt; 31570 const zcu = pt.zcu; 31571 const ptr_ty = sema.typeOf(operand); 31572 assert(ptr_ty.zigTypeTag(zcu) == .pointer); 31573 const child_ty = ptr_ty.childType(zcu); 31574 31575 const child_tag = child_ty.zigTypeTag(zcu); 31576 if (child_tag != .error_set and child_tag != .error_union) return .bool_true; 31577 if (child_tag == .error_set) return .bool_false; 31578 assert(child_tag == .error_union); 31579 31580 _ = block; 31581 _ = src; 31582 31583 return .none; 31584 } 31585 31586 fn analyzeIsNonErrComptimeOnly( 31587 sema: *Sema, 31588 block: *Block, 31589 src: LazySrcLoc, 31590 operand: Air.Inst.Ref, 31591 ) CompileError!Air.Inst.Ref { 31592 const pt = sema.pt; 31593 const zcu = pt.zcu; 31594 const ip = &zcu.intern_pool; 31595 const operand_ty = sema.typeOf(operand); 31596 const ot = operand_ty.zigTypeTag(zcu); 31597 if (ot != .error_set and ot != .error_union) return .bool_true; 31598 if (ot == .error_set) return .bool_false; 31599 assert(ot == .error_union); 31600 31601 const payload_ty = operand_ty.errorUnionPayload(zcu); 31602 if (payload_ty.zigTypeTag(zcu) == .noreturn) { 31603 return .bool_false; 31604 } 31605 31606 if (operand.toIndex()) |operand_inst| { 31607 switch (sema.air_instructions.items(.tag)[@intFromEnum(operand_inst)]) { 31608 .wrap_errunion_payload => return .bool_true, 31609 .wrap_errunion_err => return .bool_false, 31610 else => {}, 31611 } 31612 } else if (operand == .undef) { 31613 return .undef_bool; 31614 } else if (@intFromEnum(operand) < InternPool.static_len) { 31615 // None of the ref tags can be errors. 31616 return .bool_true; 31617 } 31618 31619 const maybe_operand_val = try sema.resolveValue(operand); 31620 31621 // exception if the error union error set is known to be empty, 31622 // we allow the comparison but always make it comptime-known. 31623 const set_ty = ip.errorUnionSet(operand_ty.toIntern()); 31624 switch (set_ty) { 31625 .anyerror_type => {}, 31626 .adhoc_inferred_error_set_type => if (sema.fn_ret_ty_ies) |ies| blk: { 31627 // If the error set is empty, we must return a comptime true or false. 31628 // However we want to avoid unnecessarily resolving an inferred error set 31629 // in case it is already non-empty. 31630 switch (ies.resolved) { 31631 .anyerror_type => break :blk, 31632 .none => {}, 31633 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, 31634 } 31635 31636 if (maybe_operand_val != null) break :blk; 31637 31638 // Try to avoid resolving inferred error set if possible. 31639 if (ies.errors.count() != 0) return .none; 31640 switch (ies.resolved) { 31641 .anyerror_type => return .none, 31642 .none => {}, 31643 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) { 31644 0 => return .bool_true, 31645 else => return .none, 31646 }, 31647 } 31648 // We do not have a comptime answer because this inferred error 31649 // set is not resolved, and an instruction later in this function 31650 // body may or may not cause an error to be added to this set. 31651 return .none; 31652 }, 31653 else => switch (ip.indexToKey(set_ty)) { 31654 .error_set_type => |error_set_type| { 31655 if (error_set_type.names.len == 0) return .bool_true; 31656 }, 31657 .inferred_error_set_type => |func_index| blk: { 31658 // If the error set is empty, we must return a comptime true or false. 31659 // However we want to avoid unnecessarily resolving an inferred error set 31660 // in case it is already non-empty. 31661 try zcu.maybeUnresolveIes(func_index); 31662 switch (ip.funcIesResolvedUnordered(func_index)) { 31663 .anyerror_type => break :blk, 31664 .none => {}, 31665 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, 31666 } 31667 if (maybe_operand_val != null) break :blk; 31668 if (sema.fn_ret_ty_ies) |ies| { 31669 if (ies.func == func_index) { 31670 // Try to avoid resolving inferred error set if possible. 31671 if (ies.errors.count() != 0) return .none; 31672 switch (ies.resolved) { 31673 .anyerror_type => return .none, 31674 .none => {}, 31675 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) { 31676 0 => return .bool_true, 31677 else => return .none, 31678 }, 31679 } 31680 // We do not have a comptime answer because this inferred error 31681 // set is not resolved, and an instruction later in this function 31682 // body may or may not cause an error to be added to this set. 31683 return .none; 31684 } 31685 } 31686 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty); 31687 if (resolved_ty == .anyerror_type) 31688 break :blk; 31689 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0) 31690 return .bool_true; 31691 }, 31692 else => unreachable, 31693 }, 31694 } 31695 31696 if (maybe_operand_val) |err_union| { 31697 return if (err_union.isUndef(zcu)) .undef_bool else if (err_union.getErrorName(zcu) == .none) .bool_true else .bool_false; 31698 } 31699 return .none; 31700 } 31701 31702 fn analyzeIsNonErr( 31703 sema: *Sema, 31704 block: *Block, 31705 src: LazySrcLoc, 31706 operand: Air.Inst.Ref, 31707 ) CompileError!Air.Inst.Ref { 31708 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); 31709 if (result == .none) { 31710 try sema.requireRuntimeBlock(block, src, null); 31711 return block.addUnOp(.is_non_err, operand); 31712 } else { 31713 return result; 31714 } 31715 } 31716 31717 fn analyzePtrIsNonErr( 31718 sema: *Sema, 31719 block: *Block, 31720 src: LazySrcLoc, 31721 operand: Air.Inst.Ref, 31722 ) CompileError!Air.Inst.Ref { 31723 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand); 31724 if (result == .none) { 31725 try sema.requireRuntimeBlock(block, src, null); 31726 return block.addUnOp(.is_non_err_ptr, operand); 31727 } else { 31728 return result; 31729 } 31730 } 31731 31732 fn analyzeSlice( 31733 sema: *Sema, 31734 block: *Block, 31735 src: LazySrcLoc, 31736 ptr_ptr: Air.Inst.Ref, 31737 uncasted_start: Air.Inst.Ref, 31738 uncasted_end_opt: Air.Inst.Ref, 31739 sentinel_opt: Air.Inst.Ref, 31740 sentinel_src: LazySrcLoc, 31741 ptr_src: LazySrcLoc, 31742 start_src: LazySrcLoc, 31743 end_src: LazySrcLoc, 31744 by_length: bool, 31745 ) CompileError!Air.Inst.Ref { 31746 const pt = sema.pt; 31747 const zcu = pt.zcu; 31748 // Slice expressions can operate on a variable whose type is an array. This requires 31749 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. 31750 const ptr_ptr_ty = sema.typeOf(ptr_ptr); 31751 const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(zcu)) { 31752 .pointer => ptr_ptr_ty.childType(zcu), 31753 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ptr_ty.fmt(pt)}), 31754 }; 31755 31756 var array_ty = ptr_ptr_child_ty; 31757 var slice_ty = ptr_ptr_ty; 31758 var ptr_or_slice = ptr_ptr; 31759 var elem_ty: Type = undefined; 31760 var ptr_sentinel: ?Value = null; 31761 switch (ptr_ptr_child_ty.zigTypeTag(zcu)) { 31762 .array => { 31763 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); 31764 elem_ty = ptr_ptr_child_ty.childType(zcu); 31765 }, 31766 .pointer => switch (ptr_ptr_child_ty.ptrSize(zcu)) { 31767 .one => { 31768 const double_child_ty = ptr_ptr_child_ty.childType(zcu); 31769 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 31770 if (double_child_ty.zigTypeTag(zcu) == .array) { 31771 ptr_sentinel = double_child_ty.sentinel(zcu); 31772 slice_ty = ptr_ptr_child_ty; 31773 array_ty = double_child_ty; 31774 elem_ty = double_child_ty.childType(zcu); 31775 } else { 31776 if (uncasted_end_opt == .none) { 31777 return sema.fail(block, src, "slice of single-item pointer must be bounded", .{}); 31778 } 31779 const start_value = try sema.resolveConstDefinedValue( 31780 block, 31781 start_src, 31782 uncasted_start, 31783 .{ .simple = .slice_single_item_ptr_bounds }, 31784 ); 31785 31786 const end_value = try sema.resolveConstDefinedValue( 31787 block, 31788 end_src, 31789 uncasted_end_opt, 31790 .{ .simple = .slice_single_item_ptr_bounds }, 31791 ); 31792 31793 const bounds_error_message = "slice of single-item pointer must have bounds [0..0], [0..1], or [1..1]"; 31794 if (try sema.compareScalar(start_value, .neq, end_value, .comptime_int)) { 31795 if (try sema.compareScalar(start_value, .neq, Value.zero_comptime_int, .comptime_int)) { 31796 const msg = msg: { 31797 const msg = try sema.errMsg(start_src, bounds_error_message, .{}); 31798 errdefer msg.destroy(sema.gpa); 31799 try sema.errNote( 31800 start_src, 31801 msg, 31802 "expected '{f}', found '{f}'", 31803 .{ 31804 Value.zero_comptime_int.fmtValueSema(pt, sema), 31805 start_value.fmtValueSema(pt, sema), 31806 }, 31807 ); 31808 break :msg msg; 31809 }; 31810 return sema.failWithOwnedErrorMsg(block, msg); 31811 } else if (try sema.compareScalar(end_value, .neq, Value.one_comptime_int, .comptime_int)) { 31812 const msg = msg: { 31813 const msg = try sema.errMsg(end_src, bounds_error_message, .{}); 31814 errdefer msg.destroy(sema.gpa); 31815 try sema.errNote( 31816 end_src, 31817 msg, 31818 "expected '{f}', found '{f}'", 31819 .{ 31820 Value.one_comptime_int.fmtValueSema(pt, sema), 31821 end_value.fmtValueSema(pt, sema), 31822 }, 31823 ); 31824 break :msg msg; 31825 }; 31826 return sema.failWithOwnedErrorMsg(block, msg); 31827 } 31828 } else { 31829 if (try sema.compareScalar(end_value, .gt, Value.one_comptime_int, .comptime_int)) { 31830 return sema.fail( 31831 block, 31832 end_src, 31833 "end index {f} out of bounds for slice of single-item pointer", 31834 .{end_value.fmtValueSema(pt, sema)}, 31835 ); 31836 } 31837 } 31838 31839 array_ty = try pt.arrayType(.{ 31840 .len = 1, 31841 .child = double_child_ty.toIntern(), 31842 }); 31843 const ptr_info = ptr_ptr_child_ty.ptrInfo(zcu); 31844 slice_ty = try pt.ptrType(.{ 31845 .child = array_ty.toIntern(), 31846 .flags = .{ 31847 .alignment = ptr_info.flags.alignment, 31848 .is_const = ptr_info.flags.is_const, 31849 .is_allowzero = ptr_info.flags.is_allowzero, 31850 .is_volatile = ptr_info.flags.is_volatile, 31851 .address_space = ptr_info.flags.address_space, 31852 }, 31853 }); 31854 elem_ty = double_child_ty; 31855 } 31856 }, 31857 .many, .c => { 31858 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); 31859 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 31860 slice_ty = ptr_ptr_child_ty; 31861 array_ty = ptr_ptr_child_ty; 31862 elem_ty = ptr_ptr_child_ty.childType(zcu); 31863 31864 if (ptr_ptr_child_ty.ptrSize(zcu) == .c) { 31865 if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| { 31866 if (ptr_val.isNull(zcu)) { 31867 return sema.fail(block, src, "slice of null pointer", .{}); 31868 } 31869 } 31870 } 31871 }, 31872 .slice => { 31873 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); 31874 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 31875 slice_ty = ptr_ptr_child_ty; 31876 array_ty = ptr_ptr_child_ty; 31877 elem_ty = ptr_ptr_child_ty.childType(zcu); 31878 }, 31879 }, 31880 else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}), 31881 } 31882 31883 const ptr = if (slice_ty.isSlice(zcu)) 31884 try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) 31885 else if (array_ty.zigTypeTag(zcu) == .array) ptr: { 31886 var manyptr_ty_key = zcu.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type; 31887 assert(manyptr_ty_key.child == array_ty.toIntern()); 31888 assert(manyptr_ty_key.flags.size == .one); 31889 manyptr_ty_key.child = elem_ty.toIntern(); 31890 manyptr_ty_key.flags.size = .many; 31891 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(manyptr_ty_key), ptr_or_slice, ptr_src); 31892 } else ptr_or_slice; 31893 31894 const start = try sema.coerce(block, .usize, uncasted_start, start_src); 31895 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); 31896 const new_ptr_ty = sema.typeOf(new_ptr); 31897 31898 // true if and only if the end index of the slice, implicitly or explicitly, equals 31899 // the length of the underlying object being sliced. we might learn the length of the 31900 // underlying object because it is an array (which has the length in the type), or 31901 // we might learn of the length because it is a comptime-known slice value. 31902 var end_is_len = uncasted_end_opt == .none; 31903 const end = e: { 31904 if (array_ty.zigTypeTag(zcu) == .array) { 31905 const len_val = try pt.intValue(.usize, array_ty.arrayLen(zcu)); 31906 31907 if (!end_is_len) { 31908 const end = if (by_length) end: { 31909 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31910 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 31911 break :end try sema.coerce(block, .usize, uncasted_end, end_src); 31912 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31913 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 31914 const len_s_val = try pt.intValue( 31915 .usize, 31916 array_ty.arrayLenIncludingSentinel(zcu), 31917 ); 31918 if (!(try sema.compareAll(end_val, .lte, len_s_val, .usize))) { 31919 const sentinel_label: []const u8 = if (array_ty.sentinel(zcu) != null) 31920 " +1 (sentinel)" 31921 else 31922 ""; 31923 31924 return sema.fail( 31925 block, 31926 end_src, 31927 "end index {f} out of bounds for array of length {f}{s}", 31928 .{ 31929 end_val.fmtValueSema(pt, sema), 31930 len_val.fmtValueSema(pt, sema), 31931 sentinel_label, 31932 }, 31933 ); 31934 } 31935 31936 // end_is_len is only true if we are NOT using the sentinel 31937 // length. For sentinel-length, we don't want the type to 31938 // contain the sentinel. 31939 if (end_val.eql(len_val, .usize, zcu)) { 31940 end_is_len = true; 31941 } 31942 } 31943 break :e end; 31944 } 31945 31946 break :e Air.internedToRef(len_val.toIntern()); 31947 } else if (slice_ty.isSlice(zcu)) { 31948 if (!end_is_len) { 31949 const end = if (by_length) end: { 31950 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31951 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 31952 break :end try sema.coerce(block, .usize, uncasted_end, end_src); 31953 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31954 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 31955 if (try sema.resolveValue(ptr_or_slice)) |slice_val| { 31956 if (slice_val.isUndef(zcu)) { 31957 return sema.fail(block, src, "slice of undefined", .{}); 31958 } 31959 const has_sentinel = slice_ty.sentinel(zcu) != null; 31960 const slice_len = try slice_val.sliceLen(pt); 31961 const len_plus_sent = slice_len + @intFromBool(has_sentinel); 31962 const slice_len_val_with_sentinel = try pt.intValue(.usize, len_plus_sent); 31963 if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, .usize))) { 31964 const sentinel_label: []const u8 = if (has_sentinel) 31965 " +1 (sentinel)" 31966 else 31967 ""; 31968 31969 return sema.fail( 31970 block, 31971 end_src, 31972 "end index {f} out of bounds for slice of length {d}{s}", 31973 .{ 31974 end_val.fmtValueSema(pt, sema), 31975 try slice_val.sliceLen(pt), 31976 sentinel_label, 31977 }, 31978 ); 31979 } 31980 31981 // If the slice has a sentinel, we consider end_is_len 31982 // is only true if it equals the length WITHOUT the 31983 // sentinel, so we don't add a sentinel type. 31984 const slice_len_val = try pt.intValue(.usize, slice_len); 31985 if (end_val.eql(slice_len_val, .usize, zcu)) { 31986 end_is_len = true; 31987 } 31988 } 31989 } 31990 break :e end; 31991 } 31992 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); 31993 } 31994 if (!end_is_len) { 31995 if (by_length) { 31996 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31997 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 31998 break :e try sema.coerce(block, .usize, uncasted_end, end_src); 31999 } else break :e try sema.coerce(block, .usize, uncasted_end_opt, end_src); 32000 } 32001 32002 // when slicing a many-item pointer, if a sentinel `S` is provided as in `ptr[a.. :S]`, it 32003 // must match the sentinel of `@TypeOf(ptr)`. 32004 sentinel_check: { 32005 if (sentinel_opt == .none) break :sentinel_check; 32006 const provided = provided: { 32007 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); 32008 try checkSentinelType(sema, block, sentinel_src, elem_ty); 32009 break :provided try sema.resolveConstDefinedValue( 32010 block, 32011 sentinel_src, 32012 casted, 32013 .{ .simple = .slice_sentinel }, 32014 ); 32015 }; 32016 32017 if (ptr_sentinel) |current| { 32018 if (provided.toIntern() == current.toIntern()) break :sentinel_check; 32019 } 32020 32021 return sema.failWithOwnedErrorMsg(block, msg: { 32022 const msg = try sema.errMsg(sentinel_src, "sentinel-terminated slicing of many-item pointer must match existing sentinel", .{}); 32023 errdefer msg.destroy(sema.gpa); 32024 if (ptr_sentinel) |current| { 32025 try sema.errNote(sentinel_src, msg, "expected sentinel '{f}', found '{f}'", .{ current.fmtValue(pt), provided.fmtValue(pt) }); 32026 } else { 32027 try sema.errNote(ptr_src, msg, "type '{f}' does not have a sentinel", .{slice_ty.fmt(pt)}); 32028 } 32029 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{}); 32030 break :msg msg; 32031 }); 32032 } 32033 return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); 32034 }; 32035 32036 const sentinel = s: { 32037 if (sentinel_opt != .none) { 32038 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); 32039 try checkSentinelType(sema, block, sentinel_src, elem_ty); 32040 break :s try sema.resolveConstDefinedValue(block, sentinel_src, casted, .{ .simple = .slice_sentinel }); 32041 } 32042 // If we are slicing to the end of something that is sentinel-terminated 32043 // then the resulting slice type is also sentinel-terminated. 32044 if (end_is_len) { 32045 if (ptr_sentinel) |sent| { 32046 break :s sent; 32047 } 32048 } 32049 break :s null; 32050 }; 32051 const slice_sentinel = if (sentinel_opt != .none) sentinel else null; 32052 32053 var checked_start_lte_end = by_length; 32054 var runtime_src: ?LazySrcLoc = null; 32055 32056 // requirement: start <= end 32057 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 32058 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { 32059 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, .usize))) { 32060 return sema.fail( 32061 block, 32062 start_src, 32063 "start index {f} is larger than end index {f}", 32064 .{ 32065 start_val.fmtValueSema(pt, sema), 32066 end_val.fmtValueSema(pt, sema), 32067 }, 32068 ); 32069 } 32070 checked_start_lte_end = true; 32071 if (try sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: { 32072 const expected_sentinel = sentinel orelse break :sentinel_check; 32073 const start_int = start_val.toUnsignedInt(zcu); 32074 const end_int = end_val.toUnsignedInt(zcu); 32075 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); 32076 32077 const many_ptr_ty = try pt.manyConstPtrType(elem_ty); 32078 const many_ptr_val = try pt.getCoerced(ptr_val, many_ptr_ty); 32079 const elem_ptr = try many_ptr_val.ptrElem(sentinel_index, pt); 32080 const res = try sema.pointerDerefExtra(block, src, elem_ptr); 32081 const actual_sentinel = switch (res) { 32082 .runtime_load => break :sentinel_check, 32083 .val => |v| v, 32084 .needed_well_defined => |ty| return sema.fail( 32085 block, 32086 src, 32087 "comptime dereference requires '{f}' to have a well-defined layout", 32088 .{ty.fmt(pt)}, 32089 ), 32090 .out_of_bounds => |ty| return sema.fail( 32091 block, 32092 end_src, 32093 "slice end index {d} exceeds bounds of containing decl of type '{f}'", 32094 .{ end_int, ty.fmt(pt) }, 32095 ), 32096 }; 32097 32098 if (!actual_sentinel.eql(expected_sentinel, elem_ty, zcu)) { 32099 const msg = msg: { 32100 const msg = try sema.errMsg(src, "value in memory does not match slice sentinel", .{}); 32101 errdefer msg.destroy(sema.gpa); 32102 try sema.errNote(src, msg, "expected '{f}', found '{f}'", .{ 32103 expected_sentinel.fmtValueSema(pt, sema), 32104 actual_sentinel.fmtValueSema(pt, sema), 32105 }); 32106 32107 break :msg msg; 32108 }; 32109 return sema.failWithOwnedErrorMsg(block, msg); 32110 } 32111 } else { 32112 runtime_src = ptr_src; 32113 } 32114 } else { 32115 runtime_src = start_src; 32116 } 32117 } else { 32118 runtime_src = end_src; 32119 } 32120 32121 if (!checked_start_lte_end and block.wantSafety() and !block.isComptime()) { 32122 // requirement: start <= end 32123 assert(!block.isComptime()); 32124 try sema.requireRuntimeBlock(block, src, runtime_src.?); 32125 const ok = try block.addBinOp(.cmp_lte, start, end); 32126 try sema.addSafetyCheckCall(block, src, ok, .@"panic.startGreaterThanEnd", &.{ start, end }); 32127 } 32128 const new_len = if (by_length) 32129 try sema.coerce(block, .usize, uncasted_end_opt, end_src) 32130 else 32131 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); 32132 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); 32133 32134 const new_ptr_ty_info = new_ptr_ty.ptrInfo(zcu); 32135 const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(zcu) != .c; 32136 32137 if (opt_new_len_val) |new_len_val| { 32138 const new_len_int = try new_len_val.toUnsignedIntSema(pt); 32139 32140 const return_ty = try pt.ptrTypeSema(.{ 32141 .child = (try pt.arrayType(.{ 32142 .len = new_len_int, 32143 .sentinel = if (sentinel) |s| s.toIntern() else .none, 32144 .child = elem_ty.toIntern(), 32145 })).toIntern(), 32146 .flags = .{ 32147 .alignment = new_ptr_ty_info.flags.alignment, 32148 .is_const = new_ptr_ty_info.flags.is_const, 32149 .is_allowzero = new_allowzero, 32150 .is_volatile = new_ptr_ty_info.flags.is_volatile, 32151 .address_space = new_ptr_ty_info.flags.address_space, 32152 }, 32153 }); 32154 32155 const opt_new_ptr_val = try sema.resolveValue(new_ptr); 32156 const new_ptr_val = opt_new_ptr_val orelse { 32157 const result = try block.addBitCast(return_ty, new_ptr); 32158 if (block.wantSafety()) { 32159 // requirement: slicing C ptr is non-null 32160 if (ptr_ptr_child_ty.isCPtr(zcu)) { 32161 const is_non_null = try sema.analyzeIsNull(block, ptr, true); 32162 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 32163 } 32164 32165 bounds_check: { 32166 const actual_len = if (array_ty.zigTypeTag(zcu) == .array) 32167 try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu)) 32168 else if (slice_ty.isSlice(zcu)) l: { 32169 const slice_len = try sema.analyzeSliceLen(block, src, ptr_or_slice); 32170 break :l if (slice_ty.sentinel(zcu) == null) 32171 slice_len 32172 else 32173 try sema.analyzeArithmetic(block, .add, slice_len, .one, src, end_src, end_src, true); 32174 } else break :bounds_check; 32175 32176 const actual_end = if (slice_sentinel != null) 32177 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 32178 else 32179 end; 32180 32181 try sema.addSafetyCheckIndexOob(block, src, actual_end, actual_len, .cmp_lte); 32182 } 32183 32184 // requirement: result[new_len] == slice_sentinel 32185 try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); 32186 } 32187 return result; 32188 }; 32189 32190 if (!new_ptr_val.isUndef(zcu)) { 32191 return Air.internedToRef((try pt.getCoerced(new_ptr_val, return_ty)).toIntern()); 32192 } 32193 32194 // Special case: @as([]i32, undefined)[x..x] 32195 if (new_len_int == 0) { 32196 return pt.undefRef(return_ty); 32197 } 32198 32199 return sema.fail(block, src, "non-zero length slice of undefined pointer", .{}); 32200 } 32201 32202 const return_ty = try pt.ptrTypeSema(.{ 32203 .child = elem_ty.toIntern(), 32204 .sentinel = if (sentinel) |s| s.toIntern() else .none, 32205 .flags = .{ 32206 .size = .slice, 32207 .alignment = new_ptr_ty_info.flags.alignment, 32208 .is_const = new_ptr_ty_info.flags.is_const, 32209 .is_volatile = new_ptr_ty_info.flags.is_volatile, 32210 .is_allowzero = new_allowzero, 32211 .address_space = new_ptr_ty_info.flags.address_space, 32212 }, 32213 }); 32214 32215 try sema.requireRuntimeBlock(block, src, runtime_src.?); 32216 if (block.wantSafety()) { 32217 // requirement: slicing C ptr is non-null 32218 if (ptr_ptr_child_ty.isCPtr(zcu)) { 32219 const is_non_null = try sema.analyzeIsNull(block, ptr, true); 32220 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 32221 } 32222 32223 // requirement: end <= len 32224 const opt_len_inst = if (array_ty.zigTypeTag(zcu) == .array) 32225 try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu)) 32226 else if (slice_ty.isSlice(zcu)) blk: { 32227 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { 32228 // we don't need to add one for sentinels because the 32229 // underlying value data includes the sentinel 32230 break :blk try pt.intRef(.usize, try slice_val.sliceLen(pt)); 32231 } 32232 32233 const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice); 32234 if (slice_ty.sentinel(zcu) == null) break :blk slice_len_inst; 32235 32236 // we have to add one because slice lengths don't include the sentinel 32237 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); 32238 } else null; 32239 if (opt_len_inst) |len_inst| { 32240 const actual_end = if (slice_sentinel != null) 32241 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 32242 else 32243 end; 32244 try sema.addSafetyCheckIndexOob(block, src, actual_end, len_inst, .cmp_lte); 32245 } 32246 32247 // requirement: start <= end 32248 try sema.addSafetyCheckIndexOob(block, src, start, end, .cmp_lte); 32249 } 32250 const result = try block.addInst(.{ 32251 .tag = .slice, 32252 .data = .{ .ty_pl = .{ 32253 .ty = Air.internedToRef(return_ty.toIntern()), 32254 .payload = try sema.addExtra(Air.Bin{ 32255 .lhs = new_ptr, 32256 .rhs = new_len, 32257 }), 32258 } }, 32259 }); 32260 if (block.wantSafety()) { 32261 // requirement: result[new_len] == slice_sentinel 32262 try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); 32263 } 32264 return result; 32265 } 32266 32267 /// Asserts that lhs and rhs types are both numeric. 32268 fn cmpNumeric( 32269 sema: *Sema, 32270 block: *Block, 32271 src: LazySrcLoc, 32272 uncasted_lhs: Air.Inst.Ref, 32273 uncasted_rhs: Air.Inst.Ref, 32274 op: std.math.CompareOperator, 32275 lhs_src: LazySrcLoc, 32276 rhs_src: LazySrcLoc, 32277 ) CompileError!Air.Inst.Ref { 32278 const pt = sema.pt; 32279 const zcu = pt.zcu; 32280 const lhs_ty = sema.typeOf(uncasted_lhs); 32281 const rhs_ty = sema.typeOf(uncasted_rhs); 32282 32283 assert(lhs_ty.isNumeric(zcu)); 32284 assert(rhs_ty.isNumeric(zcu)); 32285 32286 const lhs_ty_tag = lhs_ty.zigTypeTag(zcu); 32287 const rhs_ty_tag = rhs_ty.zigTypeTag(zcu); 32288 const target = zcu.getTarget(); 32289 32290 // One exception to heterogeneous comparison: comptime_float needs to 32291 // coerce to fixed-width float. 32292 32293 const lhs = if (lhs_ty_tag == .comptime_float and rhs_ty_tag == .float) 32294 try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src) 32295 else 32296 uncasted_lhs; 32297 32298 const rhs = if (lhs_ty_tag == .float and rhs_ty_tag == .comptime_float) 32299 try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src) 32300 else 32301 uncasted_rhs; 32302 32303 const maybe_lhs_val = try sema.resolveValue(lhs); 32304 const maybe_rhs_val = try sema.resolveValue(rhs); 32305 32306 // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value. 32307 if (maybe_lhs_val) |lhs_val| { 32308 // Result based on comparison exceeding type bounds 32309 if (!lhs_val.isUndef(zcu) and (lhs_ty_tag == .int or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) { 32310 if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| { 32311 return if (res) .bool_true else .bool_false; 32312 } 32313 } 32314 // Result based on NaN comparison 32315 if (lhs_val.isNan(zcu)) { 32316 return if (op == .neq) .bool_true else .bool_false; 32317 } 32318 // Result based on inf comparison to int 32319 if (lhs_val.isInf(zcu) and rhs_ty_tag == .int) return switch (op) { 32320 .neq => .bool_true, 32321 .eq => .bool_false, 32322 .gt, .gte => if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true, 32323 .lt, .lte => if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false, 32324 }; 32325 } 32326 32327 // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS value. 32328 if (maybe_rhs_val) |rhs_val| { 32329 // Result based on comparison exceeding type bounds 32330 if (!rhs_val.isUndef(zcu) and (rhs_ty_tag == .int or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) { 32331 if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| { 32332 return if (res) .bool_true else .bool_false; 32333 } 32334 } 32335 // Result based on NaN comparison 32336 if (rhs_val.isNan(zcu)) { 32337 return if (op == .neq) .bool_true else .bool_false; 32338 } 32339 // Result based on inf comparison to int 32340 if (rhs_val.isInf(zcu) and lhs_ty_tag == .int) return switch (op) { 32341 .neq => .bool_true, 32342 .eq => .bool_false, 32343 .gt, .gte => if (rhs_val.isNegativeInf(zcu)) .bool_true else .bool_false, 32344 .lt, .lte => if (rhs_val.isNegativeInf(zcu)) .bool_false else .bool_true, 32345 }; 32346 } 32347 32348 // Any other comparison depends on both values, so the result is undef if either is undef. 32349 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; 32350 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; 32351 32352 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: { 32353 if (maybe_rhs_val) |rhs_val| { 32354 const res = try Value.compareHeteroSema(lhs_val, op, rhs_val, pt); 32355 return if (res) .bool_true else .bool_false; 32356 } else break :rs rhs_src; 32357 } else lhs_src; 32358 32359 // TODO handle comparisons against lazy zero values 32360 // Some values can be compared against zero without being runtime-known or without forcing 32361 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to 32362 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout 32363 // of this function if we don't need to. 32364 try sema.requireRuntimeBlock(block, src, runtime_src); 32365 32366 // For floats, emit a float comparison instruction. 32367 const lhs_is_float = switch (lhs_ty_tag) { 32368 .float, .comptime_float => true, 32369 else => false, 32370 }; 32371 const rhs_is_float = switch (rhs_ty_tag) { 32372 .float, .comptime_float => true, 32373 else => false, 32374 }; 32375 32376 if (lhs_is_float and rhs_is_float) { 32377 // Smaller fixed-width floats coerce to larger fixed-width floats. 32378 // comptime_float coerces to fixed-width float. 32379 const dest_ty = x: { 32380 if (lhs_ty_tag == .comptime_float) { 32381 break :x rhs_ty; 32382 } else if (rhs_ty_tag == .comptime_float) { 32383 break :x lhs_ty; 32384 } 32385 if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) { 32386 break :x lhs_ty; 32387 } else { 32388 break :x rhs_ty; 32389 } 32390 }; 32391 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 32392 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 32393 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs); 32394 } 32395 32396 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. 32397 // For mixed signed and unsigned integers, implicit cast both operands to a signed 32398 // integer with + 1 bit. 32399 // For mixed floats and integers, extract the integer part from the float, cast that to 32400 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, 32401 // add/subtract 1. 32402 const lhs_is_signed = if (maybe_lhs_val) |lhs_val| 32403 !(try lhs_val.compareAllWithZeroSema(.gte, pt)) 32404 else 32405 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu)); 32406 const rhs_is_signed = if (maybe_rhs_val) |rhs_val| 32407 !(try rhs_val.compareAllWithZeroSema(.gte, pt)) 32408 else 32409 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu)); 32410 const dest_int_is_signed = lhs_is_signed or rhs_is_signed; 32411 32412 var dest_float_type: ?Type = null; 32413 32414 var lhs_bits: usize = undefined; 32415 if (maybe_lhs_val) |unresolved_lhs_val| { 32416 const lhs_val = try sema.resolveLazyValue(unresolved_lhs_val); 32417 if (!rhs_is_signed) { 32418 switch (lhs_val.orderAgainstZero(zcu)) { 32419 .gt => {}, 32420 .eq => switch (op) { // LHS = 0, RHS is unsigned 32421 .lte => return .bool_true, 32422 .gt => return .bool_false, 32423 else => {}, 32424 }, 32425 .lt => switch (op) { // LHS < 0, RHS is unsigned 32426 .neq, .lt, .lte => return .bool_true, 32427 .eq, .gt, .gte => return .bool_false, 32428 }, 32429 } 32430 } 32431 if (lhs_is_float) { 32432 const float = lhs_val.toFloat(f128, zcu); 32433 var big_int: std.math.big.int.Mutable = .{ 32434 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), 32435 .len = undefined, 32436 .positive = undefined, 32437 }; 32438 switch (big_int.setFloat(float, .away)) { 32439 .inexact => switch (op) { 32440 .eq => return .bool_false, 32441 .neq => return .bool_true, 32442 else => {}, 32443 }, 32444 .exact => {}, 32445 } 32446 lhs_bits = big_int.toConst().bitCountTwosComp(); 32447 } else { 32448 lhs_bits = lhs_val.intBitCountTwosComp(zcu); 32449 } 32450 lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed); 32451 } else if (lhs_is_float) { 32452 dest_float_type = lhs_ty; 32453 } else { 32454 const int_info = lhs_ty.intInfo(zcu); 32455 lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 32456 } 32457 32458 var rhs_bits: usize = undefined; 32459 if (maybe_rhs_val) |unresolved_rhs_val| { 32460 const rhs_val = try sema.resolveLazyValue(unresolved_rhs_val); 32461 if (!lhs_is_signed) { 32462 switch (rhs_val.orderAgainstZero(zcu)) { 32463 .gt => {}, 32464 .eq => switch (op) { // RHS = 0, LHS is unsigned 32465 .gte => return .bool_true, 32466 .lt => return .bool_false, 32467 else => {}, 32468 }, 32469 .lt => switch (op) { // RHS < 0, LHS is unsigned 32470 .neq, .gt, .gte => return .bool_true, 32471 .eq, .lt, .lte => return .bool_false, 32472 }, 32473 } 32474 } 32475 if (rhs_is_float) { 32476 const float = rhs_val.toFloat(f128, zcu); 32477 var big_int: std.math.big.int.Mutable = .{ 32478 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), 32479 .len = undefined, 32480 .positive = undefined, 32481 }; 32482 switch (big_int.setFloat(float, .away)) { 32483 .inexact => switch (op) { 32484 .eq => return .bool_false, 32485 .neq => return .bool_true, 32486 else => {}, 32487 }, 32488 .exact => {}, 32489 } 32490 rhs_bits = big_int.toConst().bitCountTwosComp(); 32491 } else { 32492 rhs_bits = rhs_val.intBitCountTwosComp(zcu); 32493 } 32494 rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed); 32495 } else if (rhs_is_float) { 32496 dest_float_type = rhs_ty; 32497 } else { 32498 const int_info = rhs_ty.intInfo(zcu); 32499 rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 32500 } 32501 32502 const dest_ty = if (dest_float_type) |ft| ft else blk: { 32503 const max_bits = @max(lhs_bits, rhs_bits); 32504 const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}); 32505 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned; 32506 break :blk try pt.intType(signedness, casted_bits); 32507 }; 32508 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 32509 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 32510 32511 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs); 32512 } 32513 32514 /// Asserts that LHS value is an int or comptime int and not undefined, and 32515 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to 32516 /// determine whether `op` has a guaranteed result. 32517 /// If it cannot be determined, returns null. 32518 /// Otherwise returns a bool for the guaranteed comparison operation. 32519 fn compareIntsOnlyPossibleResult( 32520 sema: *Sema, 32521 lhs_val: Value, 32522 op: std.math.CompareOperator, 32523 rhs_ty: Type, 32524 ) SemaError!?bool { 32525 const pt = sema.pt; 32526 const zcu = pt.zcu; 32527 32528 const min_rhs = try rhs_ty.minInt(pt, rhs_ty); 32529 const max_rhs = try rhs_ty.maxInt(pt, rhs_ty); 32530 32531 if (min_rhs.toIntern() == max_rhs.toIntern()) { 32532 // RHS is effectively comptime-known. 32533 return try Value.compareHeteroSema(lhs_val, op, min_rhs, pt); 32534 } 32535 32536 const against_min = try lhs_val.orderAdvanced(min_rhs, .sema, zcu, pt.tid); 32537 const against_max = try lhs_val.orderAdvanced(max_rhs, .sema, zcu, pt.tid); 32538 32539 switch (op) { 32540 .eq => { 32541 if (against_min.compare(.lt)) return false; 32542 if (against_max.compare(.gt)) return false; 32543 }, 32544 .neq => { 32545 if (against_min.compare(.lt)) return true; 32546 if (against_max.compare(.gt)) return true; 32547 }, 32548 .lt => { 32549 if (against_min.compare(.lt)) return true; 32550 if (against_max.compare(.gte)) return false; 32551 }, 32552 .gt => { 32553 if (against_max.compare(.gt)) return true; 32554 if (against_min.compare(.lte)) return false; 32555 }, 32556 .lte => { 32557 if (against_min.compare(.lte)) return true; 32558 if (against_max.compare(.gt)) return false; 32559 }, 32560 .gte => { 32561 if (against_max.compare(.gte)) return true; 32562 if (against_min.compare(.lt)) return false; 32563 }, 32564 } 32565 32566 return null; 32567 } 32568 32569 /// Asserts that lhs and rhs types are both vectors. 32570 fn cmpVector( 32571 sema: *Sema, 32572 block: *Block, 32573 src: LazySrcLoc, 32574 lhs: Air.Inst.Ref, 32575 rhs: Air.Inst.Ref, 32576 op: std.math.CompareOperator, 32577 lhs_src: LazySrcLoc, 32578 rhs_src: LazySrcLoc, 32579 ) CompileError!Air.Inst.Ref { 32580 const pt = sema.pt; 32581 const zcu = pt.zcu; 32582 const lhs_ty = sema.typeOf(lhs); 32583 const rhs_ty = sema.typeOf(rhs); 32584 assert(lhs_ty.zigTypeTag(zcu) == .vector); 32585 assert(rhs_ty.zigTypeTag(zcu) == .vector); 32586 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 32587 32588 const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } }); 32589 const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src); 32590 const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src); 32591 32592 const result_ty = try pt.vectorType(.{ 32593 .len = lhs_ty.vectorLen(zcu), 32594 .child = .bool_type, 32595 }); 32596 32597 const maybe_lhs_val = try sema.resolveValue(casted_lhs); 32598 const maybe_rhs_val = try sema.resolveValue(casted_rhs); 32599 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty); 32600 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty); 32601 32602 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: { 32603 if (maybe_rhs_val) |rhs_val| { 32604 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty); 32605 return Air.internedToRef(cmp_val.toIntern()); 32606 } else break :src rhs_src; 32607 } else lhs_src; 32608 32609 try sema.requireRuntimeBlock(block, src, runtime_src); 32610 return block.addCmpVector(casted_lhs, casted_rhs, op); 32611 } 32612 32613 fn wrapOptional( 32614 sema: *Sema, 32615 block: *Block, 32616 dest_ty: Type, 32617 inst: Air.Inst.Ref, 32618 inst_src: LazySrcLoc, 32619 ) !Air.Inst.Ref { 32620 if (try sema.resolveValue(inst)) |val| { 32621 return Air.internedToRef((try sema.pt.intern(.{ .opt = .{ 32622 .ty = dest_ty.toIntern(), 32623 .val = val.toIntern(), 32624 } }))); 32625 } 32626 32627 try sema.requireRuntimeBlock(block, inst_src, null); 32628 return block.addTyOp(.wrap_optional, dest_ty, inst); 32629 } 32630 32631 fn wrapErrorUnionPayload( 32632 sema: *Sema, 32633 block: *Block, 32634 dest_ty: Type, 32635 inst: Air.Inst.Ref, 32636 inst_src: LazySrcLoc, 32637 ) !Air.Inst.Ref { 32638 const pt = sema.pt; 32639 const zcu = pt.zcu; 32640 const dest_payload_ty = dest_ty.errorUnionPayload(zcu); 32641 const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); 32642 if (try sema.resolveValue(coerced)) |val| { 32643 return Air.internedToRef((try pt.intern(.{ .error_union = .{ 32644 .ty = dest_ty.toIntern(), 32645 .val = .{ .payload = val.toIntern() }, 32646 } }))); 32647 } 32648 try sema.requireRuntimeBlock(block, inst_src, null); 32649 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); 32650 } 32651 32652 fn wrapErrorUnionSet( 32653 sema: *Sema, 32654 block: *Block, 32655 dest_ty: Type, 32656 inst: Air.Inst.Ref, 32657 inst_src: LazySrcLoc, 32658 ) !Air.Inst.Ref { 32659 const pt = sema.pt; 32660 const zcu = pt.zcu; 32661 const ip = &zcu.intern_pool; 32662 const inst_ty = sema.typeOf(inst); 32663 const dest_err_set_ty = dest_ty.errorUnionSet(zcu); 32664 if (try sema.resolveValue(inst)) |val| { 32665 const expected_name = zcu.intern_pool.indexToKey(val.toIntern()).err.name; 32666 switch (dest_err_set_ty.toIntern()) { 32667 .anyerror_type => {}, 32668 .adhoc_inferred_error_set_type => ok: { 32669 const ies = sema.fn_ret_ty_ies.?; 32670 switch (ies.resolved) { 32671 .anyerror_type => break :ok, 32672 .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { 32673 break :ok; 32674 }, 32675 else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) { 32676 break :ok; 32677 }, 32678 } 32679 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 32680 }, 32681 else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) { 32682 .error_set_type => |error_set_type| ok: { 32683 if (error_set_type.nameIndex(ip, expected_name) != null) break :ok; 32684 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 32685 }, 32686 .inferred_error_set_type => |func_index| ok: { 32687 // We carefully do this in an order that avoids unnecessarily 32688 // resolving the destination error set type. 32689 try zcu.maybeUnresolveIes(func_index); 32690 switch (ip.funcIesResolvedUnordered(func_index)) { 32691 .anyerror_type => break :ok, 32692 .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { 32693 break :ok; 32694 }, 32695 else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) { 32696 break :ok; 32697 }, 32698 } 32699 32700 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 32701 }, 32702 else => unreachable, 32703 }, 32704 } 32705 return Air.internedToRef((try pt.intern(.{ .error_union = .{ 32706 .ty = dest_ty.toIntern(), 32707 .val = .{ .err_name = expected_name }, 32708 } }))); 32709 } 32710 32711 try sema.requireRuntimeBlock(block, inst_src, null); 32712 const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src); 32713 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); 32714 } 32715 32716 fn unionToTag( 32717 sema: *Sema, 32718 block: *Block, 32719 enum_ty: Type, 32720 un: Air.Inst.Ref, 32721 un_src: LazySrcLoc, 32722 ) !Air.Inst.Ref { 32723 const pt = sema.pt; 32724 const zcu = pt.zcu; 32725 if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| { 32726 return Air.internedToRef(opv.toIntern()); 32727 } 32728 if (try sema.resolveValue(un)) |un_val| { 32729 const tag_val = un_val.unionTag(zcu).?; 32730 if (tag_val.isUndef(zcu)) 32731 return try pt.undefRef(enum_ty); 32732 return Air.internedToRef(tag_val.toIntern()); 32733 } 32734 try sema.requireRuntimeBlock(block, un_src, null); 32735 return block.addTyOp(.get_union_tag, enum_ty, un); 32736 } 32737 32738 const PeerResolveStrategy = enum { 32739 /// The type is not known. 32740 /// If refined no further, this is equivalent to `exact`. 32741 unknown, 32742 /// The type may be an error set or error union. 32743 /// If refined no further, it is an error set. 32744 error_set, 32745 /// The type must be some error union. 32746 error_union, 32747 /// The type may be @TypeOf(null), an optional or a C pointer. 32748 /// If refined no further, it is @TypeOf(null). 32749 nullable, 32750 /// The type must be some optional or a C pointer. 32751 /// If refined no further, it is an optional. 32752 optional, 32753 /// The type must be either an array or a vector. 32754 /// If refined no further, it is an array. 32755 array, 32756 /// The type must be a vector. 32757 vector, 32758 /// The type must be a C pointer. 32759 c_ptr, 32760 /// The type must be a pointer (C or not). 32761 /// If refined no further, it is a non-C pointer. 32762 ptr, 32763 /// The type must be a function or a pointer to a function. 32764 /// If refined no further, it is a function. 32765 func, 32766 /// The type must be an enum literal, or some specific enum or union. Which one is decided 32767 /// afterwards based on the types in question. 32768 enum_or_union, 32769 /// The type must be some integer or float type. 32770 /// If refined no further, it is `comptime_int`. 32771 comptime_int, 32772 /// The type must be some float type. 32773 /// If refined no further, it is `comptime_float`. 32774 comptime_float, 32775 /// The type must be some float or fixed-width integer type. 32776 /// If refined no further, it is some fixed-width integer type. 32777 fixed_int, 32778 /// The type must be some fixed-width float type. 32779 fixed_float, 32780 /// The type must be a tuple. 32781 tuple, 32782 /// The peers must all be of the same type. 32783 exact, 32784 32785 /// Given two strategies, find a strategy that satisfies both, if one exists. If no such 32786 /// strategy exists, any strategy may be returned; an error will be emitted when the caller 32787 /// attempts to use the strategy to resolve the type. 32788 /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at 32789 /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy. 32790 fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy { 32791 // Our merging should be order-independent. Thus, even though the union order is arbitrary, 32792 // by sorting the tags and switching first on the smaller, we have half as many cases to 32793 // worry about (since we avoid the duplicates). 32794 const s0_is_a = @intFromEnum(a) <= @intFromEnum(b); 32795 const s0 = if (s0_is_a) a else b; 32796 const s1 = if (s0_is_a) b else a; 32797 32798 const ReasonMethod = enum { 32799 all_s0, 32800 all_s1, 32801 either, 32802 }; 32803 32804 const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) { 32805 .unknown => .{ .all_s1, s1 }, 32806 .error_set => switch (s1) { 32807 .error_set => .{ .either, .error_set }, 32808 else => .{ .all_s0, .error_union }, 32809 }, 32810 .error_union => switch (s1) { 32811 .error_union => .{ .either, .error_union }, 32812 else => .{ .all_s0, .error_union }, 32813 }, 32814 .nullable => switch (s1) { 32815 .nullable => .{ .either, .nullable }, 32816 .c_ptr => .{ .all_s1, .c_ptr }, 32817 else => .{ .all_s0, .optional }, 32818 }, 32819 .optional => switch (s1) { 32820 .optional => .{ .either, .optional }, 32821 .c_ptr => .{ .all_s1, .c_ptr }, 32822 else => .{ .all_s0, .optional }, 32823 }, 32824 .array => switch (s1) { 32825 .array => .{ .either, .array }, 32826 .vector => .{ .all_s1, .vector }, 32827 else => .{ .all_s0, .array }, 32828 }, 32829 .vector => switch (s1) { 32830 .vector => .{ .either, .vector }, 32831 else => .{ .all_s0, .vector }, 32832 }, 32833 .c_ptr => switch (s1) { 32834 .c_ptr => .{ .either, .c_ptr }, 32835 else => .{ .all_s0, .c_ptr }, 32836 }, 32837 .ptr => switch (s1) { 32838 .ptr => .{ .either, .ptr }, 32839 else => .{ .all_s0, .ptr }, 32840 }, 32841 .func => switch (s1) { 32842 .func => .{ .either, .func }, 32843 else => .{ .all_s1, s1 }, // doesn't override anything later 32844 }, 32845 .enum_or_union => switch (s1) { 32846 .enum_or_union => .{ .either, .enum_or_union }, 32847 else => .{ .all_s0, .enum_or_union }, 32848 }, 32849 .comptime_int => switch (s1) { 32850 .comptime_int => .{ .either, .comptime_int }, 32851 else => .{ .all_s1, s1 }, // doesn't override anything later 32852 }, 32853 .comptime_float => switch (s1) { 32854 .comptime_float => .{ .either, .comptime_float }, 32855 else => .{ .all_s1, s1 }, // doesn't override anything later 32856 }, 32857 .fixed_int => switch (s1) { 32858 .fixed_int => .{ .either, .fixed_int }, 32859 else => .{ .all_s1, s1 }, // doesn't override anything later 32860 }, 32861 .fixed_float => switch (s1) { 32862 .fixed_float => .{ .either, .fixed_float }, 32863 else => .{ .all_s1, s1 }, // doesn't override anything later 32864 }, 32865 .tuple => switch (s1) { 32866 .exact => .{ .all_s1, .exact }, 32867 else => .{ .all_s0, .tuple }, 32868 }, 32869 .exact => .{ .all_s0, .exact }, 32870 }; 32871 32872 switch (reason_method) { 32873 .all_s0 => { 32874 if (!s0_is_a) { 32875 reason_peer.* = b_peer_idx; 32876 } 32877 }, 32878 .all_s1 => { 32879 if (s0_is_a) { 32880 reason_peer.* = b_peer_idx; 32881 } 32882 }, 32883 .either => { 32884 // Prefer the earliest peer 32885 reason_peer.* = @min(reason_peer.*, b_peer_idx); 32886 }, 32887 } 32888 32889 return strat; 32890 } 32891 32892 fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy { 32893 return switch (ty.zigTypeTag(zcu)) { 32894 .type, .void, .bool, .@"opaque", .frame, .@"anyframe" => .exact, 32895 .noreturn, .undefined => .unknown, 32896 .null => .nullable, 32897 .comptime_int => .comptime_int, 32898 .int => .fixed_int, 32899 .comptime_float => .comptime_float, 32900 .float => .fixed_float, 32901 .pointer => if (ty.ptrInfo(zcu).flags.size == .c) .c_ptr else .ptr, 32902 .array => .array, 32903 .vector => .vector, 32904 .optional => .optional, 32905 .error_set => .error_set, 32906 .error_union => .error_union, 32907 .enum_literal, .@"enum", .@"union" => .enum_or_union, 32908 .@"struct" => if (ty.isTuple(zcu)) .tuple else .exact, 32909 .@"fn" => .func, 32910 }; 32911 } 32912 }; 32913 32914 const PeerTypeCandidateSrc = union(enum) { 32915 /// Do not print out error notes for candidate sources 32916 none: void, 32917 /// When we want to know the the src of candidate i, look up at 32918 /// index i in this slice 32919 override: []const ?LazySrcLoc, 32920 /// resolvePeerTypes originates from a @TypeOf(...) call 32921 typeof_builtin_call_node_offset: std.zig.Ast.Node.Offset, 32922 32923 pub fn resolve( 32924 self: PeerTypeCandidateSrc, 32925 block: *Block, 32926 candidate_i: usize, 32927 ) ?LazySrcLoc { 32928 return switch (self) { 32929 .none => null, 32930 .override => |candidate_srcs| if (candidate_i >= candidate_srcs.len) 32931 null 32932 else 32933 candidate_srcs[candidate_i], 32934 .typeof_builtin_call_node_offset => |node_offset| block.builtinCallArgSrc(node_offset, @intCast(candidate_i)), 32935 }; 32936 } 32937 }; 32938 32939 const PeerResolveResult = union(enum) { 32940 /// The peer type resolution was successful, and resulted in the given type. 32941 success: Type, 32942 /// There was some generic conflict between two peers. 32943 conflict: struct { 32944 peer_idx_a: usize, 32945 peer_idx_b: usize, 32946 }, 32947 /// There was an error when resolving the type of a struct or tuple field. 32948 field_error: struct { 32949 /// The name of the field which caused the failure. 32950 field_name: InternPool.NullTerminatedString, 32951 /// The type of this field in each peer. 32952 field_types: []Type, 32953 /// The error from resolving the field type. Guaranteed not to be `success`. 32954 sub_result: *PeerResolveResult, 32955 }, 32956 32957 fn report( 32958 result: PeerResolveResult, 32959 sema: *Sema, 32960 block: *Block, 32961 src: LazySrcLoc, 32962 instructions: []const Air.Inst.Ref, 32963 candidate_srcs: PeerTypeCandidateSrc, 32964 ) !*Zcu.ErrorMsg { 32965 const pt = sema.pt; 32966 32967 var opt_msg: ?*Zcu.ErrorMsg = null; 32968 errdefer if (opt_msg) |msg| msg.destroy(sema.gpa); 32969 32970 // If we mention fields we'll want to include field types, so put peer types in a buffer 32971 var peer_tys = try sema.arena.alloc(Type, instructions.len); 32972 for (peer_tys, instructions) |*ty, inst| { 32973 ty.* = sema.typeOf(inst); 32974 } 32975 32976 var cur = result; 32977 while (true) { 32978 var conflict_idx: [2]usize = undefined; 32979 32980 switch (cur) { 32981 .success => unreachable, 32982 .conflict => |conflict| { 32983 // Fall through to two-peer conflict handling below 32984 conflict_idx = .{ 32985 conflict.peer_idx_a, 32986 conflict.peer_idx_b, 32987 }; 32988 }, 32989 .field_error => |field_error| { 32990 const fmt = "struct field '{f}' has conflicting types"; 32991 const args = .{field_error.field_name.fmt(&pt.zcu.intern_pool)}; 32992 if (opt_msg) |msg| { 32993 try sema.errNote(src, msg, fmt, args); 32994 } else { 32995 opt_msg = try sema.errMsg(src, fmt, args); 32996 } 32997 32998 // Continue on to child error 32999 cur = field_error.sub_result.*; 33000 peer_tys = field_error.field_types; 33001 continue; 33002 }, 33003 } 33004 33005 // This is the path for reporting a generic conflict between two peers. 33006 33007 if (conflict_idx[1] < conflict_idx[0]) { 33008 // b comes first in source, so it's better if it comes first in the error 33009 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]); 33010 } 33011 33012 const conflict_tys: [2]Type = .{ 33013 peer_tys[conflict_idx[0]], 33014 peer_tys[conflict_idx[1]], 33015 }; 33016 const conflict_srcs: [2]?LazySrcLoc = .{ 33017 candidate_srcs.resolve(block, conflict_idx[0]), 33018 candidate_srcs.resolve(block, conflict_idx[1]), 33019 }; 33020 33021 const fmt = "incompatible types: '{f}' and '{f}'"; 33022 const args = .{ 33023 conflict_tys[0].fmt(pt), 33024 conflict_tys[1].fmt(pt), 33025 }; 33026 const msg = if (opt_msg) |msg| msg: { 33027 try sema.errNote(src, msg, fmt, args); 33028 break :msg msg; 33029 } else msg: { 33030 const msg = try sema.errMsg(src, fmt, args); 33031 opt_msg = msg; 33032 break :msg msg; 33033 }; 33034 33035 if (conflict_srcs[0]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[0].fmt(pt)}); 33036 if (conflict_srcs[1]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[1].fmt(pt)}); 33037 33038 // No child error 33039 break; 33040 } 33041 33042 return opt_msg.?; 33043 } 33044 }; 33045 33046 fn resolvePeerTypes( 33047 sema: *Sema, 33048 block: *Block, 33049 src: LazySrcLoc, 33050 instructions: []const Air.Inst.Ref, 33051 candidate_srcs: PeerTypeCandidateSrc, 33052 ) !Type { 33053 switch (instructions.len) { 33054 0 => return .noreturn, 33055 1 => return sema.typeOf(instructions[0]), 33056 else => {}, 33057 } 33058 33059 // Fast path: check if everything has the same type to bypass the main PTR logic. 33060 same_type: { 33061 const ty = sema.typeOf(instructions[0]); 33062 for (instructions[1..]) |inst| { 33063 if (sema.typeOf(inst).toIntern() != ty.toIntern()) { 33064 break :same_type; 33065 } 33066 } 33067 return ty; 33068 } 33069 33070 const peer_tys = try sema.arena.alloc(?Type, instructions.len); 33071 const peer_vals = try sema.arena.alloc(?Value, instructions.len); 33072 33073 for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { 33074 ty.* = sema.typeOf(inst); 33075 val.* = try sema.resolveValue(inst); 33076 } 33077 33078 switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { 33079 .success => |ty| return ty, 33080 else => |result| { 33081 const msg = try result.report(sema, block, src, instructions, candidate_srcs); 33082 return sema.failWithOwnedErrorMsg(block, msg); 33083 }, 33084 } 33085 } 33086 33087 fn resolvePeerTypesInner( 33088 sema: *Sema, 33089 block: *Block, 33090 src: LazySrcLoc, 33091 peer_tys: []?Type, 33092 peer_vals: []?Value, 33093 ) !PeerResolveResult { 33094 const pt = sema.pt; 33095 const zcu = pt.zcu; 33096 const ip = &zcu.intern_pool; 33097 33098 var strat_reason: usize = 0; 33099 var s: PeerResolveStrategy = .unknown; 33100 for (peer_tys, 0..) |opt_ty, i| { 33101 const ty = opt_ty orelse continue; 33102 s = s.merge(PeerResolveStrategy.select(ty, zcu), &strat_reason, i); 33103 } 33104 33105 if (s == .unknown) { 33106 // The whole thing was noreturn or undefined - try to do an exact match 33107 s = .exact; 33108 } else { 33109 // There was something other than noreturn and undefined, so we can ignore those peers 33110 for (peer_tys) |*ty_ptr| { 33111 const ty = ty_ptr.* orelse continue; 33112 switch (ty.zigTypeTag(zcu)) { 33113 .noreturn, .undefined => ty_ptr.* = null, 33114 else => {}, 33115 } 33116 } 33117 } 33118 33119 const target = zcu.getTarget(); 33120 33121 switch (s) { 33122 .unknown => unreachable, 33123 33124 .error_set => { 33125 var final_set: ?Type = null; 33126 for (peer_tys, 0..) |opt_ty, i| { 33127 const ty = opt_ty orelse continue; 33128 if (ty.zigTypeTag(zcu) != .error_set) return .{ .conflict = .{ 33129 .peer_idx_a = strat_reason, 33130 .peer_idx_b = i, 33131 } }; 33132 if (final_set) |cur_set| { 33133 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty); 33134 } else { 33135 final_set = ty; 33136 } 33137 } 33138 return .{ .success = final_set.? }; 33139 }, 33140 33141 .error_union => { 33142 var final_set: ?Type = null; 33143 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 33144 const ty = ty_ptr.* orelse continue; 33145 const set_ty = switch (ty.zigTypeTag(zcu)) { 33146 .error_set => blk: { 33147 ty_ptr.* = null; // no payload to decide on 33148 val_ptr.* = null; 33149 break :blk ty; 33150 }, 33151 .error_union => blk: { 33152 const set_ty = ty.errorUnionSet(zcu); 33153 ty_ptr.* = ty.errorUnionPayload(zcu); 33154 if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) { 33155 .error_union => |eu| switch (eu.val) { 33156 .payload => |payload_ip| val_ptr.* = Value.fromInterned(payload_ip), 33157 .err_name => val_ptr.* = null, 33158 }, 33159 .undef => val_ptr.* = Value.fromInterned(try pt.intern(.{ .undef = ty_ptr.*.?.toIntern() })), 33160 else => unreachable, 33161 }; 33162 break :blk set_ty; 33163 }, 33164 else => continue, // whole type is the payload 33165 }; 33166 if (final_set) |cur_set| { 33167 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty); 33168 } else { 33169 final_set = set_ty; 33170 } 33171 } 33172 assert(final_set != null); 33173 const final_payload = switch (try sema.resolvePeerTypesInner( 33174 block, 33175 src, 33176 peer_tys, 33177 peer_vals, 33178 )) { 33179 .success => |ty| ty, 33180 else => |result| return result, 33181 }; 33182 return .{ .success = try pt.errorUnionType(final_set.?, final_payload) }; 33183 }, 33184 33185 .nullable => { 33186 for (peer_tys, 0..) |opt_ty, i| { 33187 const ty = opt_ty orelse continue; 33188 if (!ty.eql(.null, zcu)) return .{ .conflict = .{ 33189 .peer_idx_a = strat_reason, 33190 .peer_idx_b = i, 33191 } }; 33192 } 33193 return .{ .success = .null }; 33194 }, 33195 33196 .optional => { 33197 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 33198 const ty = ty_ptr.* orelse continue; 33199 switch (ty.zigTypeTag(zcu)) { 33200 .null => { 33201 ty_ptr.* = null; 33202 val_ptr.* = null; 33203 }, 33204 .optional => { 33205 ty_ptr.* = ty.optionalChild(zcu); 33206 if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(zcu)) opt_val.optionalValue(zcu) else null; 33207 }, 33208 else => {}, 33209 } 33210 } 33211 const child_ty = switch (try sema.resolvePeerTypesInner( 33212 block, 33213 src, 33214 peer_tys, 33215 peer_vals, 33216 )) { 33217 .success => |ty| ty, 33218 else => |result| return result, 33219 }; 33220 return .{ .success = try pt.optionalType(child_ty.toIntern()) }; 33221 }, 33222 33223 .array => { 33224 // Index of the first non-null peer 33225 var opt_first_idx: ?usize = null; 33226 // Index of the first array or vector peer (i.e. not a tuple) 33227 var opt_first_arr_idx: ?usize = null; 33228 // Set to non-null once we see any peer, even a tuple 33229 var len: u64 = undefined; 33230 var sentinel: ?Value = undefined; 33231 // Only set once we see a non-tuple peer 33232 var elem_ty: Type = undefined; 33233 33234 for (peer_tys, 0..) |*ty_ptr, i| { 33235 const ty = ty_ptr.* orelse continue; 33236 33237 if (!ty.isArrayOrVector(zcu)) { 33238 // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced. 33239 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 33240 .peer_idx_a = strat_reason, 33241 .peer_idx_b = i, 33242 } }; 33243 33244 if (opt_first_idx) |first_idx| { 33245 if (arr_like.len != len) return .{ .conflict = .{ 33246 .peer_idx_a = first_idx, 33247 .peer_idx_b = i, 33248 } }; 33249 } else { 33250 opt_first_idx = i; 33251 len = arr_like.len; 33252 } 33253 33254 sentinel = null; 33255 33256 continue; 33257 } 33258 33259 const first_arr_idx = opt_first_arr_idx orelse { 33260 if (opt_first_idx == null) { 33261 opt_first_idx = i; 33262 len = ty.arrayLen(zcu); 33263 sentinel = ty.sentinel(zcu); 33264 } 33265 opt_first_arr_idx = i; 33266 elem_ty = ty.childType(zcu); 33267 continue; 33268 }; 33269 33270 if (ty.arrayLen(zcu) != len) return .{ .conflict = .{ 33271 .peer_idx_a = first_arr_idx, 33272 .peer_idx_b = i, 33273 } }; 33274 33275 const peer_elem_ty = ty.childType(zcu); 33276 if (!peer_elem_ty.eql(elem_ty, zcu)) coerce: { 33277 const peer_elem_coerces_to_elem = 33278 try sema.coerceInMemoryAllowed(block, elem_ty, peer_elem_ty, false, zcu.getTarget(), src, src, null); 33279 if (peer_elem_coerces_to_elem == .ok) { 33280 break :coerce; 33281 } 33282 33283 const elem_coerces_to_peer_elem = 33284 try sema.coerceInMemoryAllowed(block, peer_elem_ty, elem_ty, false, zcu.getTarget(), src, src, null); 33285 if (elem_coerces_to_peer_elem == .ok) { 33286 elem_ty = peer_elem_ty; 33287 break :coerce; 33288 } 33289 33290 return .{ .conflict = .{ 33291 .peer_idx_a = first_arr_idx, 33292 .peer_idx_b = i, 33293 } }; 33294 } 33295 33296 if (sentinel) |cur_sent| { 33297 if (ty.sentinel(zcu)) |peer_sent| { 33298 if (!peer_sent.eql(cur_sent, elem_ty, zcu)) sentinel = null; 33299 } else { 33300 sentinel = null; 33301 } 33302 } 33303 } 33304 33305 // There should always be at least one array or vector peer 33306 assert(opt_first_arr_idx != null); 33307 33308 return .{ .success = try pt.arrayType(.{ 33309 .len = len, 33310 .child = elem_ty.toIntern(), 33311 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none, 33312 }) }; 33313 }, 33314 33315 .vector => { 33316 var len: ?u64 = null; 33317 var first_idx: usize = undefined; 33318 for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| { 33319 const ty = ty_ptr.* orelse continue; 33320 33321 if (!ty.isArrayOrVector(zcu)) { 33322 // Allow tuples of the correct length 33323 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 33324 .peer_idx_a = strat_reason, 33325 .peer_idx_b = i, 33326 } }; 33327 33328 if (len) |expect_len| { 33329 if (arr_like.len != expect_len) return .{ .conflict = .{ 33330 .peer_idx_a = first_idx, 33331 .peer_idx_b = i, 33332 } }; 33333 } else { 33334 len = arr_like.len; 33335 first_idx = i; 33336 } 33337 33338 // Tuples won't participate in the child type resolution. We'll resolve without 33339 // them, and if the tuples have a bad type, we'll get a coercion error later. 33340 ty_ptr.* = null; 33341 val_ptr.* = null; 33342 33343 continue; 33344 } 33345 33346 if (len) |expect_len| { 33347 if (ty.arrayLen(zcu) != expect_len) return .{ .conflict = .{ 33348 .peer_idx_a = first_idx, 33349 .peer_idx_b = i, 33350 } }; 33351 } else { 33352 len = ty.arrayLen(zcu); 33353 first_idx = i; 33354 } 33355 33356 ty_ptr.* = ty.childType(zcu); 33357 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR 33358 } 33359 33360 const child_ty = switch (try sema.resolvePeerTypesInner( 33361 block, 33362 src, 33363 peer_tys, 33364 peer_vals, 33365 )) { 33366 .success => |ty| ty, 33367 else => |result| return result, 33368 }; 33369 33370 return .{ .success = try pt.vectorType(.{ 33371 .len = @intCast(len.?), 33372 .child = child_ty.toIntern(), 33373 }) }; 33374 }, 33375 33376 .c_ptr => { 33377 var opt_ptr_info: ?InternPool.Key.PtrType = null; 33378 var first_idx: usize = undefined; 33379 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 33380 const ty = opt_ty orelse continue; 33381 switch (ty.zigTypeTag(zcu)) { 33382 .comptime_int => continue, // comptime-known integers can always coerce to C pointers 33383 .int => { 33384 if (opt_val != null) { 33385 // Always allow the coercion for comptime-known ints 33386 continue; 33387 } else { 33388 // Runtime-known, so check if the type is no bigger than a usize 33389 const ptr_bits = target.ptrBitWidth(); 33390 const bits = ty.intInfo(zcu).bits; 33391 if (bits <= ptr_bits) continue; 33392 } 33393 }, 33394 .null => continue, 33395 else => {}, 33396 } 33397 33398 if (!ty.isPtrAtRuntime(zcu)) return .{ .conflict = .{ 33399 .peer_idx_a = strat_reason, 33400 .peer_idx_b = i, 33401 } }; 33402 33403 // Goes through optionals 33404 const peer_info = ty.ptrInfo(zcu); 33405 33406 var ptr_info = opt_ptr_info orelse { 33407 opt_ptr_info = peer_info; 33408 opt_ptr_info.?.flags.size = .c; 33409 first_idx = i; 33410 continue; 33411 }; 33412 33413 // Try peer -> cur, then cur -> peer 33414 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) orelse { 33415 return .{ .conflict = .{ 33416 .peer_idx_a = first_idx, 33417 .peer_idx_b = i, 33418 } }; 33419 }).toIntern(); 33420 33421 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) { 33422 const peer_sent = try ip.getCoerced(sema.gpa, pt.tid, ptr_info.sentinel, ptr_info.child); 33423 const ptr_sent = try ip.getCoerced(sema.gpa, pt.tid, peer_info.sentinel, ptr_info.child); 33424 if (ptr_sent == peer_sent) { 33425 ptr_info.sentinel = ptr_sent; 33426 } else { 33427 ptr_info.sentinel = .none; 33428 } 33429 } else { 33430 ptr_info.sentinel = .none; 33431 } 33432 33433 // Note that the align can be always non-zero; Zcu.ptrType will canonicalize it 33434 ptr_info.flags.alignment = InternPool.Alignment.min( 33435 if (ptr_info.flags.alignment != .none) 33436 ptr_info.flags.alignment 33437 else 33438 Type.fromInterned(ptr_info.child).abiAlignment(zcu), 33439 33440 if (peer_info.flags.alignment != .none) 33441 peer_info.flags.alignment 33442 else 33443 Type.fromInterned(peer_info.child).abiAlignment(zcu), 33444 ); 33445 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 33446 return .{ .conflict = .{ 33447 .peer_idx_a = first_idx, 33448 .peer_idx_b = i, 33449 } }; 33450 } 33451 33452 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 33453 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 33454 { 33455 return .{ .conflict = .{ 33456 .peer_idx_a = first_idx, 33457 .peer_idx_b = i, 33458 } }; 33459 } 33460 33461 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 33462 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 33463 33464 opt_ptr_info = ptr_info; 33465 } 33466 return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) }; 33467 }, 33468 33469 .ptr => { 33470 // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only 33471 // if there were no actual slices. Else, we want the slice index to report a conflict. 33472 var opt_slice_idx: ?usize = null; 33473 33474 var any_abi_aligned = false; 33475 var opt_ptr_info: ?InternPool.Key.PtrType = null; 33476 var first_idx: usize = undefined; 33477 var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error 33478 33479 for (peer_tys, 0..) |opt_ty, i| { 33480 const ty = opt_ty orelse continue; 33481 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(zcu)) { 33482 .pointer => ty.ptrInfo(zcu), 33483 .@"fn" => .{ 33484 .child = ty.toIntern(), 33485 .flags = .{ 33486 .address_space = target_util.defaultAddressSpace(target, .global_constant), 33487 }, 33488 }, 33489 else => return .{ .conflict = .{ 33490 .peer_idx_a = strat_reason, 33491 .peer_idx_b = i, 33492 } }, 33493 }; 33494 33495 switch (peer_info.flags.size) { 33496 .one, .many => {}, 33497 .slice => opt_slice_idx = i, 33498 .c => return .{ .conflict = .{ 33499 .peer_idx_a = strat_reason, 33500 .peer_idx_b = i, 33501 } }, 33502 } 33503 33504 var ptr_info = opt_ptr_info orelse { 33505 opt_ptr_info = peer_info; 33506 first_idx = i; 33507 continue; 33508 }; 33509 33510 other_idx = i; 33511 33512 // We want to return this in a lot of cases, so alias it here for convenience 33513 const generic_err: PeerResolveResult = .{ .conflict = .{ 33514 .peer_idx_a = first_idx, 33515 .peer_idx_b = i, 33516 } }; 33517 33518 // Note that the align can be always non-zero; Type.ptr will canonicalize it 33519 if (peer_info.flags.alignment == .none) { 33520 any_abi_aligned = true; 33521 } else if (ptr_info.flags.alignment == .none) { 33522 any_abi_aligned = true; 33523 ptr_info.flags.alignment = peer_info.flags.alignment; 33524 } else { 33525 ptr_info.flags.alignment = ptr_info.flags.alignment.minStrict(peer_info.flags.alignment); 33526 } 33527 33528 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 33529 return generic_err; 33530 } 33531 33532 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 33533 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 33534 { 33535 return generic_err; 33536 } 33537 33538 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 33539 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 33540 ptr_info.flags.is_allowzero = ptr_info.flags.is_allowzero or peer_info.flags.is_allowzero; 33541 33542 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) { 33543 .one => switch (ip.indexToKey(peer_info.child)) { 33544 .array_type => |array_type| array_type.sentinel, 33545 else => .none, 33546 }, 33547 .many, .slice => peer_info.sentinel, 33548 .c => unreachable, 33549 }; 33550 33551 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) { 33552 .one => switch (ip.indexToKey(ptr_info.child)) { 33553 .array_type => |array_type| array_type.sentinel, 33554 else => .none, 33555 }, 33556 .many, .slice => ptr_info.sentinel, 33557 .c => unreachable, 33558 }; 33559 33560 // We abstract array handling slightly so that tuple pointers can work like array pointers 33561 const peer_pointee_array = sema.typeIsArrayLike(.fromInterned(peer_info.child)); 33562 const cur_pointee_array = sema.typeIsArrayLike(.fromInterned(ptr_info.child)); 33563 33564 // This switch is just responsible for deciding the size and pointee (not including 33565 // single-pointer array sentinel). 33566 good: { 33567 switch (peer_info.flags.size) { 33568 .one => switch (ptr_info.flags.size) { 33569 .one => { 33570 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { 33571 ptr_info.child = pointee.toIntern(); 33572 break :good; 33573 } 33574 33575 const cur_arr = cur_pointee_array orelse return generic_err; 33576 const peer_arr = peer_pointee_array orelse return generic_err; 33577 33578 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| { 33579 // *[n:x]T + *[n:y]T = *[n]T 33580 if (cur_arr.len == peer_arr.len) { 33581 ptr_info.child = (try pt.arrayType(.{ 33582 .len = cur_arr.len, 33583 .child = elem_ty.toIntern(), 33584 })).toIntern(); 33585 break :good; 33586 } 33587 // *[a]T + *[b]T = []T 33588 ptr_info.flags.size = .slice; 33589 ptr_info.child = elem_ty.toIntern(); 33590 break :good; 33591 } 33592 33593 if (peer_arr.elem_ty.toIntern() == .noreturn_type) { 33594 // *struct{} + *[a]T = []T 33595 ptr_info.flags.size = .slice; 33596 ptr_info.child = cur_arr.elem_ty.toIntern(); 33597 break :good; 33598 } 33599 33600 if (cur_arr.elem_ty.toIntern() == .noreturn_type) { 33601 // *[a]T + *struct{} = []T 33602 ptr_info.flags.size = .slice; 33603 ptr_info.child = peer_arr.elem_ty.toIntern(); 33604 break :good; 33605 } 33606 33607 return generic_err; 33608 }, 33609 .many => { 33610 // Only works for *[n]T + [*]T -> [*]T 33611 const arr = peer_pointee_array orelse return generic_err; 33612 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| { 33613 ptr_info.child = pointee.toIntern(); 33614 break :good; 33615 } 33616 if (arr.elem_ty.toIntern() == .noreturn_type) { 33617 // *struct{} + [*]T -> [*]T 33618 break :good; 33619 } 33620 return generic_err; 33621 }, 33622 .slice => { 33623 // Only works for *[n]T + []T -> []T 33624 const arr = peer_pointee_array orelse return generic_err; 33625 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| { 33626 ptr_info.child = pointee.toIntern(); 33627 break :good; 33628 } 33629 if (arr.elem_ty.toIntern() == .noreturn_type) { 33630 // *struct{} + []T -> []T 33631 break :good; 33632 } 33633 return generic_err; 33634 }, 33635 .c => unreachable, 33636 }, 33637 .many => switch (ptr_info.flags.size) { 33638 .one => { 33639 // Only works for [*]T + *[n]T -> [*]T 33640 const arr = cur_pointee_array orelse return generic_err; 33641 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| { 33642 ptr_info.flags.size = .many; 33643 ptr_info.child = pointee.toIntern(); 33644 break :good; 33645 } 33646 if (arr.elem_ty.toIntern() == .noreturn_type) { 33647 // [*]T + *struct{} -> [*]T 33648 ptr_info.flags.size = .many; 33649 ptr_info.child = peer_info.child; 33650 break :good; 33651 } 33652 return generic_err; 33653 }, 33654 .many => { 33655 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { 33656 ptr_info.child = pointee.toIntern(); 33657 break :good; 33658 } 33659 return generic_err; 33660 }, 33661 .slice => { 33662 // Only works if no peers are actually slices 33663 if (opt_slice_idx) |slice_idx| { 33664 return .{ .conflict = .{ 33665 .peer_idx_a = slice_idx, 33666 .peer_idx_b = i, 33667 } }; 33668 } 33669 // Okay, then works for [*]T + "[]T" -> [*]T 33670 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { 33671 ptr_info.flags.size = .many; 33672 ptr_info.child = pointee.toIntern(); 33673 break :good; 33674 } 33675 return generic_err; 33676 }, 33677 .c => unreachable, 33678 }, 33679 .slice => switch (ptr_info.flags.size) { 33680 .one => { 33681 // Only works for []T + *[n]T -> []T 33682 const arr = cur_pointee_array orelse return generic_err; 33683 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| { 33684 ptr_info.flags.size = .slice; 33685 ptr_info.child = pointee.toIntern(); 33686 break :good; 33687 } 33688 if (arr.elem_ty.toIntern() == .noreturn_type) { 33689 // []T + *struct{} -> []T 33690 ptr_info.flags.size = .slice; 33691 ptr_info.child = peer_info.child; 33692 break :good; 33693 } 33694 return generic_err; 33695 }, 33696 .many => { 33697 // Impossible! (current peer is an actual slice) 33698 return generic_err; 33699 }, 33700 .slice => { 33701 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { 33702 ptr_info.child = pointee.toIntern(); 33703 break :good; 33704 } 33705 return generic_err; 33706 }, 33707 .c => unreachable, 33708 }, 33709 .c => unreachable, 33710 } 33711 } 33712 33713 const sentinel_ty = switch (ptr_info.flags.size) { 33714 .one => switch (ip.indexToKey(ptr_info.child)) { 33715 .array_type => |array_type| array_type.child, 33716 else => ptr_info.child, 33717 }, 33718 .many, .slice, .c => ptr_info.child, 33719 }; 33720 33721 sentinel: { 33722 no_sentinel: { 33723 if (peer_sentinel == .none) break :no_sentinel; 33724 if (cur_sentinel == .none) break :no_sentinel; 33725 const peer_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, peer_sentinel, sentinel_ty); 33726 const cur_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, cur_sentinel, sentinel_ty); 33727 if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel; 33728 // Sentinels match 33729 if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) { 33730 .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{ 33731 .len = array_type.len, 33732 .child = array_type.child, 33733 .sentinel = cur_sent_coerced, 33734 })).toIntern(), 33735 else => unreachable, 33736 } else { 33737 ptr_info.sentinel = cur_sent_coerced; 33738 } 33739 break :sentinel; 33740 } 33741 // Clear existing sentinel 33742 ptr_info.sentinel = .none; 33743 if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) { 33744 .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{ 33745 .len = array_type.len, 33746 .child = array_type.child, 33747 .sentinel = .none, 33748 })).toIntern(), 33749 else => {}, 33750 }; 33751 } 33752 33753 opt_ptr_info = ptr_info; 33754 } 33755 33756 // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance) 33757 // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to 33758 // coerce the empty struct to a specific type, but no peer provided one. We need to 33759 // detect this case and emit an error. 33760 const pointee = opt_ptr_info.?.child; 33761 switch (pointee) { 33762 .noreturn_type => return .{ .conflict = .{ 33763 .peer_idx_a = first_idx, 33764 .peer_idx_b = other_idx, 33765 } }, 33766 else => switch (ip.indexToKey(pointee)) { 33767 .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{ 33768 .peer_idx_a = first_idx, 33769 .peer_idx_b = other_idx, 33770 } }, 33771 else => {}, 33772 }, 33773 } 33774 33775 if (any_abi_aligned and opt_ptr_info.?.flags.alignment != .none) { 33776 opt_ptr_info.?.flags.alignment = opt_ptr_info.?.flags.alignment.minStrict( 33777 try Type.fromInterned(pointee).abiAlignmentSema(pt), 33778 ); 33779 } 33780 33781 return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) }; 33782 }, 33783 33784 .func => { 33785 var opt_cur_ty: ?Type = null; 33786 var first_idx: usize = undefined; 33787 for (peer_tys, 0..) |opt_ty, i| { 33788 const ty = opt_ty orelse continue; 33789 const cur_ty = opt_cur_ty orelse { 33790 opt_cur_ty = ty; 33791 first_idx = i; 33792 continue; 33793 }; 33794 if (ty.zigTypeTag(zcu) != .@"fn") return .{ .conflict = .{ 33795 .peer_idx_a = strat_reason, 33796 .peer_idx_b = i, 33797 } }; 33798 // ty -> cur_ty 33799 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, false, target, src, src)) { 33800 continue; 33801 } 33802 // cur_ty -> ty 33803 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, false, target, src, src)) { 33804 opt_cur_ty = ty; 33805 continue; 33806 } 33807 return .{ .conflict = .{ 33808 .peer_idx_a = first_idx, 33809 .peer_idx_b = i, 33810 } }; 33811 } 33812 return .{ .success = opt_cur_ty.? }; 33813 }, 33814 33815 .enum_or_union => { 33816 var opt_cur_ty: ?Type = null; 33817 // The peer index which gave the current type 33818 var cur_ty_idx: usize = undefined; 33819 33820 for (peer_tys, 0..) |opt_ty, i| { 33821 const ty = opt_ty orelse continue; 33822 switch (ty.zigTypeTag(zcu)) { 33823 .enum_literal, .@"enum", .@"union" => {}, 33824 else => return .{ .conflict = .{ 33825 .peer_idx_a = strat_reason, 33826 .peer_idx_b = i, 33827 } }, 33828 } 33829 const cur_ty = opt_cur_ty orelse { 33830 opt_cur_ty = ty; 33831 cur_ty_idx = i; 33832 continue; 33833 }; 33834 33835 // We want to return this in a lot of cases, so alias it here for convenience 33836 const generic_err: PeerResolveResult = .{ .conflict = .{ 33837 .peer_idx_a = cur_ty_idx, 33838 .peer_idx_b = i, 33839 } }; 33840 33841 switch (cur_ty.zigTypeTag(zcu)) { 33842 .enum_literal => { 33843 opt_cur_ty = ty; 33844 cur_ty_idx = i; 33845 }, 33846 .@"enum" => switch (ty.zigTypeTag(zcu)) { 33847 .enum_literal => {}, 33848 .@"enum" => { 33849 if (!ty.eql(cur_ty, zcu)) return generic_err; 33850 }, 33851 .@"union" => { 33852 const tag_ty = ty.unionTagTypeHypothetical(zcu); 33853 if (!tag_ty.eql(cur_ty, zcu)) return generic_err; 33854 opt_cur_ty = ty; 33855 cur_ty_idx = i; 33856 }, 33857 else => unreachable, 33858 }, 33859 .@"union" => switch (ty.zigTypeTag(zcu)) { 33860 .enum_literal => {}, 33861 .@"enum" => { 33862 const cur_tag_ty = cur_ty.unionTagTypeHypothetical(zcu); 33863 if (!ty.eql(cur_tag_ty, zcu)) return generic_err; 33864 }, 33865 .@"union" => { 33866 if (!ty.eql(cur_ty, zcu)) return generic_err; 33867 }, 33868 else => unreachable, 33869 }, 33870 else => unreachable, 33871 } 33872 } 33873 return .{ .success = opt_cur_ty.? }; 33874 }, 33875 33876 .comptime_int => { 33877 for (peer_tys, 0..) |opt_ty, i| { 33878 const ty = opt_ty orelse continue; 33879 switch (ty.zigTypeTag(zcu)) { 33880 .comptime_int => {}, 33881 else => return .{ .conflict = .{ 33882 .peer_idx_a = strat_reason, 33883 .peer_idx_b = i, 33884 } }, 33885 } 33886 } 33887 return .{ .success = .comptime_int }; 33888 }, 33889 33890 .comptime_float => { 33891 for (peer_tys, 0..) |opt_ty, i| { 33892 const ty = opt_ty orelse continue; 33893 switch (ty.zigTypeTag(zcu)) { 33894 .comptime_int, .comptime_float => {}, 33895 else => return .{ .conflict = .{ 33896 .peer_idx_a = strat_reason, 33897 .peer_idx_b = i, 33898 } }, 33899 } 33900 } 33901 return .{ .success = .comptime_float }; 33902 }, 33903 33904 .fixed_int => { 33905 var idx_unsigned: ?usize = null; 33906 var idx_signed: ?usize = null; 33907 33908 // TODO: this is for compatibility with legacy behavior. See beneath the loop. 33909 var any_comptime_known = false; 33910 33911 for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| { 33912 const ty = opt_ty orelse continue; 33913 const opt_val = ptr_opt_val.*; 33914 33915 const peer_tag = ty.zigTypeTag(zcu); 33916 switch (peer_tag) { 33917 .comptime_int => { 33918 // If the value is undefined, we can't refine to a fixed-width int 33919 if (opt_val == null or opt_val.?.isUndef(zcu)) return .{ .conflict = .{ 33920 .peer_idx_a = strat_reason, 33921 .peer_idx_b = i, 33922 } }; 33923 any_comptime_known = true; 33924 ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?); 33925 continue; 33926 }, 33927 .int => {}, 33928 else => return .{ .conflict = .{ 33929 .peer_idx_a = strat_reason, 33930 .peer_idx_b = i, 33931 } }, 33932 } 33933 33934 if (opt_val != null) any_comptime_known = true; 33935 33936 const info = ty.intInfo(zcu); 33937 33938 const idx_ptr = switch (info.signedness) { 33939 .unsigned => &idx_unsigned, 33940 .signed => &idx_signed, 33941 }; 33942 33943 const largest_idx = idx_ptr.* orelse { 33944 idx_ptr.* = i; 33945 continue; 33946 }; 33947 33948 const cur_info = peer_tys[largest_idx].?.intInfo(zcu); 33949 if (info.bits > cur_info.bits) { 33950 idx_ptr.* = i; 33951 } 33952 } 33953 33954 if (idx_signed == null) { 33955 return .{ .success = peer_tys[idx_unsigned.?].? }; 33956 } 33957 33958 if (idx_unsigned == null) { 33959 return .{ .success = peer_tys[idx_signed.?].? }; 33960 } 33961 33962 const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(zcu); 33963 const signed_info = peer_tys[idx_signed.?].?.intInfo(zcu); 33964 if (signed_info.bits > unsigned_info.bits) { 33965 return .{ .success = peer_tys[idx_signed.?].? }; 33966 } 33967 33968 // TODO: this is for compatibility with legacy behavior. Before this version of PTR was 33969 // implemented, the algorithm very often returned false positives, with the expectation 33970 // that you'd just hit a coercion error later. One of these was that for integers, the 33971 // largest type would always be returned, even if it couldn't fit everything. This had 33972 // an unintentional consequence to semantics, which is that if values were known at 33973 // comptime, they would be coerced down to the smallest type where possible. This 33974 // behavior is unintuitive and order-dependent, so in my opinion should be eliminated, 33975 // but for now we'll retain compatibility. 33976 if (any_comptime_known) { 33977 if (unsigned_info.bits > signed_info.bits) { 33978 return .{ .success = peer_tys[idx_unsigned.?].? }; 33979 } 33980 const idx = @min(idx_unsigned.?, idx_signed.?); 33981 return .{ .success = peer_tys[idx].? }; 33982 } 33983 33984 return .{ .conflict = .{ 33985 .peer_idx_a = idx_unsigned.?, 33986 .peer_idx_b = idx_signed.?, 33987 } }; 33988 }, 33989 33990 .fixed_float => { 33991 var opt_cur_ty: ?Type = null; 33992 33993 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 33994 const ty = opt_ty orelse continue; 33995 switch (ty.zigTypeTag(zcu)) { 33996 .comptime_float, .comptime_int => {}, 33997 .int => { 33998 if (opt_val == null) return .{ .conflict = .{ 33999 .peer_idx_a = strat_reason, 34000 .peer_idx_b = i, 34001 } }; 34002 }, 34003 .float => { 34004 if (opt_cur_ty) |cur_ty| { 34005 if (cur_ty.eql(ty, zcu)) continue; 34006 // Recreate the type so we eliminate any c_longdouble 34007 const bits = @max(cur_ty.floatBits(target), ty.floatBits(target)); 34008 opt_cur_ty = switch (bits) { 34009 16 => .f16, 34010 32 => .f32, 34011 64 => .f64, 34012 80 => .f80, 34013 128 => .f128, 34014 else => unreachable, 34015 }; 34016 } else { 34017 opt_cur_ty = ty; 34018 } 34019 }, 34020 else => return .{ .conflict = .{ 34021 .peer_idx_a = strat_reason, 34022 .peer_idx_b = i, 34023 } }, 34024 } 34025 } 34026 34027 // Note that fixed_float is only chosen if there is at least one fixed-width float peer, 34028 // so opt_cur_ty must be non-null. 34029 return .{ .success = opt_cur_ty.? }; 34030 }, 34031 34032 .tuple => { 34033 // First, check that every peer has the same approximate structure (field count) 34034 34035 var opt_first_idx: ?usize = null; 34036 var is_tuple: bool = undefined; 34037 var field_count: usize = undefined; 34038 34039 for (peer_tys, 0..) |opt_ty, i| { 34040 const ty = opt_ty orelse continue; 34041 34042 if (!ty.isTuple(zcu)) { 34043 return .{ .conflict = .{ 34044 .peer_idx_a = strat_reason, 34045 .peer_idx_b = i, 34046 } }; 34047 } 34048 34049 const first_idx = opt_first_idx orelse { 34050 opt_first_idx = i; 34051 is_tuple = ty.isTuple(zcu); 34052 field_count = ty.structFieldCount(zcu); 34053 continue; 34054 }; 34055 34056 if (ty.structFieldCount(zcu) != field_count) { 34057 return .{ .conflict = .{ 34058 .peer_idx_a = first_idx, 34059 .peer_idx_b = i, 34060 } }; 34061 } 34062 } 34063 34064 assert(opt_first_idx != null); 34065 34066 // Now, we'll recursively resolve the field types 34067 const field_types = try sema.arena.alloc(InternPool.Index, field_count); 34068 // Values for `comptime` fields - `.none` used for non-comptime fields 34069 const field_vals = try sema.arena.alloc(InternPool.Index, field_count); 34070 const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); 34071 const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); 34072 34073 for (field_types, field_vals, 0..) |*field_ty, *field_val, field_index| { 34074 // Fill buffers with types and values of the field 34075 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { 34076 const ty = opt_ty orelse { 34077 peer_field_ty.* = null; 34078 peer_field_val.* = null; 34079 continue; 34080 }; 34081 peer_field_ty.* = ty.fieldType(field_index, zcu); 34082 peer_field_val.* = if (opt_val) |val| try val.fieldValue(pt, field_index) else null; 34083 } 34084 34085 // Resolve field type recursively 34086 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) { 34087 .success => |ty| ty.toIntern(), 34088 else => |result| { 34089 const result_buf = try sema.arena.create(PeerResolveResult); 34090 result_buf.* = result; 34091 const field_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); 34092 34093 // The error info needs the field types, but we can't reuse sub_peer_tys 34094 // since the recursive call may have clobbered it. 34095 const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len); 34096 for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| { 34097 // Already-resolved types won't be referenced by the error so it's fine 34098 // to leave them undefined. 34099 const ty = opt_ty orelse continue; 34100 peer_field_ty.* = ty.fieldType(field_index, zcu); 34101 } 34102 34103 return .{ .field_error = .{ 34104 .field_name = field_name, 34105 .field_types = peer_field_tys, 34106 .sub_result = result_buf, 34107 } }; 34108 }, 34109 }; 34110 34111 // Decide if this is a comptime field. If it is comptime in all peers, and the 34112 // coerced comptime values are all the same, we say it is comptime, else not. 34113 34114 var comptime_val: ?Value = null; 34115 for (peer_tys) |opt_ty| { 34116 const struct_ty = opt_ty orelse continue; 34117 try struct_ty.resolveStructFieldInits(pt); 34118 34119 const uncoerced_field_val = try struct_ty.structFieldValueComptime(pt, field_index) orelse { 34120 comptime_val = null; 34121 break; 34122 }; 34123 const uncoerced_field = Air.internedToRef(uncoerced_field_val.toIntern()); 34124 const coerced_inst = sema.coerceExtra(block, .fromInterned(field_ty.*), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) { 34125 // It's possible for PTR to give false positives. Just give up on making this a comptime field, we'll get an error later anyway 34126 error.NotCoercible => { 34127 comptime_val = null; 34128 break; 34129 }, 34130 else => |e| return e, 34131 }; 34132 const coerced_val = (try sema.resolveValue(coerced_inst)) orelse continue; 34133 const existing = comptime_val orelse { 34134 comptime_val = coerced_val; 34135 continue; 34136 }; 34137 if (!coerced_val.eql(existing, .fromInterned(field_ty.*), zcu)) { 34138 comptime_val = null; 34139 break; 34140 } 34141 } 34142 34143 field_val.* = if (comptime_val) |v| v.toIntern() else .none; 34144 } 34145 34146 const final_ty = try ip.getTupleType(zcu.gpa, pt.tid, .{ 34147 .types = field_types, 34148 .values = field_vals, 34149 }); 34150 34151 return .{ .success = .fromInterned(final_ty) }; 34152 }, 34153 34154 .exact => { 34155 var expect_ty: ?Type = null; 34156 var first_idx: usize = undefined; 34157 for (peer_tys, 0..) |opt_ty, i| { 34158 const ty = opt_ty orelse continue; 34159 if (expect_ty) |expect| { 34160 if (!ty.eql(expect, zcu)) return .{ .conflict = .{ 34161 .peer_idx_a = first_idx, 34162 .peer_idx_b = i, 34163 } }; 34164 } else { 34165 expect_ty = ty; 34166 first_idx = i; 34167 } 34168 } 34169 return .{ .success = expect_ty.? }; 34170 }, 34171 } 34172 } 34173 34174 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { 34175 // e0 -> e1 34176 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { 34177 return e1; 34178 } 34179 34180 // e1 -> e0 34181 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { 34182 return e0; 34183 } 34184 34185 return sema.errorSetMerge(e0, e1); 34186 } 34187 34188 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { 34189 const target = sema.pt.zcu.getTarget(); 34190 34191 // ty_b -> ty_a 34192 if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, false, target, src, src, null)) { 34193 return ty_a; 34194 } 34195 34196 // ty_a -> ty_b 34197 if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, false, target, src, src, null)) { 34198 return ty_b; 34199 } 34200 34201 return null; 34202 } 34203 34204 const ArrayLike = struct { 34205 len: u64, 34206 /// `noreturn` indicates that this type is `struct{}` so can coerce to anything 34207 elem_ty: Type, 34208 }; 34209 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { 34210 const pt = sema.pt; 34211 const zcu = pt.zcu; 34212 return switch (ty.zigTypeTag(zcu)) { 34213 .array => .{ 34214 .len = ty.arrayLen(zcu), 34215 .elem_ty = ty.childType(zcu), 34216 }, 34217 .@"struct" => { 34218 const field_count = ty.structFieldCount(zcu); 34219 if (field_count == 0) return .{ 34220 .len = 0, 34221 .elem_ty = .noreturn, 34222 }; 34223 if (!ty.isTuple(zcu)) return null; 34224 const elem_ty = ty.fieldType(0, zcu); 34225 for (1..field_count) |i| { 34226 if (!ty.fieldType(i, zcu).eql(elem_ty, zcu)) { 34227 return null; 34228 } 34229 } 34230 return .{ 34231 .len = field_count, 34232 .elem_ty = elem_ty, 34233 }; 34234 }, 34235 else => null, 34236 }; 34237 } 34238 34239 pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void { 34240 const pt = sema.pt; 34241 const zcu = pt.zcu; 34242 const ip = &zcu.intern_pool; 34243 34244 if (sema.fn_ret_ty_ies) |ies| { 34245 try sema.resolveInferredErrorSetPtr(block, src, ies); 34246 assert(ies.resolved != .none); 34247 ip.funcIesResolved(sema.func_index).* = ies.resolved; 34248 } 34249 } 34250 34251 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type, src: LazySrcLoc) CompileError!void { 34252 const pt = sema.pt; 34253 const zcu = pt.zcu; 34254 const ip = &zcu.intern_pool; 34255 const fn_ty_info = zcu.typeToFunc(fn_ty).?; 34256 34257 try Type.fromInterned(fn_ty_info.return_type).resolveFully(pt); 34258 34259 if (zcu.comp.config.any_error_tracing and 34260 Type.fromInterned(fn_ty_info.return_type).isError(zcu)) 34261 { 34262 // Ensure the type exists so that backends can assume that. 34263 _ = try sema.getBuiltinType(src, .StackTrace); 34264 } 34265 34266 for (0..fn_ty_info.param_types.len) |i| { 34267 try Type.fromInterned(fn_ty_info.param_types.get(ip)[i]).resolveFully(pt); 34268 } 34269 } 34270 34271 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { 34272 return val.resolveLazy(sema.arena, sema.pt); 34273 } 34274 34275 /// Resolve a struct's alignment only without triggering resolution of its layout. 34276 /// Asserts that the alignment is not yet resolved and the layout is non-packed. 34277 pub fn resolveStructAlignment( 34278 sema: *Sema, 34279 ty: InternPool.Index, 34280 struct_type: InternPool.LoadedStructType, 34281 ) SemaError!void { 34282 const pt = sema.pt; 34283 const zcu = pt.zcu; 34284 const ip = &zcu.intern_pool; 34285 const target = zcu.getTarget(); 34286 34287 assert(sema.owner.unwrap().type == ty); 34288 34289 assert(struct_type.layout != .@"packed"); 34290 assert(struct_type.flagsUnordered(ip).alignment == .none); 34291 34292 const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); 34293 34294 // We'll guess "pointer-aligned", if the struct has an 34295 // underaligned pointer field then some allocations 34296 // might require explicit alignment. 34297 if (struct_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return; 34298 34299 try sema.resolveStructFieldTypes(ty, struct_type); 34300 34301 // We'll guess "pointer-aligned", if the struct has an 34302 // underaligned pointer field then some allocations 34303 // might require explicit alignment. 34304 if (struct_type.assumePointerAlignedIfWip(ip, ptr_align)) return; 34305 defer struct_type.clearAlignmentWip(ip); 34306 34307 // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. 34308 // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. 34309 34310 var alignment: Alignment = .@"1"; 34311 34312 for (0..struct_type.field_types.len) |i| { 34313 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34314 if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) 34315 continue; 34316 const field_align = try field_ty.structFieldAlignmentSema( 34317 struct_type.fieldAlign(ip, i), 34318 struct_type.layout, 34319 pt, 34320 ); 34321 alignment = alignment.maxStrict(field_align); 34322 } 34323 34324 struct_type.setAlignment(ip, alignment); 34325 } 34326 34327 pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void { 34328 const pt = sema.pt; 34329 const zcu = pt.zcu; 34330 const ip = &zcu.intern_pool; 34331 const struct_type = zcu.typeToStruct(ty) orelse return; 34332 34333 assert(sema.owner.unwrap().type == ty.toIntern()); 34334 34335 if (struct_type.haveLayout(ip)) 34336 return; 34337 34338 try sema.resolveStructFieldTypes(ty.toIntern(), struct_type); 34339 34340 // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. 34341 // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. 34342 34343 if (struct_type.layout == .@"packed") { 34344 sema.backingIntType(struct_type) catch |err| switch (err) { 34345 error.OutOfMemory, error.AnalysisFail => |e| return e, 34346 error.ComptimeBreak, error.ComptimeReturn => unreachable, 34347 }; 34348 return; 34349 } 34350 34351 if (struct_type.setLayoutWip(ip)) { 34352 const msg = try sema.errMsg( 34353 ty.srcLoc(zcu), 34354 "struct '{f}' depends on itself", 34355 .{ty.fmt(pt)}, 34356 ); 34357 return sema.failWithOwnedErrorMsg(null, msg); 34358 } 34359 defer struct_type.clearLayoutWip(ip); 34360 34361 const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len); 34362 const sizes = try sema.arena.alloc(u64, struct_type.field_types.len); 34363 34364 var big_align: Alignment = .@"1"; 34365 34366 for (aligns, sizes, 0..) |*field_align, *field_size, i| { 34367 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34368 if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) { 34369 struct_type.offsets.get(ip)[i] = 0; 34370 field_size.* = 0; 34371 field_align.* = .none; 34372 continue; 34373 } 34374 34375 field_size.* = field_ty.abiSizeSema(pt) catch |err| switch (err) { 34376 error.AnalysisFail => { 34377 const msg = sema.err orelse return err; 34378 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); 34379 return err; 34380 }, 34381 else => return err, 34382 }; 34383 field_align.* = try field_ty.structFieldAlignmentSema( 34384 struct_type.fieldAlign(ip, i), 34385 struct_type.layout, 34386 pt, 34387 ); 34388 big_align = big_align.maxStrict(field_align.*); 34389 } 34390 34391 if (struct_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) { 34392 const msg = try sema.errMsg( 34393 ty.srcLoc(zcu), 34394 "struct layout depends on it having runtime bits", 34395 .{}, 34396 ); 34397 return sema.failWithOwnedErrorMsg(null, msg); 34398 } 34399 34400 if (struct_type.flagsUnordered(ip).assumed_pointer_aligned and 34401 big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(zcu.getTarget().ptrBitWidth(), 8)))) 34402 { 34403 const msg = try sema.errMsg( 34404 ty.srcLoc(zcu), 34405 "struct layout depends on being pointer aligned", 34406 .{}, 34407 ); 34408 return sema.failWithOwnedErrorMsg(null, msg); 34409 } 34410 34411 if (struct_type.hasReorderedFields()) { 34412 const runtime_order = struct_type.runtime_order.get(ip); 34413 34414 for (runtime_order, 0..) |*ro, i| { 34415 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34416 if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) { 34417 ro.* = .omitted; 34418 } else { 34419 ro.* = @enumFromInt(i); 34420 } 34421 } 34422 34423 const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder; 34424 34425 const AlignSortContext = struct { 34426 aligns: []const Alignment, 34427 34428 fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool { 34429 if (a == .omitted) return false; 34430 if (b == .omitted) return true; 34431 const a_align = ctx.aligns[@intFromEnum(a)]; 34432 const b_align = ctx.aligns[@intFromEnum(b)]; 34433 return a_align.compare(.gt, b_align); 34434 } 34435 }; 34436 if (!zcu.backendSupportsFeature(.field_reordering)) { 34437 // TODO: we should probably also reorder tuple fields? This is a bit weird because it'll involve 34438 // mutating the `InternPool` for a non-container type. 34439 // 34440 // TODO: implement field reordering support in all the backends! 34441 // 34442 // This logic does not reorder fields; it only moves the omitted ones to the end 34443 // so that logic elsewhere does not need to special-case here. 34444 var i: usize = 0; 34445 var off: usize = 0; 34446 while (i + off < runtime_order.len) { 34447 if (runtime_order[i + off] == .omitted) { 34448 off += 1; 34449 continue; 34450 } 34451 runtime_order[i] = runtime_order[i + off]; 34452 i += 1; 34453 } 34454 @memset(runtime_order[i..], .omitted); 34455 } else { 34456 mem.sortUnstable(RuntimeOrder, runtime_order, AlignSortContext{ 34457 .aligns = aligns, 34458 }, AlignSortContext.lessThan); 34459 } 34460 } 34461 34462 // Calculate size, alignment, and field offsets. 34463 const offsets = struct_type.offsets.get(ip); 34464 var it = struct_type.iterateRuntimeOrder(ip); 34465 var offset: u64 = 0; 34466 while (it.next()) |i| { 34467 offsets[i] = @intCast(aligns[i].forward(offset)); 34468 offset = offsets[i] + sizes[i]; 34469 } 34470 const size = std.math.cast(u32, big_align.forward(offset)) orelse { 34471 const msg = try sema.errMsg( 34472 ty.srcLoc(zcu), 34473 "struct layout requires size {d}, this compiler implementation supports up to {d}", 34474 .{ big_align.forward(offset), std.math.maxInt(u32) }, 34475 ); 34476 return sema.failWithOwnedErrorMsg(null, msg); 34477 }; 34478 struct_type.setLayoutResolved(ip, size, big_align); 34479 _ = try ty.comptimeOnlySema(pt); 34480 } 34481 34482 fn backingIntType( 34483 sema: *Sema, 34484 struct_type: InternPool.LoadedStructType, 34485 ) CompileError!void { 34486 const pt = sema.pt; 34487 const zcu = pt.zcu; 34488 const gpa = zcu.gpa; 34489 const ip = &zcu.intern_pool; 34490 34491 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 34492 defer analysis_arena.deinit(); 34493 34494 var block: Block = .{ 34495 .parent = null, 34496 .sema = sema, 34497 .namespace = struct_type.namespace, 34498 .instructions = .{}, 34499 .inlining = null, 34500 .comptime_reason = null, // set below if needed 34501 .src_base_inst = struct_type.zir_index, 34502 .type_name_ctx = struct_type.name, 34503 }; 34504 defer assert(block.instructions.items.len == 0); 34505 34506 const fields_bit_sum = blk: { 34507 var accumulator: u64 = 0; 34508 for (0..struct_type.field_types.len) |i| { 34509 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34510 accumulator += try field_ty.bitSizeSema(pt); 34511 } 34512 break :blk accumulator; 34513 }; 34514 34515 const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?; 34516 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; 34517 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; 34518 assert(extended.opcode == .struct_decl); 34519 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 34520 34521 if (small.has_backing_int) { 34522 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; 34523 const captures_len = if (small.has_captures_len) blk: { 34524 const captures_len = zir.extra[extra_index]; 34525 extra_index += 1; 34526 break :blk captures_len; 34527 } else 0; 34528 extra_index += @intFromBool(small.has_fields_len); 34529 extra_index += @intFromBool(small.has_decls_len); 34530 34531 extra_index += captures_len * 2; 34532 34533 const backing_int_body_len = zir.extra[extra_index]; 34534 extra_index += 1; 34535 34536 const backing_int_src: LazySrcLoc = .{ 34537 .base_node_inst = struct_type.zir_index, 34538 .offset = .{ .node_offset_container_tag = .zero }, 34539 }; 34540 block.comptime_reason = .{ .reason = .{ 34541 .src = backing_int_src, 34542 .r = .{ .simple = .type }, 34543 } }; 34544 const backing_int_ty = blk: { 34545 if (backing_int_body_len == 0) { 34546 const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 34547 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref); 34548 } else { 34549 const body = zir.bodySlice(extra_index, backing_int_body_len); 34550 const ty_ref = try sema.resolveInlineBody(&block, body, zir_index); 34551 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref); 34552 } 34553 }; 34554 34555 try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); 34556 struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); 34557 } else { 34558 if (fields_bit_sum > std.math.maxInt(u16)) { 34559 return sema.fail(&block, block.nodeOffset(.zero), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum}); 34560 } 34561 const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum)); 34562 struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); 34563 } 34564 34565 try sema.flushExports(); 34566 } 34567 34568 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { 34569 const pt = sema.pt; 34570 const zcu = pt.zcu; 34571 34572 if (!backing_int_ty.isInt(zcu)) { 34573 return sema.fail(block, src, "expected backing integer type, found '{f}'", .{backing_int_ty.fmt(pt)}); 34574 } 34575 if (backing_int_ty.bitSize(zcu) != fields_bit_sum) { 34576 return sema.fail( 34577 block, 34578 src, 34579 "backing integer type '{f}' has bit size {d} but the struct fields have a total bit size of {d}", 34580 .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum }, 34581 ); 34582 } 34583 } 34584 34585 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 34586 const pt = sema.pt; 34587 if (!ty.isIndexable(pt.zcu)) { 34588 const msg = msg: { 34589 const msg = try sema.errMsg(src, "type '{f}' does not support indexing", .{ty.fmt(pt)}); 34590 errdefer msg.destroy(sema.gpa); 34591 try sema.errNote(src, msg, "operand must be an array, slice, tuple, or vector", .{}); 34592 break :msg msg; 34593 }; 34594 return sema.failWithOwnedErrorMsg(block, msg); 34595 } 34596 } 34597 34598 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 34599 const pt = sema.pt; 34600 const zcu = pt.zcu; 34601 if (ty.zigTypeTag(zcu) == .pointer) { 34602 switch (ty.ptrSize(zcu)) { 34603 .slice, .many, .c => return, 34604 .one => { 34605 const elem_ty = ty.childType(zcu); 34606 if (elem_ty.zigTypeTag(zcu) == .array) return; 34607 // TODO https://github.com/ziglang/zig/issues/15479 34608 // if (elem_ty.isTuple()) return; 34609 }, 34610 } 34611 } 34612 const msg = msg: { 34613 const msg = try sema.errMsg(src, "type '{f}' is not an indexable pointer", .{ty.fmt(pt)}); 34614 errdefer msg.destroy(sema.gpa); 34615 try sema.errNote(src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); 34616 break :msg msg; 34617 }; 34618 return sema.failWithOwnedErrorMsg(block, msg); 34619 } 34620 34621 /// Resolve a unions's alignment only without triggering resolution of its layout. 34622 /// Asserts that the alignment is not yet resolved. 34623 pub fn resolveUnionAlignment( 34624 sema: *Sema, 34625 ty: Type, 34626 union_type: InternPool.LoadedUnionType, 34627 ) SemaError!void { 34628 const pt = sema.pt; 34629 const zcu = pt.zcu; 34630 const ip = &zcu.intern_pool; 34631 const target = zcu.getTarget(); 34632 34633 assert(sema.owner.unwrap().type == ty.toIntern()); 34634 34635 assert(!union_type.haveLayout(ip)); 34636 34637 const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); 34638 34639 // We'll guess "pointer-aligned", if the union has an 34640 // underaligned pointer field then some allocations 34641 // might require explicit alignment. 34642 if (union_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return; 34643 34644 try sema.resolveUnionFieldTypes(ty, union_type); 34645 34646 // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. 34647 // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. 34648 34649 var max_align: Alignment = .@"1"; 34650 for (0..union_type.field_types.len) |field_index| { 34651 const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]); 34652 if (!(try field_ty.hasRuntimeBitsSema(pt))) continue; 34653 34654 const explicit_align = union_type.fieldAlign(ip, field_index); 34655 const field_align = if (explicit_align != .none) 34656 explicit_align 34657 else 34658 try field_ty.abiAlignmentSema(sema.pt); 34659 34660 max_align = max_align.max(field_align); 34661 } 34662 34663 union_type.setAlignment(ip, max_align); 34664 } 34665 34666 /// This logic must be kept in sync with `Type.getUnionLayout`. 34667 pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void { 34668 const pt = sema.pt; 34669 const ip = &pt.zcu.intern_pool; 34670 34671 try sema.resolveUnionFieldTypes(ty, ip.loadUnionType(ty.ip_index)); 34672 34673 // Load again, since the tag type might have changed due to resolution. 34674 const union_type = ip.loadUnionType(ty.ip_index); 34675 34676 assert(sema.owner.unwrap().type == ty.toIntern()); 34677 34678 const old_flags = union_type.flagsUnordered(ip); 34679 switch (old_flags.status) { 34680 .none, .have_field_types => {}, 34681 .field_types_wip, .layout_wip => { 34682 const msg = try sema.errMsg( 34683 ty.srcLoc(pt.zcu), 34684 "union '{f}' depends on itself", 34685 .{ty.fmt(pt)}, 34686 ); 34687 return sema.failWithOwnedErrorMsg(null, msg); 34688 }, 34689 .have_layout, .fully_resolved_wip, .fully_resolved => return, 34690 } 34691 34692 errdefer union_type.setStatusIfLayoutWip(ip, old_flags.status); 34693 34694 union_type.setStatus(ip, .layout_wip); 34695 34696 // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. 34697 // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. 34698 34699 var max_size: u64 = 0; 34700 var max_align: Alignment = .@"1"; 34701 for (0..union_type.field_types.len) |field_index| { 34702 const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]); 34703 if (field_ty.isNoReturn(pt.zcu)) continue; 34704 34705 // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s, 34706 // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type, 34707 // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us. 34708 // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to 34709 // be queried, enabling failures to be handled with the emission of a compile error, and also in 34710 // `hasRuntimeBits` for ever being able to infinite recurse in the first place. 34711 try field_ty.resolveLayout(pt); 34712 34713 if (try field_ty.hasRuntimeBitsSema(pt)) { 34714 max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) { 34715 error.AnalysisFail => { 34716 const msg = sema.err orelse return err; 34717 try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{}); 34718 return err; 34719 }, 34720 else => return err, 34721 }); 34722 } 34723 34724 const explicit_align = union_type.fieldAlign(ip, field_index); 34725 const field_align = if (explicit_align != .none) 34726 explicit_align 34727 else 34728 try field_ty.abiAlignmentSema(pt); 34729 max_align = max_align.max(field_align); 34730 } 34731 34732 const has_runtime_tag = union_type.flagsUnordered(ip).runtime_tag.hasTag() and 34733 try Type.fromInterned(union_type.enum_tag_ty).hasRuntimeBitsSema(pt); 34734 const size, const alignment, const padding = if (has_runtime_tag) layout: { 34735 const enum_tag_type: Type = .fromInterned(union_type.enum_tag_ty); 34736 const tag_align = try enum_tag_type.abiAlignmentSema(pt); 34737 const tag_size = try enum_tag_type.abiSizeSema(pt); 34738 34739 // Put the tag before or after the payload depending on which one's 34740 // alignment is greater. 34741 var size: u64 = 0; 34742 var padding: u32 = 0; 34743 if (tag_align.order(max_align).compare(.gte)) { 34744 // {Tag, Payload} 34745 size += tag_size; 34746 size = max_align.forward(size); 34747 size += max_size; 34748 const prev_size = size; 34749 size = tag_align.forward(size); 34750 padding = @intCast(size - prev_size); 34751 } else { 34752 // {Payload, Tag} 34753 size += max_size; 34754 size = switch (pt.zcu.getTarget().ofmt) { 34755 .c => max_align, 34756 else => tag_align, 34757 }.forward(size); 34758 size += tag_size; 34759 const prev_size = size; 34760 size = max_align.forward(size); 34761 padding = @intCast(size - prev_size); 34762 } 34763 34764 break :layout .{ size, max_align.max(tag_align), padding }; 34765 } else .{ max_align.forward(max_size), max_align, 0 }; 34766 34767 const casted_size = std.math.cast(u32, size) orelse { 34768 const msg = try sema.errMsg( 34769 ty.srcLoc(pt.zcu), 34770 "union layout requires size {d}, this compiler implementation supports up to {d}", 34771 .{ size, std.math.maxInt(u32) }, 34772 ); 34773 return sema.failWithOwnedErrorMsg(null, msg); 34774 }; 34775 union_type.setHaveLayout(ip, casted_size, padding, alignment); 34776 34777 if (union_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) { 34778 const msg = try sema.errMsg( 34779 ty.srcLoc(pt.zcu), 34780 "union layout depends on it having runtime bits", 34781 .{}, 34782 ); 34783 return sema.failWithOwnedErrorMsg(null, msg); 34784 } 34785 34786 if (union_type.flagsUnordered(ip).assumed_pointer_aligned and 34787 alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(pt.zcu.getTarget().ptrBitWidth(), 8)))) 34788 { 34789 const msg = try sema.errMsg( 34790 ty.srcLoc(pt.zcu), 34791 "union layout depends on being pointer aligned", 34792 .{}, 34793 ); 34794 return sema.failWithOwnedErrorMsg(null, msg); 34795 } 34796 _ = try ty.comptimeOnlySema(pt); 34797 } 34798 34799 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to 34800 /// be resolved. 34801 pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void { 34802 try sema.resolveStructLayout(ty); 34803 try sema.resolveStructFieldInits(ty); 34804 34805 const pt = sema.pt; 34806 const zcu = pt.zcu; 34807 const ip = &zcu.intern_pool; 34808 const struct_type = zcu.typeToStruct(ty).?; 34809 34810 assert(sema.owner.unwrap().type == ty.toIntern()); 34811 34812 if (struct_type.setFullyResolved(ip)) return; 34813 errdefer struct_type.clearFullyResolved(ip); 34814 34815 // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. 34816 // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. 34817 34818 // After we have resolve struct layout we have to go over the fields again to 34819 // make sure pointer fields get their child types resolved as well. 34820 // See also similar code for unions. 34821 34822 for (0..struct_type.field_types.len) |i| { 34823 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34824 try field_ty.resolveFully(pt); 34825 } 34826 } 34827 34828 pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void { 34829 try sema.resolveUnionLayout(ty); 34830 34831 const pt = sema.pt; 34832 const zcu = pt.zcu; 34833 const ip = &zcu.intern_pool; 34834 const union_obj = zcu.typeToUnion(ty).?; 34835 34836 assert(sema.owner.unwrap().type == ty.toIntern()); 34837 34838 switch (union_obj.flagsUnordered(ip).status) { 34839 .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, 34840 .fully_resolved_wip, .fully_resolved => return, 34841 } 34842 34843 // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis. 34844 // It's just triggering *other* analysis, alongside a simple loop over already-resolved info. 34845 34846 { 34847 // After we have resolve union layout we have to go over the fields again to 34848 // make sure pointer fields get their child types resolved as well. 34849 // See also similar code for structs. 34850 const prev_status = union_obj.flagsUnordered(ip).status; 34851 errdefer union_obj.setStatus(ip, prev_status); 34852 34853 union_obj.setStatus(ip, .fully_resolved_wip); 34854 for (0..union_obj.field_types.len) |field_index| { 34855 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 34856 try field_ty.resolveFully(pt); 34857 } 34858 union_obj.setStatus(ip, .fully_resolved); 34859 } 34860 34861 // And let's not forget comptime-only status. 34862 _ = try ty.comptimeOnlySema(pt); 34863 } 34864 34865 pub fn resolveStructFieldTypes( 34866 sema: *Sema, 34867 ty: InternPool.Index, 34868 struct_type: InternPool.LoadedStructType, 34869 ) SemaError!void { 34870 const pt = sema.pt; 34871 const zcu = pt.zcu; 34872 const ip = &zcu.intern_pool; 34873 34874 assert(sema.owner.unwrap().type == ty); 34875 34876 if (struct_type.haveFieldTypes(ip)) return; 34877 34878 if (struct_type.setFieldTypesWip(ip)) { 34879 const msg = try sema.errMsg( 34880 Type.fromInterned(ty).srcLoc(zcu), 34881 "struct '{f}' depends on itself", 34882 .{Type.fromInterned(ty).fmt(pt)}, 34883 ); 34884 return sema.failWithOwnedErrorMsg(null, msg); 34885 } 34886 defer struct_type.clearFieldTypesWip(ip); 34887 34888 // can't happen earlier than this because we only want the progress node if not already resolved 34889 const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null); 34890 defer tracked_unit.end(zcu); 34891 34892 sema.structFields(struct_type) catch |err| switch (err) { 34893 error.AnalysisFail, error.OutOfMemory => |e| return e, 34894 error.ComptimeBreak, error.ComptimeReturn => unreachable, 34895 }; 34896 } 34897 34898 pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void { 34899 const pt = sema.pt; 34900 const zcu = pt.zcu; 34901 const ip = &zcu.intern_pool; 34902 const struct_type = zcu.typeToStruct(ty) orelse return; 34903 34904 assert(sema.owner.unwrap().type == ty.toIntern()); 34905 34906 // Inits can start as resolved 34907 if (struct_type.haveFieldInits(ip)) return; 34908 34909 try sema.resolveStructLayout(ty); 34910 34911 if (struct_type.setInitsWip(ip)) { 34912 const msg = try sema.errMsg( 34913 ty.srcLoc(zcu), 34914 "struct '{f}' depends on itself", 34915 .{ty.fmt(pt)}, 34916 ); 34917 return sema.failWithOwnedErrorMsg(null, msg); 34918 } 34919 defer struct_type.clearInitsWip(ip); 34920 34921 // can't happen earlier than this because we only want the progress node if not already resolved 34922 const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null); 34923 defer tracked_unit.end(zcu); 34924 34925 sema.structFieldInits(struct_type) catch |err| switch (err) { 34926 error.AnalysisFail, error.OutOfMemory => |e| return e, 34927 error.ComptimeBreak, error.ComptimeReturn => unreachable, 34928 }; 34929 struct_type.setHaveFieldInits(ip); 34930 } 34931 34932 pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.LoadedUnionType) SemaError!void { 34933 const pt = sema.pt; 34934 const zcu = pt.zcu; 34935 const ip = &zcu.intern_pool; 34936 34937 assert(sema.owner.unwrap().type == ty.toIntern()); 34938 34939 switch (union_type.flagsUnordered(ip).status) { 34940 .none => {}, 34941 .field_types_wip => { 34942 const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)}); 34943 return sema.failWithOwnedErrorMsg(null, msg); 34944 }, 34945 .have_field_types, 34946 .have_layout, 34947 .layout_wip, 34948 .fully_resolved_wip, 34949 .fully_resolved, 34950 => return, 34951 } 34952 34953 // can't happen earlier than this because we only want the progress node if not already resolved 34954 const tracked_unit = zcu.trackUnitSema(union_type.name.toSlice(ip), null); 34955 defer tracked_unit.end(zcu); 34956 34957 union_type.setStatus(ip, .field_types_wip); 34958 errdefer union_type.setStatus(ip, .none); 34959 sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) { 34960 error.AnalysisFail, error.OutOfMemory => |e| return e, 34961 error.ComptimeBreak, error.ComptimeReturn => unreachable, 34962 }; 34963 union_type.setStatus(ip, .have_field_types); 34964 } 34965 34966 /// Returns a normal error set corresponding to the fully populated inferred 34967 /// error set. 34968 fn resolveInferredErrorSet( 34969 sema: *Sema, 34970 block: *Block, 34971 src: LazySrcLoc, 34972 ies_index: InternPool.Index, 34973 ) CompileError!InternPool.Index { 34974 const pt = sema.pt; 34975 const zcu = pt.zcu; 34976 const ip = &zcu.intern_pool; 34977 const func_index = ip.iesFuncIndex(ies_index); 34978 const func = zcu.funcInfo(func_index); 34979 34980 try sema.declareDependency(.{ .interned = func_index }); // resolved IES 34981 34982 try zcu.maybeUnresolveIes(func_index); 34983 const resolved_ty = func.resolvedErrorSetUnordered(ip); 34984 if (resolved_ty != .none) return resolved_ty; 34985 34986 if (zcu.analysis_in_progress.contains(AnalUnit.wrap(.{ .func = func_index }))) { 34987 return sema.fail(block, src, "unable to resolve inferred error set", .{}); 34988 } 34989 34990 // In order to ensure that all dependencies are properly added to the set, 34991 // we need to ensure the function body is analyzed of the inferred error 34992 // set. However, in the case of comptime/inline function calls with 34993 // inferred error sets, each call gets an adhoc InferredErrorSet object, which 34994 // has no corresponding function body. 34995 const ies_func_info = zcu.typeToFunc(.fromInterned(func.ty)).?; 34996 // if ies declared by a inline function with generic return type, the return_type should be generic_poison, 34997 // because inline function does not create a new declaration, and the ies has been filled with analyzeCall, 34998 // so here we can simply skip this case. 34999 if (ies_func_info.return_type == .generic_poison_type) { 35000 assert(ies_func_info.cc == .@"inline"); 35001 } else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) { 35002 if (ies_func_info.is_generic) { 35003 return sema.failWithOwnedErrorMsg(block, msg: { 35004 const msg = try sema.errMsg(src, "unable to resolve inferred error set of generic function", .{}); 35005 errdefer msg.destroy(sema.gpa); 35006 try sema.errNote(zcu.navSrcLoc(func.owner_nav), msg, "generic function declared here", .{}); 35007 break :msg msg; 35008 }); 35009 } 35010 // In this case we are dealing with the actual InferredErrorSet object that 35011 // corresponds to the function, not one created to track an inline/comptime call. 35012 const orig_func_index = ip.unwrapCoercedFunc(func_index); 35013 try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_func_index })); 35014 try pt.ensureFuncBodyUpToDate(orig_func_index); 35015 } 35016 35017 // This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody` 35018 // which calls `resolveInferredErrorSetPtr`. 35019 const final_resolved_ty = func.resolvedErrorSetUnordered(ip); 35020 assert(final_resolved_ty != .none); 35021 return final_resolved_ty; 35022 } 35023 35024 pub fn resolveInferredErrorSetPtr( 35025 sema: *Sema, 35026 block: *Block, 35027 src: LazySrcLoc, 35028 ies: *InferredErrorSet, 35029 ) CompileError!void { 35030 const pt = sema.pt; 35031 const ip = &pt.zcu.intern_pool; 35032 35033 if (ies.resolved != .none) return; 35034 35035 const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern()); 35036 35037 for (ies.inferred_error_sets.keys()) |other_ies_index| { 35038 if (ies_index == other_ies_index) continue; 35039 switch (try sema.resolveInferredErrorSet(block, src, other_ies_index)) { 35040 .anyerror_type => { 35041 ies.resolved = .anyerror_type; 35042 return; 35043 }, 35044 else => |error_set_ty_index| { 35045 const names = ip.indexToKey(error_set_ty_index).error_set_type.names; 35046 for (names.get(ip)) |name| { 35047 try ies.errors.put(sema.arena, name, {}); 35048 } 35049 }, 35050 } 35051 } 35052 35053 const resolved_error_set_ty = try pt.errorSetFromUnsortedNames(ies.errors.keys()); 35054 ies.resolved = resolved_error_set_ty.toIntern(); 35055 } 35056 35057 fn resolveAdHocInferredErrorSet( 35058 sema: *Sema, 35059 block: *Block, 35060 src: LazySrcLoc, 35061 value: InternPool.Index, 35062 ) CompileError!InternPool.Index { 35063 const pt = sema.pt; 35064 const zcu = pt.zcu; 35065 const gpa = sema.gpa; 35066 const ip = &zcu.intern_pool; 35067 const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value)); 35068 if (new_ty == .none) return value; 35069 return ip.getCoerced(gpa, pt.tid, value, new_ty); 35070 } 35071 35072 fn resolveAdHocInferredErrorSetTy( 35073 sema: *Sema, 35074 block: *Block, 35075 src: LazySrcLoc, 35076 ty: InternPool.Index, 35077 ) CompileError!InternPool.Index { 35078 const ies = sema.fn_ret_ty_ies orelse return .none; 35079 const pt = sema.pt; 35080 const zcu = pt.zcu; 35081 const ip = &zcu.intern_pool; 35082 const error_union_info = switch (ip.indexToKey(ty)) { 35083 .error_union_type => |x| x, 35084 else => return .none, 35085 }; 35086 if (error_union_info.error_set_type != .adhoc_inferred_error_set_type) 35087 return .none; 35088 35089 try sema.resolveInferredErrorSetPtr(block, src, ies); 35090 const new_ty = try pt.intern(.{ .error_union_type = .{ 35091 .error_set_type = ies.resolved, 35092 .payload_type = error_union_info.payload_type, 35093 } }); 35094 return new_ty; 35095 } 35096 35097 fn resolveInferredErrorSetTy( 35098 sema: *Sema, 35099 block: *Block, 35100 src: LazySrcLoc, 35101 ty: InternPool.Index, 35102 ) CompileError!InternPool.Index { 35103 const pt = sema.pt; 35104 const zcu = pt.zcu; 35105 const ip = &zcu.intern_pool; 35106 if (ty == .anyerror_type) return ty; 35107 switch (ip.indexToKey(ty)) { 35108 .error_set_type => return ty, 35109 .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty), 35110 else => unreachable, 35111 } 35112 } 35113 35114 fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct { 35115 /// fields_len 35116 usize, 35117 Zir.Inst.StructDecl.Small, 35118 /// extra_index 35119 usize, 35120 } { 35121 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; 35122 assert(extended.opcode == .struct_decl); 35123 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 35124 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; 35125 35126 const captures_len = if (small.has_captures_len) blk: { 35127 const captures_len = zir.extra[extra_index]; 35128 extra_index += 1; 35129 break :blk captures_len; 35130 } else 0; 35131 35132 const fields_len = if (small.has_fields_len) blk: { 35133 const fields_len = zir.extra[extra_index]; 35134 extra_index += 1; 35135 break :blk fields_len; 35136 } else 0; 35137 35138 const decls_len = if (small.has_decls_len) decls_len: { 35139 const decls_len = zir.extra[extra_index]; 35140 extra_index += 1; 35141 break :decls_len decls_len; 35142 } else 0; 35143 35144 extra_index += captures_len * 2; 35145 35146 // The backing integer cannot be handled until `resolveStructLayout()`. 35147 if (small.has_backing_int) { 35148 const backing_int_body_len = zir.extra[extra_index]; 35149 extra_index += 1; // backing_int_body_len 35150 if (backing_int_body_len == 0) { 35151 extra_index += 1; // backing_int_ref 35152 } else { 35153 extra_index += backing_int_body_len; // backing_int_body_inst 35154 } 35155 } 35156 35157 // Skip over decls. 35158 extra_index += decls_len; 35159 35160 return .{ fields_len, small, extra_index }; 35161 } 35162 35163 fn structFields( 35164 sema: *Sema, 35165 struct_type: InternPool.LoadedStructType, 35166 ) CompileError!void { 35167 const pt = sema.pt; 35168 const zcu = pt.zcu; 35169 const gpa = zcu.gpa; 35170 const ip = &zcu.intern_pool; 35171 const namespace_index = struct_type.namespace; 35172 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?; 35173 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; 35174 35175 const fields_len, _, var extra_index = structZirInfo(zir, zir_index); 35176 35177 if (fields_len == 0) switch (struct_type.layout) { 35178 .@"packed" => { 35179 try sema.backingIntType(struct_type); 35180 return; 35181 }, 35182 .auto, .@"extern" => { 35183 struct_type.setLayoutResolved(ip, 0, .none); 35184 return; 35185 }, 35186 }; 35187 35188 var block_scope: Block = .{ 35189 .parent = null, 35190 .sema = sema, 35191 .namespace = namespace_index, 35192 .instructions = .{}, 35193 .inlining = null, 35194 .comptime_reason = .{ .reason = .{ 35195 .src = .{ 35196 .base_node_inst = struct_type.zir_index, 35197 .offset = .nodeOffset(.zero), 35198 }, 35199 .r = .{ .simple = .struct_fields }, 35200 } }, 35201 .src_base_inst = struct_type.zir_index, 35202 .type_name_ctx = struct_type.name, 35203 }; 35204 defer assert(block_scope.instructions.items.len == 0); 35205 35206 const Field = struct { 35207 type_body_len: u32 = 0, 35208 align_body_len: u32 = 0, 35209 init_body_len: u32 = 0, 35210 type_ref: Zir.Inst.Ref = .none, 35211 }; 35212 const fields = try sema.arena.alloc(Field, fields_len); 35213 35214 var any_inits = false; 35215 var any_aligned = false; 35216 35217 { 35218 const bits_per_field = 4; 35219 const fields_per_u32 = 32 / bits_per_field; 35220 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 35221 const flags_index = extra_index; 35222 var bit_bag_index: usize = flags_index; 35223 extra_index += bit_bags_count; 35224 var cur_bit_bag: u32 = undefined; 35225 var field_i: u32 = 0; 35226 while (field_i < fields_len) : (field_i += 1) { 35227 if (field_i % fields_per_u32 == 0) { 35228 cur_bit_bag = zir.extra[bit_bag_index]; 35229 bit_bag_index += 1; 35230 } 35231 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 35232 cur_bit_bag >>= 1; 35233 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; 35234 cur_bit_bag >>= 1; 35235 const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0; 35236 cur_bit_bag >>= 1; 35237 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; 35238 cur_bit_bag >>= 1; 35239 35240 if (is_comptime) struct_type.setFieldComptime(ip, field_i); 35241 35242 const field_name_zir: [:0]const u8 = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index])); 35243 extra_index += 1; // field_name 35244 35245 fields[field_i] = .{}; 35246 35247 if (has_type_body) { 35248 fields[field_i].type_body_len = zir.extra[extra_index]; 35249 } else { 35250 fields[field_i].type_ref = @enumFromInt(zir.extra[extra_index]); 35251 } 35252 extra_index += 1; 35253 35254 // This string needs to outlive the ZIR code. 35255 const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls); 35256 assert(struct_type.addFieldName(ip, field_name) == null); 35257 35258 if (has_align) { 35259 fields[field_i].align_body_len = zir.extra[extra_index]; 35260 extra_index += 1; 35261 any_aligned = true; 35262 } 35263 if (has_init) { 35264 fields[field_i].init_body_len = zir.extra[extra_index]; 35265 extra_index += 1; 35266 any_inits = true; 35267 } 35268 } 35269 } 35270 35271 // Next we do only types and alignments, saving the inits for a second pass, 35272 // so that init values may depend on type layout. 35273 35274 for (fields, 0..) |zir_field, field_i| { 35275 const ty_src: LazySrcLoc = .{ 35276 .base_node_inst = struct_type.zir_index, 35277 .offset = .{ .container_field_type = @intCast(field_i) }, 35278 }; 35279 const field_ty: Type = ty: { 35280 if (zir_field.type_ref != .none) { 35281 break :ty try sema.resolveType(&block_scope, ty_src, zir_field.type_ref); 35282 } 35283 assert(zir_field.type_body_len != 0); 35284 const body = zir.bodySlice(extra_index, zir_field.type_body_len); 35285 extra_index += body.len; 35286 const ty_ref = try sema.resolveInlineBody(&block_scope, body, zir_index); 35287 break :ty try sema.analyzeAsType(&block_scope, ty_src, ty_ref); 35288 }; 35289 35290 struct_type.field_types.get(ip)[field_i] = field_ty.toIntern(); 35291 35292 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 35293 const msg = msg: { 35294 const msg = try sema.errMsg(ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 35295 errdefer msg.destroy(sema.gpa); 35296 35297 try sema.addDeclaredHereNote(msg, field_ty); 35298 break :msg msg; 35299 }; 35300 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35301 } 35302 if (field_ty.zigTypeTag(zcu) == .noreturn) { 35303 const msg = msg: { 35304 const msg = try sema.errMsg(ty_src, "struct fields cannot be 'noreturn'", .{}); 35305 errdefer msg.destroy(sema.gpa); 35306 35307 try sema.addDeclaredHereNote(msg, field_ty); 35308 break :msg msg; 35309 }; 35310 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35311 } 35312 switch (struct_type.layout) { 35313 .@"extern" => if (!try sema.validateExternType(field_ty, .struct_field)) { 35314 const msg = msg: { 35315 const msg = try sema.errMsg(ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 35316 errdefer msg.destroy(sema.gpa); 35317 35318 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .struct_field); 35319 35320 try sema.addDeclaredHereNote(msg, field_ty); 35321 break :msg msg; 35322 }; 35323 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35324 }, 35325 .@"packed" => if (!try sema.validatePackedType(field_ty)) { 35326 const msg = msg: { 35327 const msg = try sema.errMsg(ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 35328 errdefer msg.destroy(sema.gpa); 35329 35330 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty); 35331 35332 try sema.addDeclaredHereNote(msg, field_ty); 35333 break :msg msg; 35334 }; 35335 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35336 }, 35337 else => {}, 35338 } 35339 35340 if (zir_field.align_body_len > 0) { 35341 const body = zir.bodySlice(extra_index, zir_field.align_body_len); 35342 extra_index += body.len; 35343 const align_ref = try sema.resolveInlineBody(&block_scope, body, zir_index); 35344 const align_src: LazySrcLoc = .{ 35345 .base_node_inst = struct_type.zir_index, 35346 .offset = .{ .container_field_align = @intCast(field_i) }, 35347 }; 35348 const field_align = try sema.analyzeAsAlign(&block_scope, align_src, align_ref); 35349 struct_type.field_aligns.get(ip)[field_i] = field_align; 35350 } 35351 35352 extra_index += zir_field.init_body_len; 35353 } 35354 35355 struct_type.clearFieldTypesWip(ip); 35356 if (!any_inits) struct_type.setHaveFieldInits(ip); 35357 35358 try sema.flushExports(); 35359 } 35360 35361 // This logic must be kept in sync with `structFields` 35362 fn structFieldInits( 35363 sema: *Sema, 35364 struct_type: InternPool.LoadedStructType, 35365 ) CompileError!void { 35366 const pt = sema.pt; 35367 const zcu = pt.zcu; 35368 const ip = &zcu.intern_pool; 35369 35370 assert(!struct_type.haveFieldInits(ip)); 35371 35372 const namespace_index = struct_type.namespace; 35373 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?; 35374 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; 35375 const fields_len, _, var extra_index = structZirInfo(zir, zir_index); 35376 35377 var block_scope: Block = .{ 35378 .parent = null, 35379 .sema = sema, 35380 .namespace = namespace_index, 35381 .instructions = .{}, 35382 .inlining = null, 35383 .comptime_reason = undefined, // set when `block_scope` is used 35384 .src_base_inst = struct_type.zir_index, 35385 .type_name_ctx = struct_type.name, 35386 }; 35387 defer assert(block_scope.instructions.items.len == 0); 35388 35389 const Field = struct { 35390 type_body_len: u32 = 0, 35391 align_body_len: u32 = 0, 35392 init_body_len: u32 = 0, 35393 }; 35394 const fields = try sema.arena.alloc(Field, fields_len); 35395 35396 var any_inits = false; 35397 35398 { 35399 const bits_per_field = 4; 35400 const fields_per_u32 = 32 / bits_per_field; 35401 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 35402 const flags_index = extra_index; 35403 var bit_bag_index: usize = flags_index; 35404 extra_index += bit_bags_count; 35405 var cur_bit_bag: u32 = undefined; 35406 var field_i: u32 = 0; 35407 while (field_i < fields_len) : (field_i += 1) { 35408 if (field_i % fields_per_u32 == 0) { 35409 cur_bit_bag = zir.extra[bit_bag_index]; 35410 bit_bag_index += 1; 35411 } 35412 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 35413 cur_bit_bag >>= 1; 35414 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; 35415 cur_bit_bag >>= 2; 35416 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; 35417 cur_bit_bag >>= 1; 35418 35419 extra_index += 1; // field_name 35420 35421 fields[field_i] = .{}; 35422 35423 if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index]; 35424 extra_index += 1; 35425 35426 if (has_align) { 35427 fields[field_i].align_body_len = zir.extra[extra_index]; 35428 extra_index += 1; 35429 } 35430 if (has_init) { 35431 fields[field_i].init_body_len = zir.extra[extra_index]; 35432 extra_index += 1; 35433 any_inits = true; 35434 } 35435 } 35436 } 35437 35438 if (any_inits) { 35439 for (fields, 0..) |zir_field, field_i| { 35440 extra_index += zir_field.type_body_len; 35441 extra_index += zir_field.align_body_len; 35442 const body = zir.bodySlice(extra_index, zir_field.init_body_len); 35443 extra_index += zir_field.init_body_len; 35444 35445 if (body.len == 0) continue; 35446 35447 // Pre-populate the type mapping the body expects to be there. 35448 // In init bodies, the zir index of the struct itself is used 35449 // to refer to the current field type. 35450 35451 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_i]); 35452 const type_ref = Air.internedToRef(field_ty.toIntern()); 35453 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index}); 35454 sema.inst_map.putAssumeCapacity(zir_index, type_ref); 35455 35456 const init_src: LazySrcLoc = .{ 35457 .base_node_inst = struct_type.zir_index, 35458 .offset = .{ .container_field_value = @intCast(field_i) }, 35459 }; 35460 35461 block_scope.comptime_reason = .{ .reason = .{ 35462 .src = init_src, 35463 .r = .{ .simple = .struct_field_default_value }, 35464 } }; 35465 const init = try sema.resolveInlineBody(&block_scope, body, zir_index); 35466 const coerced = try sema.coerce(&block_scope, field_ty, init, init_src); 35467 const default_val = try sema.resolveConstValue(&block_scope, init_src, coerced, null); 35468 35469 if (default_val.canMutateComptimeVarState(zcu)) { 35470 const field_name = struct_type.fieldName(ip, field_i).unwrap().?; 35471 return sema.failWithContainsReferenceToComptimeVar(&block_scope, init_src, field_name, "field default value", default_val); 35472 } 35473 struct_type.field_inits.get(ip)[field_i] = default_val.toIntern(); 35474 } 35475 } 35476 35477 try sema.flushExports(); 35478 } 35479 35480 fn unionFields( 35481 sema: *Sema, 35482 union_ty: InternPool.Index, 35483 union_type: InternPool.LoadedUnionType, 35484 ) CompileError!void { 35485 const tracy = trace(@src()); 35486 defer tracy.end(); 35487 35488 const pt = sema.pt; 35489 const zcu = pt.zcu; 35490 const gpa = zcu.gpa; 35491 const ip = &zcu.intern_pool; 35492 const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?; 35493 const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail; 35494 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; 35495 assert(extended.opcode == .union_decl); 35496 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); 35497 const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); 35498 var extra_index: usize = extra.end; 35499 35500 const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { 35501 const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35502 extra_index += 1; 35503 break :blk ty_ref; 35504 } else .none; 35505 35506 const captures_len = if (small.has_captures_len) blk: { 35507 const captures_len = zir.extra[extra_index]; 35508 extra_index += 1; 35509 break :blk captures_len; 35510 } else 0; 35511 35512 const body_len = if (small.has_body_len) blk: { 35513 const body_len = zir.extra[extra_index]; 35514 extra_index += 1; 35515 break :blk body_len; 35516 } else 0; 35517 35518 const fields_len = if (small.has_fields_len) blk: { 35519 const fields_len = zir.extra[extra_index]; 35520 extra_index += 1; 35521 break :blk fields_len; 35522 } else 0; 35523 35524 const decls_len = if (small.has_decls_len) decls_len: { 35525 const decls_len = zir.extra[extra_index]; 35526 extra_index += 1; 35527 break :decls_len decls_len; 35528 } else 0; 35529 35530 // Skip over captures and decls. 35531 extra_index += captures_len * 2 + decls_len; 35532 35533 const body = zir.bodySlice(extra_index, body_len); 35534 extra_index += body.len; 35535 35536 const src: LazySrcLoc = .{ 35537 .base_node_inst = union_type.zir_index, 35538 .offset = .nodeOffset(.zero), 35539 }; 35540 35541 var block_scope: Block = .{ 35542 .parent = null, 35543 .sema = sema, 35544 .namespace = union_type.namespace, 35545 .instructions = .{}, 35546 .inlining = null, 35547 .comptime_reason = .{ .reason = .{ 35548 .src = src, 35549 .r = .{ .simple = .union_fields }, 35550 } }, 35551 .src_base_inst = union_type.zir_index, 35552 .type_name_ctx = union_type.name, 35553 }; 35554 defer assert(block_scope.instructions.items.len == 0); 35555 35556 if (body.len != 0) { 35557 _ = try sema.analyzeInlineBody(&block_scope, body, zir_index); 35558 } 35559 35560 var int_tag_ty: Type = undefined; 35561 var enum_field_names: []InternPool.NullTerminatedString = &.{}; 35562 var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty; 35563 var explicit_tags_seen: []bool = &.{}; 35564 if (tag_type_ref != .none) { 35565 const tag_ty_src: LazySrcLoc = .{ 35566 .base_node_inst = union_type.zir_index, 35567 .offset = .{ .node_offset_container_tag = .zero }, 35568 }; 35569 const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); 35570 if (small.auto_enum_tag) { 35571 // The provided type is an integer type and we must construct the enum tag type here. 35572 int_tag_ty = provided_ty; 35573 if (int_tag_ty.zigTypeTag(zcu) != .int and int_tag_ty.zigTypeTag(zcu) != .comptime_int) { 35574 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{f}'", .{int_tag_ty.fmt(pt)}); 35575 } 35576 35577 if (fields_len > 0) { 35578 const field_count_val = try pt.intValue(.comptime_int, fields_len - 1); 35579 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) { 35580 const msg = msg: { 35581 const msg = try sema.errMsg(tag_ty_src, "specified integer tag type cannot represent every field", .{}); 35582 errdefer msg.destroy(sema.gpa); 35583 try sema.errNote(tag_ty_src, msg, "type '{f}' cannot fit values in range 0...{d}", .{ 35584 int_tag_ty.fmt(pt), 35585 fields_len - 1, 35586 }); 35587 break :msg msg; 35588 }; 35589 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35590 } 35591 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 35592 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len); 35593 } 35594 } else { 35595 // The provided type is the enum tag type. 35596 const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) { 35597 .enum_type => ip.loadEnumType(provided_ty.toIntern()), 35598 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{f}'", .{provided_ty.fmt(pt)}), 35599 }; 35600 union_type.setTagType(ip, provided_ty.toIntern()); 35601 // The fields of the union must match the enum exactly. 35602 // A flag per field is used to check for missing and extraneous fields. 35603 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); 35604 @memset(explicit_tags_seen, false); 35605 } 35606 } else { 35607 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis 35608 // purposes, we still auto-generate an enum tag type the same way. That the union is 35609 // untagged is represented by the Type tag (union vs union_tagged). 35610 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 35611 } 35612 35613 var field_types: std.ArrayListUnmanaged(InternPool.Index) = .empty; 35614 var field_aligns: std.ArrayListUnmanaged(InternPool.Alignment) = .empty; 35615 35616 try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len); 35617 if (small.any_aligned_fields) 35618 try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len); 35619 35620 var max_bits: u64 = 0; 35621 var min_bits: u64 = std.math.maxInt(u64); 35622 var max_bits_src: LazySrcLoc = undefined; 35623 var min_bits_src: LazySrcLoc = undefined; 35624 var max_bits_ty: Type = undefined; 35625 var min_bits_ty: Type = undefined; 35626 const bits_per_field = 4; 35627 const fields_per_u32 = 32 / bits_per_field; 35628 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 35629 var bit_bag_index: usize = extra_index; 35630 extra_index += bit_bags_count; 35631 var cur_bit_bag: u32 = undefined; 35632 var field_i: u32 = 0; 35633 var last_tag_val: ?Value = null; 35634 const layout = union_type.flagsUnordered(ip).layout; 35635 while (field_i < fields_len) : (field_i += 1) { 35636 if (field_i % fields_per_u32 == 0) { 35637 cur_bit_bag = zir.extra[bit_bag_index]; 35638 bit_bag_index += 1; 35639 } 35640 const has_type = @as(u1, @truncate(cur_bit_bag)) != 0; 35641 cur_bit_bag >>= 1; 35642 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 35643 cur_bit_bag >>= 1; 35644 const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0; 35645 cur_bit_bag >>= 1; 35646 const unused = @as(u1, @truncate(cur_bit_bag)) != 0; 35647 cur_bit_bag >>= 1; 35648 _ = unused; 35649 35650 const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]); 35651 const field_name_zir = zir.nullTerminatedString(field_name_index); 35652 extra_index += 1; 35653 35654 const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { 35655 const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35656 extra_index += 1; 35657 break :blk field_type_ref; 35658 } else .none; 35659 35660 const align_ref: Zir.Inst.Ref = if (has_align) blk: { 35661 const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35662 extra_index += 1; 35663 break :blk align_ref; 35664 } else .none; 35665 35666 const tag_ref: Air.Inst.Ref = if (has_tag) blk: { 35667 const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35668 extra_index += 1; 35669 break :blk try sema.resolveInst(tag_ref); 35670 } else .none; 35671 35672 const name_src: LazySrcLoc = .{ 35673 .base_node_inst = union_type.zir_index, 35674 .offset = .{ .container_field_name = field_i }, 35675 }; 35676 const value_src: LazySrcLoc = .{ 35677 .base_node_inst = union_type.zir_index, 35678 .offset = .{ .container_field_value = field_i }, 35679 }; 35680 const align_src: LazySrcLoc = .{ 35681 .base_node_inst = union_type.zir_index, 35682 .offset = .{ .container_field_align = field_i }, 35683 }; 35684 const type_src: LazySrcLoc = .{ 35685 .base_node_inst = union_type.zir_index, 35686 .offset = .{ .container_field_type = field_i }, 35687 }; 35688 35689 if (enum_field_vals.capacity() > 0) { 35690 const enum_tag_val = if (tag_ref != .none) blk: { 35691 const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, value_src); 35692 const val = try sema.resolveConstDefinedValue(&block_scope, value_src, coerced, .{ .simple = .enum_field_tag_value }); 35693 last_tag_val = val; 35694 35695 break :blk val; 35696 } else blk: { 35697 if (last_tag_val) |last_tag| { 35698 const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag); 35699 if (result.overflow) return sema.fail( 35700 &block_scope, 35701 value_src, 35702 "enumeration value '{f}' too large for type '{f}'", 35703 .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) }, 35704 ); 35705 last_tag_val = result.val; 35706 } else { 35707 last_tag_val = try pt.intValue(int_tag_ty, 0); 35708 } 35709 break :blk last_tag_val.?; 35710 }; 35711 const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern()); 35712 if (gop.found_existing) { 35713 const other_value_src: LazySrcLoc = .{ 35714 .base_node_inst = union_type.zir_index, 35715 .offset = .{ .container_field_value = @intCast(gop.index) }, 35716 }; 35717 const msg = msg: { 35718 const msg = try sema.errMsg( 35719 value_src, 35720 "enum tag value {f} already taken", 35721 .{enum_tag_val.fmtValueSema(pt, sema)}, 35722 ); 35723 errdefer msg.destroy(gpa); 35724 try sema.errNote(other_value_src, msg, "other occurrence here", .{}); 35725 break :msg msg; 35726 }; 35727 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35728 } 35729 } 35730 35731 // This string needs to outlive the ZIR code. 35732 const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls); 35733 if (enum_field_names.len != 0) { 35734 enum_field_names[field_i] = field_name; 35735 } 35736 35737 const field_ty: Type = if (!has_type) 35738 .void 35739 else if (field_type_ref == .none) 35740 .noreturn 35741 else 35742 try sema.resolveType(&block_scope, type_src, field_type_ref); 35743 35744 if (explicit_tags_seen.len > 0) { 35745 const tag_ty = union_type.tagTypeUnordered(ip); 35746 const tag_info = ip.loadEnumType(tag_ty); 35747 const enum_index = tag_info.nameIndex(ip, field_name) orelse { 35748 return sema.fail(&block_scope, name_src, "no field named '{f}' in enum '{f}'", .{ 35749 field_name.fmt(ip), Type.fromInterned(tag_ty).fmt(pt), 35750 }); 35751 }; 35752 35753 // No check for duplicate because the check already happened in order 35754 // to create the enum type in the first place. 35755 assert(!explicit_tags_seen[enum_index]); 35756 explicit_tags_seen[enum_index] = true; 35757 35758 // Enforce the enum fields and the union fields being in the same order. 35759 if (enum_index != field_i) { 35760 const msg = msg: { 35761 const enum_field_src: LazySrcLoc = .{ 35762 .base_node_inst = Type.fromInterned(tag_ty).typeDeclInstAllowGeneratedTag(zcu).?, 35763 .offset = .{ .container_field_name = enum_index }, 35764 }; 35765 const msg = try sema.errMsg(name_src, "union field '{f}' ordered differently than corresponding enum field", .{ 35766 field_name.fmt(ip), 35767 }); 35768 errdefer msg.destroy(sema.gpa); 35769 try sema.errNote(enum_field_src, msg, "enum field here", .{}); 35770 break :msg msg; 35771 }; 35772 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35773 } 35774 } 35775 35776 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 35777 const msg = msg: { 35778 const msg = try sema.errMsg(type_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 35779 errdefer msg.destroy(sema.gpa); 35780 35781 try sema.addDeclaredHereNote(msg, field_ty); 35782 break :msg msg; 35783 }; 35784 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35785 } 35786 switch (layout) { 35787 .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) { 35788 const msg = msg: { 35789 const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 35790 errdefer msg.destroy(sema.gpa); 35791 35792 try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field); 35793 35794 try sema.addDeclaredHereNote(msg, field_ty); 35795 break :msg msg; 35796 }; 35797 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35798 }, 35799 .@"packed" => { 35800 if (!try sema.validatePackedType(field_ty)) { 35801 const msg = msg: { 35802 const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 35803 errdefer msg.destroy(sema.gpa); 35804 35805 try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty); 35806 35807 try sema.addDeclaredHereNote(msg, field_ty); 35808 break :msg msg; 35809 }; 35810 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35811 } 35812 const field_bits = try field_ty.bitSizeSema(pt); 35813 if (field_bits >= max_bits) { 35814 max_bits = field_bits; 35815 max_bits_src = type_src; 35816 max_bits_ty = field_ty; 35817 } 35818 if (field_bits <= min_bits) { 35819 min_bits = field_bits; 35820 min_bits_src = type_src; 35821 min_bits_ty = field_ty; 35822 } 35823 }, 35824 .auto => {}, 35825 } 35826 35827 field_types.appendAssumeCapacity(field_ty.toIntern()); 35828 35829 if (small.any_aligned_fields) { 35830 field_aligns.appendAssumeCapacity(if (align_ref != .none) 35831 try sema.resolveAlign(&block_scope, align_src, align_ref) 35832 else 35833 .none); 35834 } else { 35835 assert(align_ref == .none); 35836 } 35837 } 35838 35839 union_type.setFieldTypes(ip, field_types.items); 35840 union_type.setFieldAligns(ip, field_aligns.items); 35841 35842 if (layout == .@"packed" and fields_len != 0 and min_bits != max_bits) { 35843 const msg = msg: { 35844 const msg = try sema.errMsg(src, "packed union has fields with mismatching bit sizes", .{}); 35845 errdefer msg.destroy(sema.gpa); 35846 try sema.errNote(min_bits_src, msg, "{d} bits here", .{min_bits}); 35847 try sema.addDeclaredHereNote(msg, min_bits_ty); 35848 try sema.errNote(max_bits_src, msg, "{d} bits here", .{max_bits}); 35849 try sema.addDeclaredHereNote(msg, max_bits_ty); 35850 break :msg msg; 35851 }; 35852 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35853 } 35854 35855 if (explicit_tags_seen.len > 0) { 35856 const tag_ty = union_type.tagTypeUnordered(ip); 35857 const tag_info = ip.loadEnumType(tag_ty); 35858 if (tag_info.names.len > fields_len) { 35859 const msg = msg: { 35860 const msg = try sema.errMsg(src, "enum field(s) missing in union", .{}); 35861 errdefer msg.destroy(sema.gpa); 35862 35863 for (tag_info.names.get(ip), 0..) |field_name, field_index| { 35864 if (explicit_tags_seen[field_index]) continue; 35865 try sema.addFieldErrNote(.fromInterned(tag_ty), field_index, msg, "field '{f}' missing, declared here", .{ 35866 field_name.fmt(ip), 35867 }); 35868 } 35869 try sema.addDeclaredHereNote(msg, .fromInterned(tag_ty)); 35870 break :msg msg; 35871 }; 35872 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35873 } 35874 } else if (enum_field_vals.count() > 0) { 35875 const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_ty, union_type.name); 35876 union_type.setTagType(ip, enum_ty); 35877 } else { 35878 const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_ty, union_type.name); 35879 union_type.setTagType(ip, enum_ty); 35880 } 35881 35882 try sema.flushExports(); 35883 } 35884 35885 fn generateUnionTagTypeNumbered( 35886 sema: *Sema, 35887 block: *Block, 35888 enum_field_names: []const InternPool.NullTerminatedString, 35889 enum_field_vals: []const InternPool.Index, 35890 union_type: InternPool.Index, 35891 union_name: InternPool.NullTerminatedString, 35892 ) !InternPool.Index { 35893 const pt = sema.pt; 35894 const zcu = pt.zcu; 35895 const gpa = sema.gpa; 35896 const ip = &zcu.intern_pool; 35897 35898 const name = try ip.getOrPutStringFmt( 35899 gpa, 35900 pt.tid, 35901 "@typeInfo({f}).@\"union\".tag_type.?", 35902 .{union_name.fmt(ip)}, 35903 .no_embedded_nulls, 35904 ); 35905 35906 const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{ 35907 .name = name, 35908 .owner_union_ty = union_type, 35909 .tag_ty = if (enum_field_vals.len == 0) 35910 (try pt.intType(.unsigned, 0)).toIntern() 35911 else 35912 ip.typeOf(enum_field_vals[0]), 35913 .names = enum_field_names, 35914 .values = enum_field_vals, 35915 .tag_mode = .explicit, 35916 .parent_namespace = block.namespace, 35917 }); 35918 35919 return enum_ty; 35920 } 35921 35922 fn generateUnionTagTypeSimple( 35923 sema: *Sema, 35924 block: *Block, 35925 enum_field_names: []const InternPool.NullTerminatedString, 35926 union_type: InternPool.Index, 35927 union_name: InternPool.NullTerminatedString, 35928 ) !InternPool.Index { 35929 const pt = sema.pt; 35930 const zcu = pt.zcu; 35931 const ip = &zcu.intern_pool; 35932 const gpa = sema.gpa; 35933 35934 const name = try ip.getOrPutStringFmt( 35935 gpa, 35936 pt.tid, 35937 "@typeInfo({f}).@\"union\".tag_type.?", 35938 .{union_name.fmt(ip)}, 35939 .no_embedded_nulls, 35940 ); 35941 35942 const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{ 35943 .name = name, 35944 .owner_union_ty = union_type, 35945 .tag_ty = (try pt.smallestUnsignedInt(enum_field_names.len -| 1)).toIntern(), 35946 .names = enum_field_names, 35947 .values = &.{}, 35948 .tag_mode = .auto, 35949 .parent_namespace = block.namespace, 35950 }); 35951 35952 return enum_ty; 35953 } 35954 35955 /// There is another implementation of this in `Type.onePossibleValue`. This one 35956 /// in `Sema` is for calling during semantic analysis, and performs field resolution 35957 /// to get the answer. The one in `Type` is for calling during codegen and asserts 35958 /// that the types are already resolved. 35959 /// TODO assert the return value matches `ty.onePossibleValue` 35960 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { 35961 const pt = sema.pt; 35962 const zcu = pt.zcu; 35963 const ip = &zcu.intern_pool; 35964 return switch (ty.toIntern()) { 35965 .u0_type, 35966 .i0_type, 35967 => try pt.intValue(ty, 0), 35968 .u1_type, 35969 .u8_type, 35970 .i8_type, 35971 .u16_type, 35972 .i16_type, 35973 .u29_type, 35974 .u32_type, 35975 .i32_type, 35976 .u64_type, 35977 .i64_type, 35978 .u80_type, 35979 .u128_type, 35980 .i128_type, 35981 .u256_type, 35982 .usize_type, 35983 .isize_type, 35984 .c_char_type, 35985 .c_short_type, 35986 .c_ushort_type, 35987 .c_int_type, 35988 .c_uint_type, 35989 .c_long_type, 35990 .c_ulong_type, 35991 .c_longlong_type, 35992 .c_ulonglong_type, 35993 .c_longdouble_type, 35994 .f16_type, 35995 .f32_type, 35996 .f64_type, 35997 .f80_type, 35998 .f128_type, 35999 .anyopaque_type, 36000 .bool_type, 36001 .type_type, 36002 .anyerror_type, 36003 .adhoc_inferred_error_set_type, 36004 .comptime_int_type, 36005 .comptime_float_type, 36006 .enum_literal_type, 36007 .ptr_usize_type, 36008 .ptr_const_comptime_int_type, 36009 .manyptr_u8_type, 36010 .manyptr_const_u8_type, 36011 .manyptr_const_u8_sentinel_0_type, 36012 .slice_const_u8_type, 36013 .slice_const_u8_sentinel_0_type, 36014 .vector_8_i8_type, 36015 .vector_16_i8_type, 36016 .vector_32_i8_type, 36017 .vector_64_i8_type, 36018 .vector_1_u8_type, 36019 .vector_2_u8_type, 36020 .vector_4_u8_type, 36021 .vector_8_u8_type, 36022 .vector_16_u8_type, 36023 .vector_32_u8_type, 36024 .vector_64_u8_type, 36025 .vector_2_i16_type, 36026 .vector_4_i16_type, 36027 .vector_8_i16_type, 36028 .vector_16_i16_type, 36029 .vector_32_i16_type, 36030 .vector_4_u16_type, 36031 .vector_8_u16_type, 36032 .vector_16_u16_type, 36033 .vector_32_u16_type, 36034 .vector_2_i32_type, 36035 .vector_4_i32_type, 36036 .vector_8_i32_type, 36037 .vector_16_i32_type, 36038 .vector_4_u32_type, 36039 .vector_8_u32_type, 36040 .vector_16_u32_type, 36041 .vector_2_i64_type, 36042 .vector_4_i64_type, 36043 .vector_8_i64_type, 36044 .vector_2_u64_type, 36045 .vector_4_u64_type, 36046 .vector_8_u64_type, 36047 .vector_1_u128_type, 36048 .vector_2_u128_type, 36049 .vector_1_u256_type, 36050 .vector_4_f16_type, 36051 .vector_8_f16_type, 36052 .vector_16_f16_type, 36053 .vector_32_f16_type, 36054 .vector_2_f32_type, 36055 .vector_4_f32_type, 36056 .vector_8_f32_type, 36057 .vector_16_f32_type, 36058 .vector_2_f64_type, 36059 .vector_4_f64_type, 36060 .vector_8_f64_type, 36061 .anyerror_void_error_union_type, 36062 => null, 36063 .void_type => Value.void, 36064 .noreturn_type => Value.@"unreachable", 36065 .anyframe_type => unreachable, 36066 .null_type => Value.null, 36067 .undefined_type => Value.undef, 36068 .optional_noreturn_type => try pt.nullValue(ty), 36069 .generic_poison_type => unreachable, 36070 .empty_tuple_type => Value.empty_tuple, 36071 // values, not types 36072 .undef, 36073 .undef_bool, 36074 .undef_usize, 36075 .undef_u1, 36076 .zero, 36077 .zero_usize, 36078 .zero_u1, 36079 .zero_u8, 36080 .one, 36081 .one_usize, 36082 .one_u1, 36083 .one_u8, 36084 .four_u8, 36085 .negative_one, 36086 .void_value, 36087 .unreachable_value, 36088 .null_value, 36089 .bool_true, 36090 .bool_false, 36091 .empty_tuple, 36092 // invalid 36093 .none, 36094 => unreachable, 36095 36096 _ => switch (ty.toIntern().unwrap(ip).getTag(ip)) { 36097 .removed => unreachable, 36098 36099 .type_int_signed, // i0 handled above 36100 .type_int_unsigned, // u0 handled above 36101 .type_pointer, 36102 .type_slice, 36103 .type_anyframe, 36104 .type_error_union, 36105 .type_anyerror_union, 36106 .type_error_set, 36107 .type_inferred_error_set, 36108 .type_opaque, 36109 .type_function, 36110 => null, 36111 36112 .simple_type, // handled above 36113 // values, not types 36114 .undef, 36115 .simple_value, 36116 .ptr_nav, 36117 .ptr_uav, 36118 .ptr_uav_aligned, 36119 .ptr_comptime_alloc, 36120 .ptr_comptime_field, 36121 .ptr_int, 36122 .ptr_eu_payload, 36123 .ptr_opt_payload, 36124 .ptr_elem, 36125 .ptr_field, 36126 .ptr_slice, 36127 .opt_payload, 36128 .opt_null, 36129 .int_u8, 36130 .int_u16, 36131 .int_u32, 36132 .int_i32, 36133 .int_usize, 36134 .int_comptime_int_u32, 36135 .int_comptime_int_i32, 36136 .int_small, 36137 .int_positive, 36138 .int_negative, 36139 .int_lazy_align, 36140 .int_lazy_size, 36141 .error_set_error, 36142 .error_union_error, 36143 .error_union_payload, 36144 .enum_literal, 36145 .enum_tag, 36146 .float_f16, 36147 .float_f32, 36148 .float_f64, 36149 .float_f80, 36150 .float_f128, 36151 .float_c_longdouble_f80, 36152 .float_c_longdouble_f128, 36153 .float_comptime_float, 36154 .variable, 36155 .threadlocal_variable, 36156 .@"extern", 36157 .func_decl, 36158 .func_instance, 36159 .func_coerced, 36160 .only_possible_value, 36161 .union_value, 36162 .bytes, 36163 .aggregate, 36164 .repeated, 36165 // memoized value, not types 36166 .memoized_call, 36167 => unreachable, 36168 36169 .type_array_big, 36170 .type_array_small, 36171 .type_vector, 36172 .type_enum_auto, 36173 .type_enum_explicit, 36174 .type_enum_nonexhaustive, 36175 .type_struct, 36176 .type_struct_packed, 36177 .type_struct_packed_inits, 36178 .type_tuple, 36179 .type_union, 36180 => switch (ip.indexToKey(ty.toIntern())) { 36181 inline .array_type, .vector_type => |seq_type, seq_tag| { 36182 const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; 36183 if (seq_type.len + @intFromBool(has_sentinel) == 0) return try pt.aggregateValue(ty, &.{}); 36184 if (try sema.typeHasOnePossibleValue(.fromInterned(seq_type.child))) |opv| { 36185 return try pt.aggregateSplatValue(ty, opv); 36186 } 36187 return null; 36188 }, 36189 36190 .struct_type => { 36191 // Resolving the layout first helps to avoid loops. 36192 // If the type has a coherent layout, we can recurse through fields safely. 36193 try ty.resolveLayout(pt); 36194 36195 const struct_type = ip.loadStructType(ty.toIntern()); 36196 36197 if (struct_type.field_types.len == 0) { 36198 // In this case the struct has no fields at all and 36199 // therefore has one possible value. 36200 return try pt.aggregateValue(ty, &.{}); 36201 } 36202 36203 const field_vals = try sema.arena.alloc( 36204 InternPool.Index, 36205 struct_type.field_types.len, 36206 ); 36207 for (field_vals, 0..) |*field_val, i| { 36208 if (struct_type.fieldIsComptime(ip, i)) { 36209 try ty.resolveStructFieldInits(pt); 36210 field_val.* = struct_type.field_inits.get(ip)[i]; 36211 continue; 36212 } 36213 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 36214 if (try sema.typeHasOnePossibleValue(field_ty)) |field_opv| { 36215 field_val.* = field_opv.toIntern(); 36216 } else return null; 36217 } 36218 36219 // In this case the struct has no runtime-known fields and 36220 // therefore has one possible value. 36221 return try pt.aggregateValue(ty, field_vals); 36222 }, 36223 36224 .tuple_type => |tuple| { 36225 for (tuple.values.get(ip)) |val| { 36226 if (val == .none) return null; 36227 } 36228 // In this case the struct has all comptime-known fields and 36229 // therefore has one possible value. 36230 // TODO: write something like getCoercedInts to avoid needing to dupe 36231 return try pt.aggregateValue(ty, try sema.arena.dupe(InternPool.Index, tuple.values.get(ip))); 36232 }, 36233 36234 .union_type => { 36235 // Resolving the layout first helps to avoid loops. 36236 // If the type has a coherent layout, we can recurse through fields safely. 36237 try ty.resolveLayout(pt); 36238 36239 const union_obj = ip.loadUnionType(ty.toIntern()); 36240 const tag_val = (try sema.typeHasOnePossibleValue(.fromInterned(union_obj.tagTypeUnordered(ip)))) orelse 36241 return null; 36242 if (union_obj.field_types.len == 0) { 36243 const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() }); 36244 return Value.fromInterned(only); 36245 } 36246 const only_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[0]); 36247 const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse 36248 return null; 36249 const only = try pt.internUnion(.{ 36250 .ty = ty.toIntern(), 36251 .tag = tag_val.toIntern(), 36252 .val = val_val.toIntern(), 36253 }); 36254 return Value.fromInterned(only); 36255 }, 36256 36257 .enum_type => { 36258 const enum_type = ip.loadEnumType(ty.toIntern()); 36259 switch (enum_type.tag_mode) { 36260 .nonexhaustive => { 36261 if (enum_type.tag_ty == .comptime_int_type) return null; 36262 36263 if (try sema.typeHasOnePossibleValue(.fromInterned(enum_type.tag_ty))) |int_opv| { 36264 const only = try pt.intern(.{ .enum_tag = .{ 36265 .ty = ty.toIntern(), 36266 .int = int_opv.toIntern(), 36267 } }); 36268 return Value.fromInterned(only); 36269 } 36270 36271 return null; 36272 }, 36273 .auto, .explicit => { 36274 if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null; 36275 36276 return Value.fromInterned(switch (enum_type.names.len) { 36277 0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }), 36278 1 => try pt.intern(.{ .enum_tag = .{ 36279 .ty = ty.toIntern(), 36280 .int = if (enum_type.values.len == 0) 36281 (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern() 36282 else 36283 try ip.getCoercedInts( 36284 zcu.gpa, 36285 pt.tid, 36286 ip.indexToKey(enum_type.values.get(ip)[0]).int, 36287 enum_type.tag_ty, 36288 ), 36289 } }), 36290 else => return null, 36291 }); 36292 }, 36293 } 36294 }, 36295 36296 else => unreachable, 36297 }, 36298 36299 .type_optional => { 36300 const payload_ip = ip.indexToKey(ty.toIntern()).opt_type; 36301 // Although ?noreturn is handled above, the element type 36302 // can be effectively noreturn for example via an empty 36303 // enum or error set. 36304 if (ip.isNoReturn(payload_ip)) return try pt.nullValue(ty); 36305 return null; 36306 }, 36307 }, 36308 }; 36309 } 36310 36311 /// Returns the type of the AIR instruction. 36312 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type { 36313 return sema.getTmpAir().typeOf(inst, &sema.pt.zcu.intern_pool); 36314 } 36315 36316 pub fn getTmpAir(sema: Sema) Air { 36317 return .{ 36318 .instructions = sema.air_instructions.slice(), 36319 .extra = sema.air_extra, 36320 }; 36321 } 36322 36323 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 { 36324 const fields = std.meta.fields(@TypeOf(extra)); 36325 try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len); 36326 return sema.addExtraAssumeCapacity(extra); 36327 } 36328 36329 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { 36330 const result: u32 = @intCast(sema.air_extra.items.len); 36331 sema.air_extra.appendSliceAssumeCapacity(&payloadToExtraItems(extra)); 36332 return result; 36333 } 36334 36335 fn payloadToExtraItems(data: anytype) [@typeInfo(@TypeOf(data)).@"struct".fields.len]u32 { 36336 const fields = @typeInfo(@TypeOf(data)).@"struct".fields; 36337 var result: [fields.len]u32 = undefined; 36338 inline for (&result, fields) |*val, field| { 36339 val.* = switch (field.type) { 36340 u32 => @field(data, field.name), 36341 i32, Air.CondBr.BranchHints, Air.Asm.Flags => @bitCast(@field(data, field.name)), 36342 Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(data, field.name)), 36343 else => @compileError("bad field type: " ++ @typeName(field.type)), 36344 }; 36345 } 36346 return result; 36347 } 36348 36349 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void { 36350 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(refs)); 36351 } 36352 36353 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index { 36354 const air_datas = sema.air_instructions.items(.data); 36355 const air_tags = sema.air_instructions.items(.tag); 36356 switch (air_tags[@intFromEnum(inst_index)]) { 36357 .br => return air_datas[@intFromEnum(inst_index)].br.block_inst, 36358 else => return null, 36359 } 36360 } 36361 36362 fn isComptimeKnown( 36363 sema: *Sema, 36364 inst: Air.Inst.Ref, 36365 ) !bool { 36366 return (try sema.resolveValue(inst)) != null; 36367 } 36368 36369 fn analyzeComptimeAlloc( 36370 sema: *Sema, 36371 block: *Block, 36372 src: LazySrcLoc, 36373 var_type: Type, 36374 alignment: Alignment, 36375 ) CompileError!Air.Inst.Ref { 36376 const pt = sema.pt; 36377 const zcu = pt.zcu; 36378 36379 // Needed to make an anon decl with type `var_type` (the `finish()` call below). 36380 _ = try sema.typeHasOnePossibleValue(var_type); 36381 36382 const ptr_type = try pt.ptrTypeSema(.{ 36383 .child = var_type.toIntern(), 36384 .flags = .{ 36385 .alignment = alignment, 36386 .address_space = target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), 36387 }, 36388 }); 36389 36390 const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment); 36391 36392 return Air.internedToRef((try pt.intern(.{ .ptr = .{ 36393 .ty = ptr_type.toIntern(), 36394 .base_addr = .{ .comptime_alloc = alloc }, 36395 .byte_offset = 0, 36396 } }))); 36397 } 36398 36399 fn resolveAddressSpace( 36400 sema: *Sema, 36401 block: *Block, 36402 src: LazySrcLoc, 36403 zir_ref: Zir.Inst.Ref, 36404 ctx: std.builtin.AddressSpace.Context, 36405 ) !std.builtin.AddressSpace { 36406 const air_ref = try sema.resolveInst(zir_ref); 36407 return sema.analyzeAsAddressSpace(block, src, air_ref, ctx); 36408 } 36409 36410 pub fn analyzeAsAddressSpace( 36411 sema: *Sema, 36412 block: *Block, 36413 src: LazySrcLoc, 36414 air_ref: Air.Inst.Ref, 36415 ctx: std.builtin.AddressSpace.Context, 36416 ) !std.builtin.AddressSpace { 36417 const pt = sema.pt; 36418 const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace); 36419 const coerced = try sema.coerce(block, addrspace_ty, air_ref, src); 36420 const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{ .simple = .@"addrspace" }); 36421 const address_space = try sema.interpretBuiltinType(block, src, addrspace_val, std.builtin.AddressSpace); 36422 const target = pt.zcu.getTarget(); 36423 36424 if (!target.cpu.supportsAddressSpace(address_space, ctx)) { 36425 // TODO error messages could be made more elaborate here 36426 const entity = switch (ctx) { 36427 .function => "functions", 36428 .variable => "mutable values", 36429 .constant => "constant values", 36430 .pointer => "pointers", 36431 }; 36432 return sema.fail( 36433 block, 36434 src, 36435 "{s} with address space '{s}' are not supported on {s}", 36436 .{ entity, @tagName(address_space), @tagName(target.cpu.arch.family()) }, 36437 ); 36438 } 36439 36440 return address_space; 36441 } 36442 36443 /// Asserts the value is a pointer and dereferences it. 36444 /// Returns `null` if the pointer contents cannot be loaded at comptime. 36445 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { 36446 // TODO: audit use sites to eliminate this coercion 36447 const pt = sema.pt; 36448 const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty); 36449 switch (try sema.pointerDerefExtra(block, src, coerced_ptr_val)) { 36450 .runtime_load => return null, 36451 .val => |v| return v, 36452 .needed_well_defined => |ty| return sema.fail( 36453 block, 36454 src, 36455 "comptime dereference requires '{f}' to have a well-defined layout", 36456 .{ty.fmt(pt)}, 36457 ), 36458 .out_of_bounds => |ty| return sema.fail( 36459 block, 36460 src, 36461 "dereference of '{f}' exceeds bounds of containing decl of type '{f}'", 36462 .{ ptr_ty.fmt(pt), ty.fmt(pt) }, 36463 ), 36464 } 36465 } 36466 36467 const DerefResult = union(enum) { 36468 runtime_load, 36469 val: Value, 36470 needed_well_defined: Type, 36471 out_of_bounds: Type, 36472 }; 36473 36474 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value) CompileError!DerefResult { 36475 const pt = sema.pt; 36476 const ip = &pt.zcu.intern_pool; 36477 switch (try sema.loadComptimePtr(block, src, ptr_val)) { 36478 .success => |mv| return .{ .val = try mv.intern(pt, sema.arena) }, 36479 .runtime_load => return .runtime_load, 36480 .undef => return sema.failWithUseOfUndef(block, src, null), 36481 .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}), 36482 .null_payload => return sema.fail(block, src, "attempt to use null value", .{}), 36483 .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}), 36484 .needed_well_defined => |ty| return .{ .needed_well_defined = ty }, 36485 .out_of_bounds => |ty| return .{ .out_of_bounds = ty }, 36486 .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}), 36487 } 36488 } 36489 36490 /// Used to convert a u64 value to a usize value, emitting a compile error if the number 36491 /// is too big to fit. 36492 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { 36493 if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; 36494 return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value '{d}' which is too big for this compiler implementation to handle", .{int}); 36495 } 36496 36497 /// For pointer-like optionals, it returns the pointer type. For pointers, 36498 /// the type is returned unmodified. 36499 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether 36500 /// a type has zero bits, which can cause a "foo depends on itself" compile error. 36501 /// This logic must be kept in sync with `Type.isPtrLikeOptional`. 36502 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { 36503 const pt = sema.pt; 36504 const zcu = pt.zcu; 36505 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { 36506 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 36507 .one, .many, .c => ty, 36508 .slice => null, 36509 }, 36510 .opt_type => |opt_child| switch (zcu.intern_pool.indexToKey(opt_child)) { 36511 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 36512 .slice, .c => null, 36513 .many, .one => { 36514 if (ptr_type.flags.is_allowzero) return null; 36515 36516 // optionals of zero sized types behave like bools, not pointers 36517 const payload_ty: Type = .fromInterned(opt_child); 36518 if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) { 36519 return null; 36520 } 36521 36522 return payload_ty; 36523 }, 36524 }, 36525 else => null, 36526 }, 36527 else => null, 36528 }; 36529 } 36530 36531 fn unionFieldIndex( 36532 sema: *Sema, 36533 block: *Block, 36534 union_ty: Type, 36535 field_name: InternPool.NullTerminatedString, 36536 field_src: LazySrcLoc, 36537 ) !u32 { 36538 const pt = sema.pt; 36539 const zcu = pt.zcu; 36540 const ip = &zcu.intern_pool; 36541 try union_ty.resolveFields(pt); 36542 const union_obj = zcu.typeToUnion(union_ty).?; 36543 const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse 36544 return sema.failWithBadUnionFieldAccess(block, union_ty, union_obj, field_src, field_name); 36545 return @intCast(field_index); 36546 } 36547 36548 fn structFieldIndex( 36549 sema: *Sema, 36550 block: *Block, 36551 struct_ty: Type, 36552 field_name: InternPool.NullTerminatedString, 36553 field_src: LazySrcLoc, 36554 ) !u32 { 36555 const pt = sema.pt; 36556 const zcu = pt.zcu; 36557 const ip = &zcu.intern_pool; 36558 try struct_ty.resolveFields(pt); 36559 const struct_type = zcu.typeToStruct(struct_ty).?; 36560 return struct_type.nameIndex(ip, field_name) orelse 36561 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); 36562 } 36563 36564 const IntFromFloatMode = enum { exact, truncate }; 36565 36566 fn intFromFloat( 36567 sema: *Sema, 36568 block: *Block, 36569 src: LazySrcLoc, 36570 val: Value, 36571 float_ty: Type, 36572 int_ty: Type, 36573 mode: IntFromFloatMode, 36574 ) CompileError!Value { 36575 const pt = sema.pt; 36576 const zcu = pt.zcu; 36577 if (float_ty.zigTypeTag(zcu) == .vector) { 36578 const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(zcu)); 36579 for (result_data, 0..) |*scalar, elem_idx| { 36580 const elem_val = try val.elemValue(pt, elem_idx); 36581 scalar.* = (try sema.intFromFloatScalar(block, src, elem_val, int_ty.scalarType(zcu), mode, elem_idx)).toIntern(); 36582 } 36583 return pt.aggregateValue(int_ty, result_data); 36584 } 36585 return sema.intFromFloatScalar(block, src, val, int_ty, mode, null); 36586 } 36587 36588 fn intFromFloatScalar( 36589 sema: *Sema, 36590 block: *Block, 36591 src: LazySrcLoc, 36592 val: Value, 36593 int_ty: Type, 36594 mode: IntFromFloatMode, 36595 vec_idx: ?usize, 36596 ) CompileError!Value { 36597 const pt = sema.pt; 36598 const zcu = pt.zcu; 36599 36600 if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, vec_idx); 36601 36602 const float = val.toFloat(f128, zcu); 36603 if (std.math.isNan(float)) { 36604 return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{ 36605 int_ty.fmt(pt), 36606 }); 36607 } 36608 if (std.math.isInf(float)) { 36609 return sema.fail(block, src, "float value Inf cannot be stored in integer type '{f}'", .{ 36610 int_ty.fmt(pt), 36611 }); 36612 } 36613 36614 var big_int: std.math.big.int.Mutable = .{ 36615 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), 36616 .len = undefined, 36617 .positive = undefined, 36618 }; 36619 switch (big_int.setFloat(float, .trunc)) { 36620 .inexact => switch (mode) { 36621 .exact => return sema.fail( 36622 block, 36623 src, 36624 "fractional component prevents float value '{f}' from coercion to type '{f}'", 36625 .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) }, 36626 ), 36627 .truncate => {}, 36628 }, 36629 .exact => {}, 36630 } 36631 const cti_result = try pt.intValue_big(.comptime_int, big_int.toConst()); 36632 if (int_ty.toIntern() == .comptime_int_type) return cti_result; 36633 36634 const int_info = int_ty.intInfo(zcu); 36635 if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) { 36636 return sema.fail(block, src, "float value '{f}' cannot be stored in integer type '{f}'", .{ 36637 val.fmtValueSema(pt, sema), int_ty.fmt(pt), 36638 }); 36639 } 36640 return pt.getCoerced(cti_result, int_ty); 36641 } 36642 36643 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. 36644 /// Vectors are also accepted. Vector results are reduced with AND. 36645 /// 36646 /// If provided, `vector_index` reports the first element that failed the range check. 36647 fn intFitsInType( 36648 sema: *Sema, 36649 val: Value, 36650 ty: Type, 36651 vector_index: ?*usize, 36652 ) CompileError!bool { 36653 const pt = sema.pt; 36654 const zcu = pt.zcu; 36655 if (ty.toIntern() == .comptime_int_type) return true; 36656 const info = ty.intInfo(zcu); 36657 switch (val.toIntern()) { 36658 .zero_usize, .zero_u8 => return true, 36659 else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { 36660 .undef => return true, 36661 .variable, .@"extern", .func, .ptr => { 36662 const target = zcu.getTarget(); 36663 const ptr_bits = target.ptrBitWidth(); 36664 return switch (info.signedness) { 36665 .signed => info.bits > ptr_bits, 36666 .unsigned => info.bits >= ptr_bits, 36667 }; 36668 }, 36669 .int => |int| switch (int.storage) { 36670 .u64, .i64, .big_int => { 36671 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; 36672 const big_int = int.storage.toBigInt(&buffer); 36673 return big_int.fitsInTwosComp(info.signedness, info.bits); 36674 }, 36675 .lazy_align => |lazy_ty| { 36676 const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed); 36677 // If it is u16 or bigger we know the alignment fits without resolving it. 36678 if (info.bits >= max_needed_bits) return true; 36679 const x = try Type.fromInterned(lazy_ty).abiAlignmentSema(pt); 36680 if (x == .none) return true; 36681 const actual_needed_bits = @as(usize, x.toLog2Units()) + 1 + @intFromBool(info.signedness == .signed); 36682 return info.bits >= actual_needed_bits; 36683 }, 36684 .lazy_size => |lazy_ty| { 36685 const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed); 36686 // If it is u64 or bigger we know the size fits without resolving it. 36687 if (info.bits >= max_needed_bits) return true; 36688 const x = try Type.fromInterned(lazy_ty).abiSizeSema(pt); 36689 if (x == 0) return true; 36690 const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed); 36691 return info.bits >= actual_needed_bits; 36692 }, 36693 }, 36694 .aggregate => |aggregate| { 36695 assert(ty.zigTypeTag(zcu) == .vector); 36696 return switch (aggregate.storage) { 36697 .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(zcu), &zcu.intern_pool), 0..) |byte, i| { 36698 if (byte == 0) continue; 36699 const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); 36700 if (info.bits >= actual_needed_bits) continue; 36701 if (vector_index) |vi| vi.* = i; 36702 break false; 36703 } else true, 36704 .elems, .repeated_elem => for (switch (aggregate.storage) { 36705 .bytes => unreachable, 36706 .elems => |elems| elems, 36707 .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), 36708 }, 0..) |elem, i| { 36709 if (try sema.intFitsInType(Value.fromInterned(elem), ty.scalarType(zcu), null)) continue; 36710 if (vector_index) |vi| vi.* = i; 36711 break false; 36712 } else true, 36713 }; 36714 }, 36715 else => unreachable, 36716 }, 36717 } 36718 } 36719 36720 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { 36721 const pt = sema.pt; 36722 if (!(try int_val.compareAllWithZeroSema(.gte, pt))) return false; 36723 const end_val = try pt.intValue(tag_ty, end); 36724 if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false; 36725 return true; 36726 } 36727 36728 /// Asserts the type is an enum. 36729 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { 36730 const pt = sema.pt; 36731 const zcu = pt.zcu; 36732 const enum_type = zcu.intern_pool.loadEnumType(ty.toIntern()); 36733 assert(enum_type.tag_mode != .nonexhaustive); 36734 // The `tagValueIndex` function call below relies on the type being the integer tag type. 36735 // `getCoerced` assumes the value will fit the new type. 36736 if (!(try sema.intFitsInType(int, .fromInterned(enum_type.tag_ty), null))) return false; 36737 const int_coerced = try pt.getCoerced(int, .fromInterned(enum_type.tag_ty)); 36738 36739 return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null; 36740 } 36741 36742 /// Asserts the values are comparable. Both operands have type `ty`. 36743 /// For vectors, returns true if the comparison is true for ALL elements. 36744 /// 36745 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)` 36746 fn compareAll( 36747 sema: *Sema, 36748 lhs: Value, 36749 op: std.math.CompareOperator, 36750 rhs: Value, 36751 ty: Type, 36752 ) CompileError!bool { 36753 const pt = sema.pt; 36754 const zcu = pt.zcu; 36755 if (ty.zigTypeTag(zcu) == .vector) { 36756 var i: usize = 0; 36757 while (i < ty.vectorLen(zcu)) : (i += 1) { 36758 const lhs_elem = try lhs.elemValue(pt, i); 36759 const rhs_elem = try rhs.elemValue(pt, i); 36760 if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)))) { 36761 return false; 36762 } 36763 } 36764 return true; 36765 } 36766 return sema.compareScalar(lhs, op, rhs, ty); 36767 } 36768 36769 /// Asserts the values are comparable. Both operands have type `ty`. 36770 fn compareScalar( 36771 sema: *Sema, 36772 lhs: Value, 36773 op: std.math.CompareOperator, 36774 rhs: Value, 36775 ty: Type, 36776 ) CompileError!bool { 36777 const pt = sema.pt; 36778 const coerced_lhs = try pt.getCoerced(lhs, ty); 36779 const coerced_rhs = try pt.getCoerced(rhs, ty); 36780 36781 // Equality comparisons of signed zero and NaN need to use floating point semantics 36782 if (coerced_lhs.isFloat(pt.zcu) or coerced_rhs.isFloat(pt.zcu)) 36783 return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt); 36784 36785 switch (op) { 36786 .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty), 36787 .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)), 36788 else => return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt), 36789 } 36790 } 36791 36792 fn valuesEqual( 36793 sema: *Sema, 36794 lhs: Value, 36795 rhs: Value, 36796 ty: Type, 36797 ) CompileError!bool { 36798 return lhs.eql(rhs, ty, sema.pt.zcu); 36799 } 36800 36801 /// Asserts the values are comparable vectors of type `ty`. 36802 fn compareVector( 36803 sema: *Sema, 36804 lhs: Value, 36805 op: std.math.CompareOperator, 36806 rhs: Value, 36807 ty: Type, 36808 ) !Value { 36809 const pt = sema.pt; 36810 const zcu = pt.zcu; 36811 assert(ty.zigTypeTag(zcu) == .vector); 36812 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(zcu)); 36813 for (result_data, 0..) |*scalar, i| { 36814 const lhs_elem = try lhs.elemValue(pt, i); 36815 const rhs_elem = try rhs.elemValue(pt, i); 36816 if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) { 36817 scalar.* = .undef_bool; 36818 } else { 36819 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)); 36820 scalar.* = Value.makeBool(res_bool).toIntern(); 36821 } 36822 } 36823 return pt.aggregateValue(try pt.vectorType(.{ 36824 .len = ty.vectorLen(zcu), 36825 .child = .bool_type, 36826 }), result_data); 36827 } 36828 36829 /// Merge lhs with rhs. 36830 /// Asserts that lhs and rhs are both error sets and are resolved. 36831 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { 36832 const pt = sema.pt; 36833 const ip = &pt.zcu.intern_pool; 36834 const arena = sema.arena; 36835 const lhs_names = lhs.errorSetNames(pt.zcu); 36836 const rhs_names = rhs.errorSetNames(pt.zcu); 36837 var names: InferredErrorSet.NameMap = .{}; 36838 try names.ensureUnusedCapacity(arena, lhs_names.len); 36839 36840 for (0..lhs_names.len) |lhs_index| { 36841 names.putAssumeCapacityNoClobber(lhs_names.get(ip)[lhs_index], {}); 36842 } 36843 for (0..rhs_names.len) |rhs_index| { 36844 try names.put(arena, rhs_names.get(ip)[rhs_index], {}); 36845 } 36846 36847 return pt.errorSetFromUnsortedNames(names.keys()); 36848 } 36849 36850 /// Avoids crashing the compiler when asking if inferred allocations are noreturn. 36851 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool { 36852 if (ref == .unreachable_value) return true; 36853 if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) { 36854 .inferred_alloc, .inferred_alloc_comptime => return false, 36855 else => {}, 36856 }; 36857 return sema.typeOf(ref).isNoReturn(sema.pt.zcu); 36858 } 36859 36860 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type. 36861 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool { 36862 if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) { 36863 .inferred_alloc, .inferred_alloc_comptime => return false, 36864 else => {}, 36865 }; 36866 return sema.typeOf(ref).zigTypeTag(sema.pt.zcu) == tag; 36867 } 36868 36869 pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { 36870 const pt = sema.pt; 36871 if (!pt.zcu.comp.incremental) return; 36872 36873 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee); 36874 if (gop.found_existing) return; 36875 36876 // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields 36877 // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would 36878 // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve 36879 // the loop. 36880 // Note that this also disallows a `nav_val` 36881 switch (sema.owner.unwrap()) { 36882 .nav_val => |this_nav| switch (dependee) { 36883 .nav_val => |other_nav| if (this_nav == other_nav) return, 36884 else => {}, 36885 }, 36886 .nav_ty => |this_nav| switch (dependee) { 36887 .nav_ty => |other_nav| if (this_nav == other_nav) return, 36888 else => {}, 36889 }, 36890 else => {}, 36891 } 36892 36893 try pt.addDependency(sema.owner, dependee); 36894 } 36895 36896 fn isComptimeMutablePtr(sema: *Sema, val: Value) bool { 36897 return switch (sema.pt.zcu.intern_pool.indexToKey(val.toIntern())) { 36898 .slice => |slice| sema.isComptimeMutablePtr(Value.fromInterned(slice.ptr)), 36899 .ptr => |ptr| switch (ptr.base_addr) { 36900 .uav, .nav, .int => false, 36901 .comptime_field => true, 36902 .comptime_alloc => |alloc_index| !sema.getComptimeAlloc(alloc_index).is_const, 36903 .eu_payload, .opt_payload => |base| sema.isComptimeMutablePtr(Value.fromInterned(base)), 36904 .arr_elem, .field => |bi| sema.isComptimeMutablePtr(Value.fromInterned(bi.base)), 36905 }, 36906 else => false, 36907 }; 36908 } 36909 36910 fn checkRuntimeValue(sema: *Sema, ptr: Air.Inst.Ref) bool { 36911 const val = ptr.toInterned() orelse return true; 36912 return !Value.fromInterned(val).canMutateComptimeVarState(sema.pt.zcu); 36913 } 36914 36915 fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Air.Inst.Ref) CompileError!void { 36916 if (sema.checkRuntimeValue(val)) return; 36917 return sema.failWithOwnedErrorMsg(block, msg: { 36918 const msg = try sema.errMsg(val_src, "runtime value contains reference to comptime var", .{}); 36919 errdefer msg.destroy(sema.gpa); 36920 try sema.errNote(val_src, msg, "comptime var pointers are not available at runtime", .{}); 36921 const pt = sema.pt; 36922 const zcu = pt.zcu; 36923 const val_str = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, "runtime_value", .no_embedded_nulls); 36924 try sema.explainWhyValueContainsReferenceToComptimeVar(msg, val_src, val_str, .fromInterned(val.toInterned().?)); 36925 break :msg msg; 36926 }); 36927 } 36928 36929 fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError { 36930 return sema.failWithOwnedErrorMsg(block, msg: { 36931 const msg = try sema.errMsg(src, "{s} contains reference to comptime var", .{kind_of_value}); 36932 errdefer msg.destroy(sema.gpa); 36933 if (val) |v| try sema.explainWhyValueContainsReferenceToComptimeVar(msg, src, value_name, v); 36934 break :msg msg; 36935 }); 36936 } 36937 36938 fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, val: Value) Allocator.Error!void { 36939 // Our goal is something like this: 36940 // note: '(value.? catch unreachable)[0]' points to 'v0.?.foo' 36941 // note: '(v0.?.bar catch unreachable)' points to 'v1' 36942 // note: 'v1.?' points to a comptime var 36943 36944 var intermediate_value_count: u32 = 0; 36945 var cur_val: Value = val; 36946 while (true) { 36947 switch (try sema.notePathToComptimeAllocPtr(msg, src, cur_val, intermediate_value_count, value_name)) { 36948 .done => return, 36949 .new_val => |new_val| { 36950 intermediate_value_count += 1; 36951 cur_val = new_val; 36952 }, 36953 } 36954 } 36955 } 36956 36957 fn notePathToComptimeAllocPtr( 36958 sema: *Sema, 36959 msg: *Zcu.ErrorMsg, 36960 src: LazySrcLoc, 36961 val: Value, 36962 intermediate_value_count: u32, 36963 start_value_name: InternPool.NullTerminatedString, 36964 ) Allocator.Error!union(enum) { 36965 done, 36966 new_val: Value, 36967 } { 36968 const arena = sema.arena; 36969 const pt = sema.pt; 36970 const zcu = pt.zcu; 36971 const ip = &zcu.intern_pool; 36972 36973 var first_path: std.ArrayListUnmanaged(u8) = .empty; 36974 if (intermediate_value_count == 0) { 36975 try first_path.print(arena, "{f}", .{start_value_name.fmt(ip)}); 36976 } else { 36977 try first_path.print(arena, "v{d}", .{intermediate_value_count - 1}); 36978 } 36979 36980 const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path); 36981 36982 switch (ip.indexToKey(comptime_ptr.toIntern()).ptr.base_addr) { 36983 .comptime_field => { 36984 try sema.errNote(src, msg, "'{s}' points to comptime field", .{first_path.items}); 36985 return .done; 36986 }, 36987 .comptime_alloc => |idx| { 36988 const cta = sema.getComptimeAlloc(idx); 36989 if (!cta.is_const) { 36990 try sema.errNote(cta.src, msg, "'{s}' points to comptime var declared here", .{first_path.items}); 36991 return .done; 36992 } 36993 }, 36994 else => {}, // there will be another stage 36995 } 36996 36997 const derivation = comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema) catch |err| switch (err) { 36998 error.OutOfMemory => |e| return e, 36999 error.AnalysisFail => unreachable, 37000 }; 37001 37002 var second_path_aw: std.Io.Writer.Allocating = .init(arena); 37003 defer second_path_aw.deinit(); 37004 const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count}); 37005 const deriv_start = @import("print_value.zig").printPtrDerivation( 37006 derivation, 37007 &second_path_aw.writer, 37008 pt, 37009 .lvalue, 37010 .{ .str = inter_name }, 37011 20, 37012 ) catch return error.OutOfMemory; 37013 37014 switch (deriv_start) { 37015 .int, .nav_ptr => unreachable, 37016 .uav_ptr => |uav| { 37017 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() }); 37018 return .{ .new_val = .fromInterned(uav.val) }; 37019 }, 37020 .comptime_alloc_ptr => |cta_info| { 37021 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() }); 37022 const cta = sema.getComptimeAlloc(cta_info.idx); 37023 if (cta.is_const) { 37024 return .{ .new_val = cta_info.val }; 37025 } else { 37026 try sema.errNote(cta.src, msg, "'{s}' is a comptime var declared here", .{inter_name}); 37027 return .done; 37028 } 37029 }, 37030 .comptime_field_ptr => { 37031 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() }); 37032 try sema.errNote(src, msg, "'{s}' is a comptime field", .{inter_name}); 37033 return .done; 37034 }, 37035 .eu_payload_ptr, 37036 .opt_payload_ptr, 37037 .field_ptr, 37038 .elem_ptr, 37039 .offset_and_cast, 37040 => unreachable, 37041 } 37042 } 37043 37044 fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayListUnmanaged(u8)) Allocator.Error!Value { 37045 const pt = sema.pt; 37046 const zcu = pt.zcu; 37047 const ip = &zcu.intern_pool; 37048 const arena = sema.arena; 37049 assert(val.canMutateComptimeVarState(zcu)); 37050 switch (ip.indexToKey(val.toIntern())) { 37051 .ptr => return val, 37052 .error_union => |eu| { 37053 try path.insert(arena, 0, '('); 37054 try path.appendSlice(arena, " catch unreachable)"); 37055 return sema.notePathToComptimeAllocPtrInner(.fromInterned(eu.val.payload), path); 37056 }, 37057 .slice => |slice| { 37058 try path.appendSlice(arena, ".ptr"); 37059 return sema.notePathToComptimeAllocPtrInner(.fromInterned(slice.ptr), path); 37060 }, 37061 .opt => |opt| { 37062 try path.appendSlice(arena, ".?"); 37063 return sema.notePathToComptimeAllocPtrInner(.fromInterned(opt.val), path); 37064 }, 37065 .un => |un| { 37066 assert(un.tag != .none); 37067 const union_ty: Type = .fromInterned(un.ty); 37068 const backing_enum = union_ty.unionTagTypeHypothetical(zcu); 37069 const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?; 37070 const field_name = backing_enum.enumFieldName(field_idx, zcu); 37071 try path.print(arena, ".{f}", .{field_name.fmt(ip)}); 37072 return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path); 37073 }, 37074 .aggregate => |agg| { 37075 const elem: InternPool.Index, const elem_idx: usize = switch (agg.storage) { 37076 .bytes => unreachable, 37077 .repeated_elem => |elem| .{ elem, 0 }, 37078 .elems => |elems| for (elems, 0..) |elem, elem_idx| { 37079 if (Value.fromInterned(elem).canMutateComptimeVarState(zcu)) { 37080 break .{ elem, elem_idx }; 37081 } 37082 } else unreachable, 37083 }; 37084 const agg_ty: Type = .fromInterned(agg.ty); 37085 switch (agg_ty.zigTypeTag(zcu)) { 37086 .array, .vector => try path.print(arena, "[{d}]", .{elem_idx}), 37087 .pointer => switch (elem_idx) { 37088 Value.slice_ptr_index => try path.appendSlice(arena, ".ptr"), 37089 Value.slice_len_index => try path.appendSlice(arena, ".len"), 37090 else => unreachable, 37091 }, 37092 .@"struct" => if (agg_ty.isTuple(zcu)) { 37093 try path.print(arena, "[{d}]", .{elem_idx}); 37094 } else { 37095 const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?; 37096 try path.print(arena, ".{f}", .{name.fmt(ip)}); 37097 }, 37098 else => unreachable, 37099 } 37100 return sema.notePathToComptimeAllocPtrInner(.fromInterned(elem), path); 37101 }, 37102 else => unreachable, 37103 } 37104 } 37105 37106 /// Returns true if any value contained in `val` is undefined. 37107 fn anyUndef(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) !bool { 37108 const pt = sema.pt; 37109 const zcu = pt.zcu; 37110 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 37111 .undef => true, 37112 .simple_value => |v| v == .undefined, 37113 .slice => { 37114 // If the slice contents are runtime-known, reification will fail later on with a 37115 // specific error message. 37116 const arr = try sema.maybeDerefSliceAsArray(block, src, val) orelse return false; 37117 return sema.anyUndef(block, src, arr); 37118 }, 37119 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| { 37120 const elem = zcu.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i]; 37121 if (try sema.anyUndef(block, src, Value.fromInterned(elem))) break true; 37122 } else false, 37123 else => false, 37124 }; 37125 } 37126 37127 /// Asserts that `slice_val` is a slice of `u8`. 37128 fn sliceToIpString( 37129 sema: *Sema, 37130 block: *Block, 37131 src: LazySrcLoc, 37132 slice_val: Value, 37133 reason: ComptimeReason, 37134 ) CompileError!InternPool.NullTerminatedString { 37135 const pt = sema.pt; 37136 const zcu = pt.zcu; 37137 const slice_ty = slice_val.typeOf(zcu); 37138 assert(slice_ty.isSlice(zcu)); 37139 assert(slice_ty.childType(zcu).toIntern() == .u8_type); 37140 const array_val = try sema.derefSliceAsArray(block, src, slice_val, reason); 37141 const array_ty = array_val.typeOf(zcu); 37142 return array_val.toIpString(array_ty, pt); 37143 } 37144 37145 /// Given a slice value, attempts to dereference it into a comptime-known array. 37146 /// Emits a compile error if the contents of the slice are not comptime-known. 37147 /// Asserts that `slice_val` is a slice. 37148 fn derefSliceAsArray( 37149 sema: *Sema, 37150 block: *Block, 37151 src: LazySrcLoc, 37152 slice_val: Value, 37153 reason: ComptimeReason, 37154 ) CompileError!Value { 37155 return try sema.maybeDerefSliceAsArray(block, src, slice_val) orelse { 37156 return sema.failWithNeededComptime(block, src, reason); 37157 }; 37158 } 37159 37160 /// Given a slice value, attempts to dereference it into a comptime-known array. 37161 /// Returns `null` if the contents of the slice are not comptime-known. 37162 /// Asserts that `slice_val` is a slice. 37163 fn maybeDerefSliceAsArray( 37164 sema: *Sema, 37165 block: *Block, 37166 src: LazySrcLoc, 37167 slice_val: Value, 37168 ) CompileError!?Value { 37169 const pt = sema.pt; 37170 const zcu = pt.zcu; 37171 const ip = &zcu.intern_pool; 37172 assert(slice_val.typeOf(zcu).isSlice(zcu)); 37173 const slice = switch (ip.indexToKey(slice_val.toIntern())) { 37174 .undef => return sema.failWithUseOfUndef(block, src, null), 37175 .slice => |slice| slice, 37176 else => unreachable, 37177 }; 37178 const elem_ty = Type.fromInterned(slice.ty).childType(zcu); 37179 const len = try Value.fromInterned(slice.len).toUnsignedIntSema(pt); 37180 const array_ty = try pt.arrayType(.{ 37181 .child = elem_ty.toIntern(), 37182 .len = len, 37183 }); 37184 const ptr_ty = try pt.ptrTypeSema(p: { 37185 var p = Type.fromInterned(slice.ty).ptrInfo(zcu); 37186 p.flags.size = .one; 37187 p.child = array_ty.toIntern(); 37188 p.sentinel = .none; 37189 break :p p; 37190 }); 37191 const casted_ptr = try pt.getCoerced(Value.fromInterned(slice.ptr), ptr_ty); 37192 return sema.pointerDeref(block, src, casted_ptr, ptr_ty); 37193 } 37194 37195 fn analyzeUnreachable(sema: *Sema, block: *Block, src: LazySrcLoc, safety_check: bool) !void { 37196 if (safety_check and block.wantSafety()) { 37197 // We only apply the first hint in a branch. 37198 // This allows user-provided hints to override implicit cold hints. 37199 if (sema.branch_hint == null) { 37200 sema.branch_hint = .cold; 37201 } 37202 37203 try sema.safetyPanic(block, src, .reached_unreachable); 37204 } else { 37205 _ = try block.addNoOp(.unreach); 37206 } 37207 } 37208 37209 /// This should be called exactly once, at the end of a `Sema`'s lifetime. 37210 /// It takes the exports stored in `sema.export` and flushes them to the `Zcu` 37211 /// to be processed by the linker after the update. 37212 pub fn flushExports(sema: *Sema) !void { 37213 if (sema.exports.items.len == 0) return; 37214 37215 const zcu = sema.pt.zcu; 37216 const gpa = zcu.gpa; 37217 37218 // There may be existing exports. For instance, a struct may export 37219 // things during both field type resolution and field default resolution. 37220 // 37221 // So, pick up and delete any existing exports. This strategy performs 37222 // redundant work, but that's okay, because this case is exceedingly rare. 37223 if (zcu.single_exports.get(sema.owner)) |export_idx| { 37224 try sema.exports.append(gpa, export_idx.ptr(zcu).*); 37225 } else if (zcu.multi_exports.get(sema.owner)) |info| { 37226 try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]); 37227 } 37228 zcu.deleteUnitExports(sema.owner); 37229 37230 // `sema.exports` is completed; store the data into the `Zcu`. 37231 if (sema.exports.items.len == 1) { 37232 try zcu.single_exports.ensureUnusedCapacity(gpa, 1); 37233 const export_idx: Zcu.Export.Index = zcu.free_exports.pop() orelse idx: { 37234 _ = try zcu.all_exports.addOne(gpa); 37235 break :idx @enumFromInt(zcu.all_exports.items.len - 1); 37236 }; 37237 export_idx.ptr(zcu).* = sema.exports.items[0]; 37238 zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx); 37239 } else { 37240 try zcu.multi_exports.ensureUnusedCapacity(gpa, 1); 37241 const exports_base = zcu.all_exports.items.len; 37242 try zcu.all_exports.appendSlice(gpa, sema.exports.items); 37243 zcu.multi_exports.putAssumeCapacityNoClobber(sema.owner, .{ 37244 .index = @intCast(exports_base), 37245 .len = @intCast(sema.exports.items.len), 37246 }); 37247 } 37248 } 37249 37250 /// Called as soon as a `declared` enum type is created. 37251 /// Resolves the tag type and field inits. 37252 /// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this. 37253 pub fn resolveDeclaredEnum( 37254 pt: Zcu.PerThread, 37255 wip_ty: InternPool.WipEnumType, 37256 inst: Zir.Inst.Index, 37257 tracked_inst: InternPool.TrackedInst.Index, 37258 namespace: InternPool.NamespaceIndex, 37259 type_name: InternPool.NullTerminatedString, 37260 small: Zir.Inst.EnumDecl.Small, 37261 body: []const Zir.Inst.Index, 37262 tag_type_ref: Zir.Inst.Ref, 37263 any_values: bool, 37264 fields_len: u32, 37265 zir: Zir, 37266 body_end: usize, 37267 ) Zcu.SemaError!void { 37268 const zcu = pt.zcu; 37269 const gpa = zcu.gpa; 37270 37271 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; 37272 37273 var arena: std.heap.ArenaAllocator = .init(gpa); 37274 defer arena.deinit(); 37275 37276 var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa); 37277 defer comptime_err_ret_trace.deinit(); 37278 37279 var sema: Sema = .{ 37280 .pt = pt, 37281 .gpa = gpa, 37282 .arena = arena.allocator(), 37283 .code = zir, 37284 .owner = .wrap(.{ .type = wip_ty.index }), 37285 .func_index = .none, 37286 .func_is_naked = false, 37287 .fn_ret_ty = .void, 37288 .fn_ret_ty_ies = null, 37289 .comptime_err_ret_trace = &comptime_err_ret_trace, 37290 }; 37291 defer sema.deinit(); 37292 37293 if (zcu.comp.debugIncremental()) { 37294 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, sema.owner); 37295 info.last_update_gen = zcu.generation; 37296 } 37297 37298 try sema.declareDependency(.{ .src_hash = tracked_inst }); 37299 37300 var block: Block = .{ 37301 .parent = null, 37302 .sema = &sema, 37303 .namespace = namespace, 37304 .instructions = .{}, 37305 .inlining = null, 37306 .comptime_reason = .{ .reason = .{ 37307 .src = src, 37308 .r = .{ .simple = .enum_fields }, 37309 } }, 37310 .src_base_inst = tracked_inst, 37311 .type_name_ctx = type_name, 37312 }; 37313 defer block.instructions.deinit(gpa); 37314 37315 sema.resolveDeclaredEnumInner( 37316 &block, 37317 wip_ty, 37318 inst, 37319 tracked_inst, 37320 src, 37321 small, 37322 body, 37323 tag_type_ref, 37324 any_values, 37325 fields_len, 37326 zir, 37327 body_end, 37328 ) catch |err| switch (err) { 37329 error.ComptimeBreak => unreachable, 37330 error.ComptimeReturn => unreachable, 37331 error.OutOfMemory => |e| return e, 37332 error.AnalysisFail => { 37333 if (!zcu.failed_analysis.contains(sema.owner)) { 37334 try zcu.transitive_failed_analysis.put(gpa, sema.owner, {}); 37335 } 37336 return error.AnalysisFail; 37337 }, 37338 }; 37339 } 37340 37341 fn resolveDeclaredEnumInner( 37342 sema: *Sema, 37343 block: *Block, 37344 wip_ty: InternPool.WipEnumType, 37345 inst: Zir.Inst.Index, 37346 tracked_inst: InternPool.TrackedInst.Index, 37347 src: LazySrcLoc, 37348 small: Zir.Inst.EnumDecl.Small, 37349 body: []const Zir.Inst.Index, 37350 tag_type_ref: Zir.Inst.Ref, 37351 any_values: bool, 37352 fields_len: u32, 37353 zir: Zir, 37354 body_end: usize, 37355 ) Zcu.CompileError!void { 37356 const pt = sema.pt; 37357 const zcu = pt.zcu; 37358 const gpa = zcu.gpa; 37359 const ip = &zcu.intern_pool; 37360 37361 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; 37362 37363 const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = .zero } }; 37364 37365 const int_tag_ty = ty: { 37366 if (body.len != 0) { 37367 _ = try sema.analyzeInlineBody(block, body, inst); 37368 } 37369 37370 if (tag_type_ref != .none) { 37371 const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); 37372 if (ty.zigTypeTag(zcu) != .int and ty.zigTypeTag(zcu) != .comptime_int) { 37373 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{f}'", .{ty.fmt(pt)}); 37374 } 37375 break :ty ty; 37376 } else if (fields_len == 0) { 37377 break :ty try pt.intType(.unsigned, 0); 37378 } else { 37379 const bits = std.math.log2_int_ceil(usize, fields_len); 37380 break :ty try pt.intType(.unsigned, bits); 37381 } 37382 }; 37383 37384 wip_ty.setTagTy(ip, int_tag_ty.toIntern()); 37385 37386 var extra_index = body_end + bit_bags_count; 37387 var bit_bag_index: usize = body_end; 37388 var cur_bit_bag: u32 = undefined; 37389 var last_tag_val: ?Value = null; 37390 for (0..fields_len) |field_i_usize| { 37391 const field_i: u32 = @intCast(field_i_usize); 37392 if (field_i % 32 == 0) { 37393 cur_bit_bag = zir.extra[bit_bag_index]; 37394 bit_bag_index += 1; 37395 } 37396 const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0; 37397 cur_bit_bag >>= 1; 37398 37399 const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]); 37400 const field_name_zir = zir.nullTerminatedString(field_name_index); 37401 extra_index += 1; // field name 37402 37403 const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls); 37404 37405 const value_src: LazySrcLoc = .{ 37406 .base_node_inst = tracked_inst, 37407 .offset = .{ .container_field_value = field_i }, 37408 }; 37409 37410 const tag_overflow = if (has_tag_value) overflow: { 37411 const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 37412 extra_index += 1; 37413 const tag_inst = try sema.resolveInst(tag_val_ref); 37414 last_tag_val = try sema.resolveConstDefinedValue(block, .{ 37415 .base_node_inst = tracked_inst, 37416 .offset = .{ .container_field_name = field_i }, 37417 }, tag_inst, .{ .simple = .enum_field_tag_value }); 37418 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true; 37419 last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty); 37420 if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| { 37421 assert(conflict.kind == .value); // AstGen validated names are unique 37422 const other_field_src: LazySrcLoc = .{ 37423 .base_node_inst = tracked_inst, 37424 .offset = .{ .container_field_value = conflict.prev_field_idx }, 37425 }; 37426 const msg = msg: { 37427 const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)}); 37428 errdefer msg.destroy(gpa); 37429 try sema.errNote(other_field_src, msg, "other occurrence here", .{}); 37430 break :msg msg; 37431 }; 37432 return sema.failWithOwnedErrorMsg(block, msg); 37433 } 37434 break :overflow false; 37435 } else if (any_values) overflow: { 37436 if (last_tag_val) |last_tag| { 37437 const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag); 37438 last_tag_val = result.val; 37439 if (result.overflow) break :overflow true; 37440 } else { 37441 last_tag_val = try pt.intValue(int_tag_ty, 0); 37442 } 37443 if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| { 37444 assert(conflict.kind == .value); // AstGen validated names are unique 37445 const other_field_src: LazySrcLoc = .{ 37446 .base_node_inst = tracked_inst, 37447 .offset = .{ .container_field_value = conflict.prev_field_idx }, 37448 }; 37449 const msg = msg: { 37450 const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)}); 37451 errdefer msg.destroy(gpa); 37452 try sema.errNote(other_field_src, msg, "other occurrence here", .{}); 37453 break :msg msg; 37454 }; 37455 return sema.failWithOwnedErrorMsg(block, msg); 37456 } 37457 break :overflow false; 37458 } else overflow: { 37459 assert(wip_ty.nextField(ip, field_name, .none) == null); 37460 last_tag_val = try pt.intValue(.comptime_int, field_i); 37461 if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true; 37462 last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty); 37463 break :overflow false; 37464 }; 37465 37466 if (tag_overflow) { 37467 const msg = try sema.errMsg(value_src, "enumeration value '{f}' too large for type '{f}'", .{ 37468 last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt), 37469 }); 37470 return sema.failWithOwnedErrorMsg(block, msg); 37471 } 37472 } 37473 if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) { 37474 if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) { 37475 return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); 37476 } 37477 } 37478 } 37479 37480 pub const bitCastVal = @import("Sema/bitcast.zig").bitCast; 37481 pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice; 37482 37483 const loadComptimePtr = @import("Sema/comptime_ptr_access.zig").loadComptimePtr; 37484 const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadResult; 37485 const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr; 37486 const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult; 37487 37488 pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!Type { 37489 assert(decl.kind() == .type); 37490 try sema.ensureMemoizedStateResolved(src, decl.stage()); 37491 return .fromInterned(sema.pt.zcu.builtin_decl_values.get(decl)); 37492 } 37493 pub fn getBuiltin(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!InternPool.Index { 37494 assert(decl.kind() != .type); 37495 try sema.ensureMemoizedStateResolved(src, decl.stage()); 37496 return sema.pt.zcu.builtin_decl_values.get(decl); 37497 } 37498 37499 pub const NavPtrModifiers = struct { 37500 alignment: Alignment, 37501 @"linksection": InternPool.OptionalNullTerminatedString, 37502 @"addrspace": std.builtin.AddressSpace, 37503 }; 37504 37505 pub fn resolveNavPtrModifiers( 37506 sema: *Sema, 37507 block: *Block, 37508 zir_decl: Zir.Inst.Declaration.Unwrapped, 37509 decl_inst: Zir.Inst.Index, 37510 nav_ty: Type, 37511 ) CompileError!NavPtrModifiers { 37512 const pt = sema.pt; 37513 const zcu = pt.zcu; 37514 const gpa = zcu.gpa; 37515 const ip = &zcu.intern_pool; 37516 37517 const align_src = block.src(.{ .node_offset_var_decl_align = .zero }); 37518 const section_src = block.src(.{ .node_offset_var_decl_section = .zero }); 37519 const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = .zero }); 37520 37521 const alignment: InternPool.Alignment = a: { 37522 const align_body = zir_decl.align_body orelse break :a .none; 37523 const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst); 37524 break :a try sema.analyzeAsAlign(block, align_src, align_ref); 37525 }; 37526 37527 const @"linksection": InternPool.OptionalNullTerminatedString = ls: { 37528 const linksection_body = zir_decl.linksection_body orelse break :ls .none; 37529 const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst); 37530 const bytes = try sema.toConstString(block, section_src, linksection_ref, .{ .simple = .@"linksection" }); 37531 if (std.mem.indexOfScalar(u8, bytes, 0) != null) { 37532 return sema.fail(block, section_src, "linksection cannot contain null bytes", .{}); 37533 } else if (bytes.len == 0) { 37534 return sema.fail(block, section_src, "linksection cannot be empty", .{}); 37535 } 37536 break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls); 37537 }; 37538 37539 const @"addrspace": std.builtin.AddressSpace = as: { 37540 const addrspace_ctx: std.builtin.AddressSpace.Context = switch (zir_decl.kind) { 37541 .@"var" => .variable, 37542 else => switch (nav_ty.zigTypeTag(zcu)) { 37543 .@"fn" => .function, 37544 else => .constant, 37545 }, 37546 }; 37547 const target = zcu.getTarget(); 37548 const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) { 37549 .function => target_util.defaultAddressSpace(target, .function), 37550 .variable => target_util.defaultAddressSpace(target, .global_mutable), 37551 .constant => target_util.defaultAddressSpace(target, .global_constant), 37552 else => unreachable, 37553 }; 37554 const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst); 37555 break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx); 37556 }; 37557 37558 return .{ 37559 .alignment = alignment, 37560 .@"linksection" = @"linksection", 37561 .@"addrspace" = @"addrspace", 37562 }; 37563 } 37564 37565 pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool { 37566 const pt = sema.pt; 37567 const zcu = pt.zcu; 37568 const ip = &zcu.intern_pool; 37569 const gpa = zcu.gpa; 37570 37571 var any_changed = false; 37572 37573 inline for (comptime std.enums.values(Zcu.BuiltinDecl)) |builtin_decl| { 37574 if (stage == comptime builtin_decl.stage()) { 37575 const parent_ns: Zcu.Namespace.Index, const parent_name: []const u8, const name: []const u8 = switch (comptime builtin_decl.access()) { 37576 .direct => |name| .{ builtin_namespace, "std.builtin", name }, 37577 .nested => |nested| access: { 37578 const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(nested[0])); 37579 const parent_ns = parent_ty.getNamespace(zcu).unwrap() orelse { 37580 return sema.fail(block, simple_src, "std.builtin.{s} is not a container type", .{@tagName(nested[0])}); 37581 }; 37582 break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] }; 37583 }, 37584 }; 37585 37586 const name_nts = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); 37587 const nav = try sema.namespaceLookup(block, simple_src, parent_ns, name_nts) orelse 37588 return sema.fail(block, simple_src, "{s} missing {s}", .{ parent_name, name }); 37589 37590 const src: LazySrcLoc = .{ 37591 .base_node_inst = ip.getNav(nav).srcInst(ip), 37592 .offset = .nodeOffset(.zero), 37593 }; 37594 37595 const result = try sema.analyzeNavVal(block, src, nav); 37596 37597 const uncoerced_val = try sema.resolveConstDefinedValue(block, src, result, null); 37598 const maybe_lazy_val: Value = switch (builtin_decl.kind()) { 37599 .type => if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .type) { 37600 return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name }); 37601 } else val: { 37602 try uncoerced_val.toType().resolveFully(pt); 37603 break :val uncoerced_val; 37604 }, 37605 .func => val: { 37606 const func_ty = try sema.getExpectedBuiltinFnType(builtin_decl); 37607 const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src); 37608 break :val .fromInterned(coerced.toInterned().?); 37609 }, 37610 .string => val: { 37611 const coerced = try sema.coerce(block, .slice_const_u8, Air.internedToRef(uncoerced_val.toIntern()), src); 37612 break :val .fromInterned(coerced.toInterned().?); 37613 }, 37614 }; 37615 const val = try sema.resolveLazyValue(maybe_lazy_val); 37616 37617 const prev = zcu.builtin_decl_values.get(builtin_decl); 37618 if (val.toIntern() != prev) { 37619 zcu.builtin_decl_values.set(builtin_decl, val.toIntern()); 37620 any_changed = true; 37621 } 37622 } 37623 } 37624 37625 return any_changed; 37626 } 37627 37628 /// Given that `decl.kind() == .func`, get the type expected of the function. 37629 fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Type { 37630 const pt = sema.pt; 37631 return switch (decl) { 37632 // `noinline fn () void` 37633 .returnError => try pt.funcType(.{ 37634 .param_types = &.{}, 37635 .return_type = .void_type, 37636 .is_noinline = true, 37637 }), 37638 37639 // `fn ([]const u8, ?usize) noreturn` 37640 .@"panic.call" => try pt.funcType(.{ 37641 .param_types = &.{ 37642 .slice_const_u8_type, 37643 (try pt.optionalType(.usize_type)).toIntern(), 37644 }, 37645 .return_type = .noreturn_type, 37646 }), 37647 37648 // `fn (anytype, anytype) noreturn` 37649 .@"panic.sentinelMismatch", 37650 .@"panic.inactiveUnionField", 37651 => try pt.funcType(.{ 37652 .param_types = &.{ .generic_poison_type, .generic_poison_type }, 37653 .return_type = .noreturn_type, 37654 .is_generic = true, 37655 }), 37656 37657 // `fn (anyerror) noreturn` 37658 .@"panic.unwrapError" => try pt.funcType(.{ 37659 .param_types = &.{.anyerror_type}, 37660 .return_type = .noreturn_type, 37661 }), 37662 37663 // `fn (usize) noreturn` 37664 .@"panic.sliceCastLenRemainder" => try pt.funcType(.{ 37665 .param_types = &.{.usize_type}, 37666 .return_type = .noreturn_type, 37667 }), 37668 37669 // `fn (usize, usize) noreturn` 37670 .@"panic.outOfBounds", 37671 .@"panic.startGreaterThanEnd", 37672 => try pt.funcType(.{ 37673 .param_types = &.{ .usize_type, .usize_type }, 37674 .return_type = .noreturn_type, 37675 }), 37676 37677 // `fn () noreturn` 37678 .@"panic.reachedUnreachable", 37679 .@"panic.unwrapNull", 37680 .@"panic.castToNull", 37681 .@"panic.incorrectAlignment", 37682 .@"panic.invalidErrorCode", 37683 .@"panic.integerOutOfBounds", 37684 .@"panic.integerOverflow", 37685 .@"panic.shlOverflow", 37686 .@"panic.shrOverflow", 37687 .@"panic.divideByZero", 37688 .@"panic.exactDivisionRemainder", 37689 .@"panic.integerPartOutOfBounds", 37690 .@"panic.corruptSwitch", 37691 .@"panic.shiftRhsTooBig", 37692 .@"panic.invalidEnumValue", 37693 .@"panic.forLenMismatch", 37694 .@"panic.copyLenMismatch", 37695 .@"panic.memcpyAlias", 37696 .@"panic.noreturnReturned", 37697 => try pt.funcType(.{ 37698 .param_types = &.{}, 37699 .return_type = .noreturn_type, 37700 }), 37701 37702 else => unreachable, 37703 }; 37704 }