blob 8ea0a19f (1596766B) - Raw
1 //! Semantic analysis of ZIR instructions. 2 //! Shared to every Block. Stored on the stack. 3 //! State used for compiling a ZIR into AIR. 4 //! Transforms untyped ZIR instructions into semantically-analyzed AIR instructions. 5 //! Does type checking, comptime control flow, and safety-check generation. 6 //! This is the the heart of the Zig compiler. 7 8 const std = @import("std"); 9 const math = std.math; 10 const mem = std.mem; 11 const Allocator = mem.Allocator; 12 const assert = std.debug.assert; 13 const log = std.log.scoped(.sema); 14 15 const Sema = @This(); 16 const Value = @import("Value.zig"); 17 const MutableValue = @import("mutable_value.zig").MutableValue; 18 const Type = @import("Type.zig"); 19 const Air = @import("Air.zig"); 20 const Zir = std.zig.Zir; 21 const Zcu = @import("Zcu.zig"); 22 const trace = @import("tracy.zig").trace; 23 const Namespace = Zcu.Namespace; 24 const CompileError = Zcu.CompileError; 25 const SemaError = Zcu.SemaError; 26 const LazySrcLoc = Zcu.LazySrcLoc; 27 const RangeSet = @import("RangeSet.zig"); 28 const target_util = @import("target.zig"); 29 const Package = @import("Package.zig"); 30 const crash_report = @import("crash_report.zig"); 31 const build_options = @import("build_options"); 32 const Compilation = @import("Compilation.zig"); 33 const InternPool = @import("InternPool.zig"); 34 const Alignment = InternPool.Alignment; 35 const AnalUnit = InternPool.AnalUnit; 36 const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; 37 const Cache = std.Build.Cache; 38 const LowerZon = @import("Sema/LowerZon.zig"); 39 const arith = @import("Sema/arith.zig"); 40 41 pt: Zcu.PerThread, 42 /// Alias to `zcu.gpa`. 43 gpa: Allocator, 44 /// Points to the temporary arena allocator of the Sema. 45 /// This arena will be cleared when the sema is destroyed. 46 arena: Allocator, 47 code: Zir, 48 air_instructions: std.MultiArrayList(Air.Inst) = .{}, 49 air_extra: std.ArrayListUnmanaged(u32) = .empty, 50 /// Maps ZIR to AIR. 51 inst_map: InstMap = .{}, 52 /// The "owner" of a `Sema` represents the root "thing" that is being analyzed. 53 /// This does not change throughout the entire lifetime of a `Sema`. For instance, 54 /// when analyzing a runtime function body, this is always `func` of that function, 55 /// even if an inline/comptime function call is being analyzed. 56 owner: AnalUnit, 57 /// The function this ZIR code is the body of, according to the source code. 58 /// This starts out the same as `sema.owner.func` if applicable, and then diverges 59 /// in the case of an inline or comptime function call. 60 /// This could be `none`, a `func_decl`, or a `func_instance`. 61 func_index: InternPool.Index, 62 /// Whether the type of func_index has a calling convention of `.naked`. 63 func_is_naked: bool, 64 /// Used to restore the error return trace when returning a non-error from a function. 65 error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none, 66 comptime_err_ret_trace: *std.ArrayList(LazySrcLoc), 67 /// When semantic analysis needs to know the return type of the function whose body 68 /// is being analyzed, this `Type` should be used instead of going through `func`. 69 /// This will correctly handle the case of a comptime/inline function call of a 70 /// generic function which uses a type expression for the return type. 71 /// The type will be `void` in the case that `func` is `null`. 72 fn_ret_ty: Type, 73 /// In case of the return type being an error union with an inferred error 74 /// set, this is the inferred error set. `null` otherwise. Allocated with 75 /// `Sema.arena`. 76 fn_ret_ty_ies: ?*InferredErrorSet, 77 branch_quota: u32 = default_branch_quota, 78 branch_count: u32 = 0, 79 /// Populated when returning `error.ComptimeBreak`. Used to communicate the 80 /// break instruction up the stack to find the corresponding Block. 81 comptime_break_inst: Zir.Inst.Index = undefined, 82 /// These are lazily created runtime blocks from block_inline instructions. 83 /// They are created when an break_inline passes through a runtime condition, because 84 /// Sema must convert comptime control flow to runtime control flow, which means 85 /// breaking from a block. 86 post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .empty, 87 /// Populated with the last compile error created. 88 err: ?*Zcu.ErrorMsg = null, 89 90 /// The temporary arena is used for the memory of the `InferredAlloc` values 91 /// here so the values can be dropped without any cleanup. 92 unresolved_inferred_allocs: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .empty, 93 94 /// Links every pointer derived from a base `alloc` back to that `alloc`. Used 95 /// to detect comptime-known `const`s. 96 /// TODO: ZIR liveness analysis would allow us to remove elements from this map. 97 base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .empty, 98 99 /// Runtime `alloc`s are placed in this map to track all comptime-known writes 100 /// before the corresponding `make_ptr_const` instruction. 101 /// If any store to the alloc depends on a runtime condition or stores a runtime 102 /// value, the corresponding element in this map is erased, to indicate that the 103 /// alloc is not comptime-known. 104 /// If the alloc remains in this map when `make_ptr_const` is reached, its value 105 /// is comptime-known, and all stores to the pointer must be applied at comptime 106 /// to determine the comptime value. 107 /// Backed by gpa. 108 maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .empty, 109 110 /// Comptime-mutable allocs, and any comptime allocs which reference it, are 111 /// stored as elements of this array. 112 /// Pointers to such memory are represented via an index into this array. 113 /// Backed by gpa. 114 comptime_allocs: std.ArrayListUnmanaged(ComptimeAlloc) = .empty, 115 116 /// A list of exports performed by this analysis. After this `Sema` terminates, 117 /// these are flushed to `Zcu.single_exports` or `Zcu.multi_exports`. 118 exports: std.ArrayListUnmanaged(Zcu.Export) = .empty, 119 120 /// All references registered so far by this `Sema`. This is a temporary duplicate 121 /// of data stored in `Zcu.all_references`. It exists to avoid adding references to 122 /// a given `AnalUnit` multiple times. 123 references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty, 124 type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty, 125 126 /// All dependencies registered so far by this `Sema`. This is a temporary duplicate 127 /// of the main dependency data. It exists to avoid adding dependencies to a given 128 /// `AnalUnit` multiple times. 129 dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .empty, 130 131 /// Whether memoization of this call is permitted. Operations with side effects global 132 /// to the `Sema`, such as `@setEvalBranchQuota`, set this to `false`. It is observed 133 /// by `analyzeCall`. 134 allow_memoize: bool = true, 135 136 /// The `BranchHint` for the current branch of runtime control flow. 137 /// This state is on `Sema` so that `cold` hints can be propagated up through blocks with less special handling. 138 branch_hint: ?std.builtin.BranchHint = null, 139 140 const RuntimeIndex = enum(u32) { 141 zero = 0, 142 comptime_field_ptr = std.math.maxInt(u32), 143 _, 144 145 pub fn increment(ri: *RuntimeIndex) void { 146 ri.* = @enumFromInt(@intFromEnum(ri.*) + 1); 147 } 148 }; 149 150 const MaybeComptimeAlloc = struct { 151 /// The runtime index of the `alloc` instruction. 152 runtime_index: RuntimeIndex, 153 /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to 154 /// RLS, a single comptime-known allocation may have arbitrarily many stores. 155 /// This list also contains `set_union_tag`, `optional_payload_ptr_set`, and 156 /// `errunion_payload_ptr_set` instructions. 157 /// If the instruction is one of these three tags, `src` may be `.unneeded`. 158 stores: std.MultiArrayList(struct { 159 inst: Air.Inst.Index, 160 src: LazySrcLoc, 161 }) = .{}, 162 }; 163 164 const ComptimeAlloc = struct { 165 val: MutableValue, 166 is_const: bool, 167 src: LazySrcLoc, 168 /// `.none` indicates that the alignment is the natural alignment of `val`. 169 alignment: Alignment, 170 /// This is the `runtime_index` at the point of this allocation. If an store 171 /// to this alloc ever occurs with a runtime index greater than this one, it 172 /// is behind a runtime condition, so a compile error will be emitted. 173 runtime_index: RuntimeIndex, 174 }; 175 176 /// `src` may be `null` if `is_const` will be set. 177 fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex { 178 const idx = sema.comptime_allocs.items.len; 179 try sema.comptime_allocs.append(sema.gpa, .{ 180 .val = .{ .interned = try sema.pt.intern(.{ .undef = ty.toIntern() }) }, 181 .is_const = false, 182 .src = src, 183 .alignment = alignment, 184 .runtime_index = block.runtime_index, 185 }); 186 return @enumFromInt(idx); 187 } 188 189 pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc { 190 return &sema.comptime_allocs.items[@intFromEnum(idx)]; 191 } 192 193 pub const default_branch_quota = 1000; 194 195 pub const InferredErrorSet = struct { 196 /// The function body from which this error set originates. 197 /// This is `none` in the case of a comptime/inline function call, corresponding to 198 /// `InternPool.Index.adhoc_inferred_error_set_type`. 199 /// The function's resolved error set is not set until analysis of the 200 /// function body completes. 201 func: InternPool.Index, 202 /// All currently known errors that this error set contains. This includes 203 /// direct additions via `return error.Foo;`, and possibly also errors that 204 /// are returned from any dependent functions. 205 errors: NameMap = .{}, 206 /// Other inferred error sets which this inferred error set should include. 207 inferred_error_sets: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty, 208 /// The regular error set created by resolving this inferred error set. 209 resolved: InternPool.Index = .none, 210 211 pub const NameMap = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void); 212 213 pub fn addErrorSet( 214 self: *InferredErrorSet, 215 err_set_ty: Type, 216 ip: *InternPool, 217 arena: Allocator, 218 ) !void { 219 switch (err_set_ty.toIntern()) { 220 .anyerror_type => self.resolved = .anyerror_type, 221 .adhoc_inferred_error_set_type => {}, // Adding an inferred error set to itself. 222 223 else => switch (ip.indexToKey(err_set_ty.toIntern())) { 224 .error_set_type => |error_set_type| { 225 for (error_set_type.names.get(ip)) |name| { 226 try self.errors.put(arena, name, {}); 227 } 228 }, 229 .inferred_error_set_type => { 230 try self.inferred_error_sets.put(arena, err_set_ty.toIntern(), {}); 231 }, 232 else => unreachable, 233 }, 234 } 235 } 236 }; 237 238 /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve 239 /// instructions during analysis. 240 /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the 241 /// zir instruction index and a start offset. An index is not present in the map if the value 242 /// at the index is `Air.Inst.Ref.none`. 243 /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that 244 /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can 245 /// be called safely for any of the instructions passed in. 246 pub const InstMap = struct { 247 items: []Air.Inst.Ref = &[_]Air.Inst.Ref{}, 248 start: Zir.Inst.Index = @enumFromInt(0), 249 250 pub fn deinit(map: InstMap, allocator: mem.Allocator) void { 251 allocator.free(map.items); 252 } 253 254 pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref { 255 if (!map.contains(key)) return null; 256 return map.items[@intFromEnum(key) - @intFromEnum(map.start)]; 257 } 258 259 pub fn putAssumeCapacity( 260 map: *InstMap, 261 key: Zir.Inst.Index, 262 ref: Air.Inst.Ref, 263 ) void { 264 map.items[@intFromEnum(key) - @intFromEnum(map.start)] = ref; 265 } 266 267 pub fn putAssumeCapacityNoClobber( 268 map: *InstMap, 269 key: Zir.Inst.Index, 270 ref: Air.Inst.Ref, 271 ) void { 272 assert(!map.contains(key)); 273 map.putAssumeCapacity(key, ref); 274 } 275 276 pub const GetOrPutResult = struct { 277 value_ptr: *Air.Inst.Ref, 278 found_existing: bool, 279 }; 280 281 pub fn getOrPutAssumeCapacity( 282 map: *InstMap, 283 key: Zir.Inst.Index, 284 ) GetOrPutResult { 285 const index = @intFromEnum(key) - @intFromEnum(map.start); 286 return GetOrPutResult{ 287 .value_ptr = &map.items[index], 288 .found_existing = map.items[index] != .none, 289 }; 290 } 291 292 pub fn remove(map: InstMap, key: Zir.Inst.Index) bool { 293 if (!map.contains(key)) return false; 294 map.items[@intFromEnum(key) - @intFromEnum(map.start)] = .none; 295 return true; 296 } 297 298 pub fn contains(map: InstMap, key: Zir.Inst.Index) bool { 299 return map.items[@intFromEnum(key) - @intFromEnum(map.start)] != .none; 300 } 301 302 pub fn ensureSpaceForInstructions( 303 map: *InstMap, 304 allocator: mem.Allocator, 305 insts: []const Zir.Inst.Index, 306 ) !void { 307 const start, const end = mem.minMax(u32, @ptrCast(insts)); 308 const map_start = @intFromEnum(map.start); 309 if (map_start <= start and end < map.items.len + map_start) 310 return; 311 312 const old_start = if (map.items.len == 0) start else map_start; 313 var better_capacity = map.items.len; 314 var better_start = old_start; 315 while (true) { 316 const extra_capacity = better_capacity / 2 + 16; 317 better_capacity += extra_capacity; 318 better_start -|= @intCast(extra_capacity / 2); 319 if (better_start <= start and end < better_capacity + better_start) 320 break; 321 } 322 323 const start_diff = old_start - better_start; 324 const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity); 325 @memset(new_items[0..start_diff], .none); 326 @memcpy(new_items[start_diff..][0..map.items.len], map.items); 327 @memset(new_items[start_diff + map.items.len ..], .none); 328 329 allocator.free(map.items); 330 map.items = new_items; 331 map.start = @enumFromInt(better_start); 332 } 333 }; 334 335 /// This is the context needed to semantically analyze ZIR instructions and 336 /// produce AIR instructions. 337 /// This is a temporary structure stored on the stack; references to it are valid only 338 /// during semantic analysis of the block. 339 pub const Block = struct { 340 parent: ?*Block, 341 /// Shared among all child blocks. 342 sema: *Sema, 343 /// The namespace to use for lookups from this source block 344 namespace: InternPool.NamespaceIndex, 345 /// The AIR instructions generated for this block. 346 instructions: std.ArrayListUnmanaged(Air.Inst.Index), 347 // `param` instructions are collected here to be used by the `func` instruction. 348 /// When doing a generic function instantiation, this array collects a type 349 /// for each *runtime-known* parameter. This array corresponds to the instance 350 /// function type, while `Sema.comptime_args` corresponds to the generic owner 351 /// function type. 352 /// This memory is allocated by a parent `Sema` in the temporary arena, and is 353 /// used to add a `func_instance` into the `InternPool`. 354 params: std.MultiArrayList(Param) = .{}, 355 356 label: ?*Label = null, 357 inlining: ?*Inlining, 358 /// If runtime_index is not 0 then one of these is guaranteed to be non null. 359 runtime_cond: ?LazySrcLoc = null, 360 runtime_loop: ?LazySrcLoc = null, 361 /// Non zero if a non-inline loop or a runtime conditional have been encountered. 362 /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index. 363 runtime_index: RuntimeIndex = .zero, 364 inline_block: Zir.Inst.OptionalIndex = .none, 365 366 comptime_reason: ?BlockComptimeReason = null, 367 is_typeof: bool = false, 368 369 /// Keep track of the active error return trace index around blocks so that we can correctly 370 /// pop the error trace upon block exit. 371 error_return_trace_index: Air.Inst.Ref = .none, 372 373 /// when null, it is determined by build mode, changed by @setRuntimeSafety 374 want_safety: ?bool = null, 375 376 /// What mode to generate float operations in, set by @setFloatMode 377 float_mode: std.builtin.FloatMode = .strict, 378 379 c_import_buf: ?*std.ArrayList(u8) = null, 380 381 /// If not `null`, this boolean is set when a `dbg_var_ptr`, `dbg_var_val`, or `dbg_arg_inline`. 382 /// instruction is emitted. It signals that the innermost lexically 383 /// enclosing `block`/`block_inline` should be translated into a real AIR 384 /// `block` in order for codegen to match lexical scoping for debug vars. 385 need_debug_scope: ?*bool = null, 386 387 /// Relative source locations encountered while traversing this block should be 388 /// treated as relative to the AST node of this ZIR instruction. 389 src_base_inst: InternPool.TrackedInst.Index, 390 391 /// The name of the current "context" for naming namespace types. 392 /// The interpretation of this depends on the name strategy in ZIR, but the name 393 /// is always incorporated into the type name somehow. 394 /// See `Sema.createTypeName`. 395 type_name_ctx: InternPool.NullTerminatedString, 396 397 /// Create a `LazySrcLoc` based on an `Offset` from the code being analyzed in this block. 398 /// Specifically, the given `Offset` is treated as relative to `block.src_base_inst`. 399 pub fn src(block: Block, offset: LazySrcLoc.Offset) LazySrcLoc { 400 return .{ 401 .base_node_inst = block.src_base_inst, 402 .offset = offset, 403 }; 404 } 405 406 fn isComptime(block: Block) bool { 407 return block.comptime_reason != null; 408 } 409 410 fn builtinCallArgSrc(block: *Block, builtin_call_node: std.zig.Ast.Node.Offset, arg_index: u32) LazySrcLoc { 411 return block.src(.{ .node_offset_builtin_call_arg = .{ 412 .builtin_call_node = builtin_call_node, 413 .arg_index = arg_index, 414 } }); 415 } 416 417 pub fn nodeOffset(block: Block, node_offset: std.zig.Ast.Node.Offset) LazySrcLoc { 418 return block.src(LazySrcLoc.Offset.nodeOffset(node_offset)); 419 } 420 421 fn tokenOffset(block: Block, tok_offset: std.zig.Ast.TokenOffset) LazySrcLoc { 422 return block.src(.{ .token_offset = tok_offset }); 423 } 424 425 const Param = struct { 426 /// `none` means `anytype`. 427 ty: InternPool.Index, 428 is_comptime: bool, 429 name: Zir.NullTerminatedString, 430 }; 431 432 /// This `Block` maps a block ZIR instruction to the corresponding 433 /// AIR instruction for break instruction analysis. 434 pub const Label = struct { 435 zir_block: Zir.Inst.Index, 436 merges: Merges, 437 }; 438 439 /// This `Block` indicates that an inline function call is happening 440 /// and return instructions should be analyzed as a break instruction 441 /// to this AIR block instruction. 442 /// It is shared among all the blocks in an inline or comptime called 443 /// function. 444 pub const Inlining = struct { 445 call_block: *Block, 446 call_src: LazySrcLoc, 447 func: InternPool.Index, 448 449 /// Populated lazily by `refFrame`. 450 ref_frame: Zcu.InlineReferenceFrame.Index.Optional = .none, 451 452 /// If `true`, the following fields are `undefined`. This doesn't represent a true inline 453 /// call, but rather a generic call analyzing the instantiation's generic type bodies. 454 is_generic_instantiation: bool, 455 456 has_comptime_args: bool, 457 comptime_result: Air.Inst.Ref, 458 merges: Merges, 459 460 fn refFrame(inlining: *Inlining, zcu: *Zcu) Allocator.Error!Zcu.InlineReferenceFrame.Index { 461 if (inlining.ref_frame == .none) { 462 inlining.ref_frame = (try zcu.addInlineReferenceFrame(.{ 463 .callee = inlining.func, 464 .call_src = inlining.call_src, 465 .parent = if (inlining.call_block.inlining) |parent_inlining| p: { 466 break :p (try parent_inlining.refFrame(zcu)).toOptional(); 467 } else .none, 468 })).toOptional(); 469 } 470 return inlining.ref_frame.unwrap().?; 471 } 472 }; 473 474 pub const Merges = struct { 475 block_inst: Air.Inst.Index, 476 /// Separate array list from break_inst_list so that it can be passed directly 477 /// to resolvePeerTypes. 478 results: std.ArrayListUnmanaged(Air.Inst.Ref), 479 /// Keeps track of the break instructions so that the operand can be replaced 480 /// if we need to add type coercion at the end of block analysis. 481 /// Same indexes, capacity, length as `results`. 482 br_list: std.ArrayListUnmanaged(Air.Inst.Index), 483 /// Keeps the source location of the rhs operand of the break instruction, 484 /// to enable more precise compile errors. 485 /// Same indexes, capacity, length as `results`. 486 src_locs: std.ArrayListUnmanaged(?LazySrcLoc), 487 /// Most blocks do not utilize this field. When it is used, its use is 488 /// contextual. The possible uses are as follows: 489 /// * for a `switch_block[_ref]`, this refers to dummy `br` instructions 490 /// which correspond to `switch_continue` ZIR. The switch logic will 491 /// rewrite these to appropriate AIR switch dispatches. 492 extra_insts: std.ArrayListUnmanaged(Air.Inst.Index) = .empty, 493 /// Same indexes, capacity, length as `extra_insts`. 494 extra_src_locs: std.ArrayListUnmanaged(LazySrcLoc) = .empty, 495 496 pub fn deinit(merges: *@This(), allocator: Allocator) void { 497 merges.results.deinit(allocator); 498 merges.br_list.deinit(allocator); 499 merges.src_locs.deinit(allocator); 500 merges.extra_insts.deinit(allocator); 501 merges.extra_src_locs.deinit(allocator); 502 } 503 }; 504 505 pub fn makeSubBlock(parent: *Block) Block { 506 return .{ 507 .parent = parent, 508 .sema = parent.sema, 509 .namespace = parent.namespace, 510 .instructions = .{}, 511 .label = null, 512 .inlining = parent.inlining, 513 .comptime_reason = parent.comptime_reason, 514 .is_typeof = parent.is_typeof, 515 .runtime_cond = parent.runtime_cond, 516 .runtime_loop = parent.runtime_loop, 517 .runtime_index = parent.runtime_index, 518 .want_safety = parent.want_safety, 519 .float_mode = parent.float_mode, 520 .c_import_buf = parent.c_import_buf, 521 .error_return_trace_index = parent.error_return_trace_index, 522 .need_debug_scope = parent.need_debug_scope, 523 .src_base_inst = parent.src_base_inst, 524 .type_name_ctx = parent.type_name_ctx, 525 }; 526 } 527 528 fn wantSafeTypes(block: *const Block) bool { 529 return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) { 530 .Debug => true, 531 .ReleaseSafe => true, 532 .ReleaseFast => false, 533 .ReleaseSmall => false, 534 }; 535 } 536 537 fn wantSafety(block: *const Block) bool { 538 if (block.isComptime()) return false; // runtime safety checks are pointless in comptime blocks 539 return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) { 540 .Debug => true, 541 .ReleaseSafe => true, 542 .ReleaseFast => false, 543 .ReleaseSmall => false, 544 }; 545 } 546 547 pub fn getFileScope(block: *Block, zcu: *Zcu) *Zcu.File { 548 return zcu.fileByIndex(getFileScopeIndex(block, zcu)); 549 } 550 551 pub fn getFileScopeIndex(block: *Block, zcu: *Zcu) Zcu.File.Index { 552 return zcu.namespacePtr(block.namespace).file_scope; 553 } 554 555 fn addTy( 556 block: *Block, 557 tag: Air.Inst.Tag, 558 ty: Type, 559 ) error{OutOfMemory}!Air.Inst.Ref { 560 return block.addInst(.{ 561 .tag = tag, 562 .data = .{ .ty = ty }, 563 }); 564 } 565 566 fn addTyOp( 567 block: *Block, 568 tag: Air.Inst.Tag, 569 ty: Type, 570 operand: Air.Inst.Ref, 571 ) error{OutOfMemory}!Air.Inst.Ref { 572 return block.addInst(.{ 573 .tag = tag, 574 .data = .{ .ty_op = .{ 575 .ty = Air.internedToRef(ty.toIntern()), 576 .operand = operand, 577 } }, 578 }); 579 } 580 581 fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref { 582 return block.addInst(.{ 583 .tag = .bitcast, 584 .data = .{ .ty_op = .{ 585 .ty = Air.internedToRef(ty.toIntern()), 586 .operand = operand, 587 } }, 588 }); 589 } 590 591 fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref { 592 return block.addInst(.{ 593 .tag = tag, 594 .data = .{ .no_op = {} }, 595 }); 596 } 597 598 fn addUnOp( 599 block: *Block, 600 tag: Air.Inst.Tag, 601 operand: Air.Inst.Ref, 602 ) error{OutOfMemory}!Air.Inst.Ref { 603 return block.addInst(.{ 604 .tag = tag, 605 .data = .{ .un_op = operand }, 606 }); 607 } 608 609 fn addBr( 610 block: *Block, 611 target_block: Air.Inst.Index, 612 operand: Air.Inst.Ref, 613 ) error{OutOfMemory}!Air.Inst.Ref { 614 return block.addInst(.{ 615 .tag = .br, 616 .data = .{ .br = .{ 617 .block_inst = target_block, 618 .operand = operand, 619 } }, 620 }); 621 } 622 623 fn addBinOp( 624 block: *Block, 625 tag: Air.Inst.Tag, 626 lhs: Air.Inst.Ref, 627 rhs: Air.Inst.Ref, 628 ) error{OutOfMemory}!Air.Inst.Ref { 629 return block.addInst(.{ 630 .tag = tag, 631 .data = .{ .bin_op = .{ 632 .lhs = lhs, 633 .rhs = rhs, 634 } }, 635 }); 636 } 637 638 fn addStructFieldPtr( 639 block: *Block, 640 struct_ptr: Air.Inst.Ref, 641 field_index: u32, 642 ptr_field_ty: Type, 643 ) !Air.Inst.Ref { 644 const ty = Air.internedToRef(ptr_field_ty.toIntern()); 645 const tag: Air.Inst.Tag = switch (field_index) { 646 0 => .struct_field_ptr_index_0, 647 1 => .struct_field_ptr_index_1, 648 2 => .struct_field_ptr_index_2, 649 3 => .struct_field_ptr_index_3, 650 else => { 651 return block.addInst(.{ 652 .tag = .struct_field_ptr, 653 .data = .{ .ty_pl = .{ 654 .ty = ty, 655 .payload = try block.sema.addExtra(Air.StructField{ 656 .struct_operand = struct_ptr, 657 .field_index = field_index, 658 }), 659 } }, 660 }); 661 }, 662 }; 663 return block.addInst(.{ 664 .tag = tag, 665 .data = .{ .ty_op = .{ 666 .ty = ty, 667 .operand = struct_ptr, 668 } }, 669 }); 670 } 671 672 fn addStructFieldVal( 673 block: *Block, 674 struct_val: Air.Inst.Ref, 675 field_index: u32, 676 field_ty: Type, 677 ) !Air.Inst.Ref { 678 return block.addInst(.{ 679 .tag = .struct_field_val, 680 .data = .{ .ty_pl = .{ 681 .ty = Air.internedToRef(field_ty.toIntern()), 682 .payload = try block.sema.addExtra(Air.StructField{ 683 .struct_operand = struct_val, 684 .field_index = field_index, 685 }), 686 } }, 687 }); 688 } 689 690 fn addSliceElemPtr( 691 block: *Block, 692 slice: Air.Inst.Ref, 693 elem_index: Air.Inst.Ref, 694 elem_ptr_ty: Type, 695 ) !Air.Inst.Ref { 696 return block.addInst(.{ 697 .tag = .slice_elem_ptr, 698 .data = .{ .ty_pl = .{ 699 .ty = Air.internedToRef(elem_ptr_ty.toIntern()), 700 .payload = try block.sema.addExtra(Air.Bin{ 701 .lhs = slice, 702 .rhs = elem_index, 703 }), 704 } }, 705 }); 706 } 707 708 fn addPtrElemPtr( 709 block: *Block, 710 array_ptr: Air.Inst.Ref, 711 elem_index: Air.Inst.Ref, 712 elem_ptr_ty: Type, 713 ) !Air.Inst.Ref { 714 const ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 715 return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref); 716 } 717 718 fn addPtrElemPtrTypeRef( 719 block: *Block, 720 array_ptr: Air.Inst.Ref, 721 elem_index: Air.Inst.Ref, 722 elem_ptr_ty: Air.Inst.Ref, 723 ) !Air.Inst.Ref { 724 return block.addInst(.{ 725 .tag = .ptr_elem_ptr, 726 .data = .{ .ty_pl = .{ 727 .ty = elem_ptr_ty, 728 .payload = try block.sema.addExtra(Air.Bin{ 729 .lhs = array_ptr, 730 .rhs = elem_index, 731 }), 732 } }, 733 }); 734 } 735 736 fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref { 737 const sema = block.sema; 738 const pt = sema.pt; 739 const zcu = pt.zcu; 740 return block.addInst(.{ 741 .tag = if (block.float_mode == .optimized) .cmp_vector_optimized else .cmp_vector, 742 .data = .{ .ty_pl = .{ 743 .ty = Air.internedToRef((try pt.vectorType(.{ 744 .len = sema.typeOf(lhs).vectorLen(zcu), 745 .child = .bool_type, 746 })).toIntern()), 747 .payload = try sema.addExtra(Air.VectorCmp{ 748 .lhs = lhs, 749 .rhs = rhs, 750 .op = Air.VectorCmp.encodeOp(cmp_op), 751 }), 752 } }, 753 }); 754 } 755 756 fn addReduce(block: *Block, operand: Air.Inst.Ref, operation: std.builtin.ReduceOp) !Air.Inst.Ref { 757 const sema = block.sema; 758 const zcu = sema.pt.zcu; 759 const allow_optimized = switch (sema.typeOf(operand).childType(zcu).zigTypeTag(zcu)) { 760 .float => true, 761 .bool, .int => false, 762 else => unreachable, 763 }; 764 return block.addInst(.{ 765 .tag = if (allow_optimized and block.float_mode == .optimized) .reduce_optimized else .reduce, 766 .data = .{ .reduce = .{ 767 .operand = operand, 768 .operation = operation, 769 } }, 770 }); 771 } 772 773 fn addAggregateInit( 774 block: *Block, 775 aggregate_ty: Type, 776 elements: []const Air.Inst.Ref, 777 ) !Air.Inst.Ref { 778 const sema = block.sema; 779 const ty_ref = Air.internedToRef(aggregate_ty.toIntern()); 780 try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len); 781 const extra_index: u32 = @intCast(sema.air_extra.items.len); 782 sema.appendRefsAssumeCapacity(elements); 783 784 return block.addInst(.{ 785 .tag = .aggregate_init, 786 .data = .{ .ty_pl = .{ 787 .ty = ty_ref, 788 .payload = extra_index, 789 } }, 790 }); 791 } 792 793 fn addUnionInit( 794 block: *Block, 795 union_ty: Type, 796 field_index: u32, 797 init: Air.Inst.Ref, 798 ) !Air.Inst.Ref { 799 return block.addInst(.{ 800 .tag = .union_init, 801 .data = .{ .ty_pl = .{ 802 .ty = Air.internedToRef(union_ty.toIntern()), 803 .payload = try block.sema.addExtra(Air.UnionInit{ 804 .field_index = field_index, 805 .init = init, 806 }), 807 } }, 808 }); 809 } 810 811 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { 812 return (try block.addInstAsIndex(inst)).toRef(); 813 } 814 815 pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { 816 const sema = block.sema; 817 const gpa = sema.gpa; 818 819 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 820 try block.instructions.ensureUnusedCapacity(gpa, 1); 821 822 const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 823 sema.air_instructions.appendAssumeCapacity(inst); 824 block.instructions.appendAssumeCapacity(result_index); 825 return result_index; 826 } 827 828 /// Insert an instruction into the block at `index`. Moves all following 829 /// instructions forward in the block to make room. Operation is O(N). 830 pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { 831 return (try block.insertInstAsIndex(index, inst)).toRef(); 832 } 833 834 pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { 835 const sema = block.sema; 836 const gpa = sema.gpa; 837 838 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 839 840 const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 841 sema.air_instructions.appendAssumeCapacity(inst); 842 843 try block.instructions.insert(gpa, @intFromEnum(index), result_index); 844 return result_index; 845 } 846 847 pub fn ownerModule(block: Block) *Package.Module { 848 const zcu = block.sema.pt.zcu; 849 return zcu.namespacePtr(block.namespace).fileScope(zcu).mod.?; 850 } 851 852 fn trackZir(block: *Block, inst: Zir.Inst.Index) Allocator.Error!InternPool.TrackedInst.Index { 853 const pt = block.sema.pt; 854 block.sema.code.assertTrackable(inst); 855 return pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{ 856 .file = block.getFileScopeIndex(pt.zcu), 857 .inst = inst, 858 }); 859 } 860 861 /// Returns the `*Block` that should be passed to `Sema.failWithOwnedErrorMsg`, because all inline 862 /// calls below it have already been reported with "called at comptime from here" notes. 863 fn explainWhyBlockIsComptime(start_block: *Block, err_msg: *Zcu.ErrorMsg) !*Block { 864 const sema = start_block.sema; 865 var block = start_block; 866 while (true) { 867 switch (block.comptime_reason.?) { 868 .inlining_parent => { 869 const inlining = block.inlining.?; 870 try sema.errNote(inlining.call_src, err_msg, "called at comptime from here", .{}); 871 block = inlining.call_block; 872 }, 873 .reason => |r| { 874 try r.r.explain(sema, r.src, err_msg); 875 return block; 876 }, 877 } 878 } 879 } 880 }; 881 882 /// Represents the reason we are resolving a value or evaluating code at comptime. 883 /// Most reasons are represented by a `std.zig.SimpleComptimeReason`, which provides a plain message. 884 const ComptimeReason = union(enum) { 885 /// Evaluating at comptime for a reason in the `std.zig.SimpleComptimeReason` enum. 886 simple: std.zig.SimpleComptimeReason, 887 888 /// Evaluating at comptime because of a comptime-only type. This field is separate so that 889 /// the type in question can be included in the error message. AstGen could never emit this 890 /// reason, because it knows nothing of types. 891 /// The format string looks like "foo '{f}' bar", where "{f}" is the comptime-only type. 892 /// We will then explain why this type is comptime-only. 893 comptime_only: struct { 894 ty: Type, 895 msg: enum { 896 union_init, 897 struct_init, 898 tuple_init, 899 }, 900 }, 901 902 /// Like `comptime_only`, but for a parameter type. 903 /// Includes a "parameter type declared here" note. 904 comptime_only_param_ty: struct { 905 ty: Type, 906 param_ty_src: LazySrcLoc, 907 }, 908 909 /// Like `comptime_only`, but for a return type. 910 /// Includes a "return type declared here" note. 911 comptime_only_ret_ty: struct { 912 ty: Type, 913 is_generic_inst: bool, 914 ret_ty_src: LazySrcLoc, 915 }, 916 917 /// Evaluating at comptime because we're evaluating an argument to a parameter marked `comptime`. 918 comptime_param: struct { 919 comptime_src: LazySrcLoc, 920 }, 921 922 fn explain(reason: ComptimeReason, sema: *Sema, src: LazySrcLoc, err_msg: *Zcu.ErrorMsg) !void { 923 switch (reason) { 924 .simple => |simple| { 925 try sema.errNote(src, err_msg, "{s}", .{simple.message()}); 926 }, 927 .comptime_only => |co| { 928 const pre, const post = switch (co.msg) { 929 .union_init => .{ "initializer of comptime-only union", "must be comptime-known" }, 930 .struct_init => .{ "initializer of comptime-only struct", "must be comptime-known" }, 931 .tuple_init => .{ "initializer of comptime-only tuple", "must be comptime-known" }, 932 }; 933 try sema.errNote(src, err_msg, "{s} '{f}' {s}", .{ pre, co.ty.fmt(sema.pt), post }); 934 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); 935 }, 936 .comptime_only_param_ty => |co| { 937 try sema.errNote(src, err_msg, "argument to parameter with comptime-only type '{f}' must be comptime-known", .{co.ty.fmt(sema.pt)}); 938 try sema.errNote(co.param_ty_src, err_msg, "parameter type declared here", .{}); 939 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); 940 }, 941 .comptime_only_ret_ty => |co| { 942 const function_with: []const u8 = if (co.is_generic_inst) "generic function instantiated with" else "function with"; 943 try sema.errNote(src, err_msg, "call to {s} comptime-only return type '{f}' is evaluated at comptime", .{ function_with, co.ty.fmt(sema.pt) }); 944 try sema.errNote(co.ret_ty_src, err_msg, "return type declared here", .{}); 945 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); 946 }, 947 .comptime_param => |cp| { 948 try sema.errNote(src, err_msg, "argument to comptime parameter must be comptime-known", .{}); 949 try sema.errNote(cp.comptime_src, err_msg, "parameter declared comptime here", .{}); 950 }, 951 } 952 } 953 }; 954 955 /// Represents the reason a `Block` is being evaluated at comptime. 956 const BlockComptimeReason = union(enum) { 957 /// This block inherits being comptime-only from the `inlining` call site. 958 inlining_parent, 959 960 /// Comptime evaluation began somewhere in the current function for a given `ComptimeReason`. 961 reason: struct { 962 /// The source location which this reason originates from. `r` is reported here. 963 src: LazySrcLoc, 964 r: ComptimeReason, 965 }, 966 }; 967 968 const LabeledBlock = struct { 969 block: Block, 970 label: Block.Label, 971 972 fn destroy(lb: *LabeledBlock, gpa: Allocator) void { 973 lb.block.instructions.deinit(gpa); 974 lb.label.merges.deinit(gpa); 975 gpa.destroy(lb); 976 } 977 }; 978 979 /// The value stored in the inferred allocation. This will go into 980 /// peer type resolution. This is stored in a separate list so that 981 /// the items are contiguous in memory and thus can be passed to 982 /// `Zcu.resolvePeerTypes`. 983 const InferredAlloc = struct { 984 /// The placeholder `store` instructions used before the result pointer type 985 /// is known. These should be rewritten to perform any required coercions 986 /// when the type is resolved. 987 /// Allocated from `sema.arena`. 988 prongs: std.ArrayListUnmanaged(Air.Inst.Index) = .empty, 989 }; 990 991 pub fn deinit(sema: *Sema) void { 992 const gpa = sema.gpa; 993 sema.air_instructions.deinit(gpa); 994 sema.air_extra.deinit(gpa); 995 sema.inst_map.deinit(gpa); 996 { 997 var it = sema.post_hoc_blocks.iterator(); 998 while (it.next()) |entry| { 999 const labeled_block = entry.value_ptr.*; 1000 labeled_block.destroy(gpa); 1001 } 1002 sema.post_hoc_blocks.deinit(gpa); 1003 } 1004 sema.unresolved_inferred_allocs.deinit(gpa); 1005 sema.base_allocs.deinit(gpa); 1006 sema.maybe_comptime_allocs.deinit(gpa); 1007 sema.comptime_allocs.deinit(gpa); 1008 sema.exports.deinit(gpa); 1009 sema.references.deinit(gpa); 1010 sema.type_references.deinit(gpa); 1011 sema.dependencies.deinit(gpa); 1012 sema.* = undefined; 1013 } 1014 1015 /// Performs semantic analysis of a ZIR body which is behind a runtime condition. If comptime 1016 /// control flow happens here, Sema will convert it to runtime control flow by introducing post-hoc 1017 /// blocks where necessary. 1018 /// Returns the branch hint for this branch. 1019 fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !std.builtin.BranchHint { 1020 const parent_hint = sema.branch_hint; 1021 defer sema.branch_hint = parent_hint; 1022 sema.branch_hint = null; 1023 1024 sema.analyzeBodyInner(block, body) catch |err| switch (err) { 1025 error.ComptimeBreak => { 1026 const zir_datas = sema.code.instructions.items(.data); 1027 const break_data = zir_datas[@intFromEnum(sema.comptime_break_inst)].@"break"; 1028 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 1029 try sema.addRuntimeBreak(block, extra.block_inst, break_data.operand); 1030 }, 1031 else => |e| return e, 1032 }; 1033 1034 return sema.branch_hint orelse .none; 1035 } 1036 1037 /// Semantically analyze a ZIR function body. It is guranteed by AstGen that such a body cannot 1038 /// trigger comptime control flow to move above the function body. 1039 pub fn analyzeFnBody( 1040 sema: *Sema, 1041 block: *Block, 1042 body: []const Zir.Inst.Index, 1043 ) !void { 1044 sema.analyzeBodyInner(block, body) catch |err| switch (err) { 1045 error.ComptimeBreak => unreachable, // unexpected comptime control flow 1046 else => |e| return e, 1047 }; 1048 } 1049 1050 /// Given a ZIR body which can be exited via a `break_inline` instruction, or a non-inline body which 1051 /// we are evaluating at comptime, semantically analyze the body and return the result from it. 1052 /// Returns `null` if control flow did not break from this block, but instead terminated with some 1053 /// other runtime noreturn instruction. Compile-time breaks to blocks further up the stack still 1054 /// return `error.ComptimeBreak`. If `block.isComptime()`, this function will never return `null`. 1055 fn analyzeInlineBody( 1056 sema: *Sema, 1057 block: *Block, 1058 body: []const Zir.Inst.Index, 1059 /// The index which a break instruction can target to break from this body. 1060 break_target: Zir.Inst.Index, 1061 ) CompileError!?Air.Inst.Ref { 1062 if (sema.analyzeBodyInner(block, body)) |_| { 1063 return null; 1064 } else |err| switch (err) { 1065 error.ComptimeBreak => {}, 1066 else => |e| return e, 1067 } 1068 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst)); 1069 switch (break_inst.tag) { 1070 .switch_continue => { 1071 // This is handled by separate logic. 1072 return error.ComptimeBreak; 1073 }, 1074 .break_inline, .@"break" => {}, 1075 else => unreachable, 1076 } 1077 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data; 1078 if (extra.block_inst != break_target) { 1079 // This control flow goes further up the stack. 1080 return error.ComptimeBreak; 1081 } 1082 return try sema.resolveInst(break_inst.data.@"break".operand); 1083 } 1084 1085 /// Like `analyzeInlineBody`, but if the body does not break with a value, returns 1086 /// `.unreachable_value` instead of `null`. Notably, use this to evaluate an arbitrary 1087 /// body at comptime to a single result value. 1088 pub fn resolveInlineBody( 1089 sema: *Sema, 1090 block: *Block, 1091 body: []const Zir.Inst.Index, 1092 /// The index which a break instruction can target to break from this body. 1093 break_target: Zir.Inst.Index, 1094 ) CompileError!Air.Inst.Ref { 1095 return (try sema.analyzeInlineBody(block, body, break_target)) orelse .unreachable_value; 1096 } 1097 1098 /// This function is the main loop of `Sema`. It analyzes a single body of ZIR instructions. 1099 /// 1100 /// If this function returns normally, the merges of `block` were populated with all possible 1101 /// (runtime) results of this block. Peer type resolution should be performed on the result, 1102 /// and relevant runtime instructions written to perform necessary coercions and breaks. See 1103 /// `resolveAnalyzedBlock`. This form of return is impossible if `block.isComptime()`. 1104 /// 1105 /// Alternatively, this function may return `error.ComptimeBreak`. This indicates that comptime 1106 /// control flow is happening, and we are breaking at comptime from a block indicated by the 1107 /// break instruction in `sema.comptime_break_inst`. This occurs for any `break_inline`, or for a 1108 /// standard `break` at comptime. This error is pushed up the stack until the target block is 1109 /// reached, at which point the break operand will be fetched. 1110 /// 1111 /// It is rare to call this function directly. Usually, you want one of the following wrappers: 1112 /// * If the body is exited via a `break_inline`, or is being evaluated at comptime, 1113 /// use `Sema.analyzeInlineBody` or `Sema.resolveInlineBody`. 1114 /// * If the body is behind a fresh runtime condition, use `Sema.analyzeBodyRuntimeBreak`. 1115 /// * If the body is an entire function body, use `Sema.analyzeFnBody`. 1116 /// * If the body is to be generated into an AIR `block`, use `Sema.resolveBlockBody`. 1117 /// * Otherwise, direct usage of `Sema.analyzeBodyInner` may be necessary. 1118 fn analyzeBodyInner( 1119 sema: *Sema, 1120 block: *Block, 1121 body: []const Zir.Inst.Index, 1122 ) CompileError!void { 1123 // No tracy calls here, to avoid interfering with the tail call mechanism. 1124 1125 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); 1126 1127 const pt = sema.pt; 1128 const zcu = pt.zcu; 1129 const map = &sema.inst_map; 1130 const tags = sema.code.instructions.items(.tag); 1131 const datas = sema.code.instructions.items(.data); 1132 1133 var crash_info = crash_report.prepAnalyzeBody(sema, block, body); 1134 crash_info.push(); 1135 defer crash_info.pop(); 1136 1137 // We use a while (true) loop here to avoid a redundant way of breaking out of 1138 // the loop. The only way to break out of the loop is with a `noreturn` 1139 // instruction. 1140 var i: u32 = 0; 1141 while (true) { 1142 crash_info.setBodyIndex(i); 1143 const inst = body[i]; 1144 1145 // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away. 1146 if (build_options.enable_logging) { 1147 std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path: { 1148 const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool); 1149 const file = zcu.fileByIndex(file_index); 1150 break :path file.path.fmt(zcu.comp); 1151 }, inst }); 1152 } 1153 1154 const air_inst: Air.Inst.Ref = inst: switch (tags[@intFromEnum(inst)]) { 1155 // zig fmt: off 1156 .alloc => try sema.zirAlloc(block, inst), 1157 .alloc_inferred => try sema.zirAllocInferred(block, true), 1158 .alloc_inferred_mut => try sema.zirAllocInferred(block, false), 1159 .alloc_inferred_comptime => try sema.zirAllocInferredComptime(true), 1160 .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(false), 1161 .resolve_inferred_alloc => try sema.zirResolveInferredAlloc(block, inst), 1162 .alloc_mut => try sema.zirAllocMut(block, inst), 1163 .alloc_comptime_mut => try sema.zirAllocComptime(block, inst), 1164 .make_ptr_const => try sema.zirMakePtrConst(block, inst), 1165 .anyframe_type => try sema.zirAnyframeType(block, inst), 1166 .array_cat => try sema.zirArrayCat(block, inst), 1167 .array_mul => try sema.zirArrayMul(block, inst), 1168 .array_type => try sema.zirArrayType(block, inst), 1169 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), 1170 .vector_type => try sema.zirVectorType(block, inst), 1171 .as_node => try sema.zirAsNode(block, inst), 1172 .as_shift_operand => try sema.zirAsShiftOperand(block, inst), 1173 .bit_and => try sema.zirBitwise(block, inst, .bit_and), 1174 .bit_not => try sema.zirBitNot(block, inst), 1175 .bit_or => try sema.zirBitwise(block, inst, .bit_or), 1176 .bitcast => try sema.zirBitcast(block, inst), 1177 .suspend_block => try sema.zirSuspendBlock(block, inst), 1178 .bool_not => try sema.zirBoolNot(block, inst), 1179 .bool_br_and => try sema.zirBoolBr(block, inst, false), 1180 .bool_br_or => try sema.zirBoolBr(block, inst, true), 1181 .c_import => try sema.zirCImport(block, inst), 1182 .call => try sema.zirCall(block, inst, .direct), 1183 .field_call => try sema.zirCall(block, inst, .field), 1184 .cmp_lt => try sema.zirCmp(block, inst, .lt), 1185 .cmp_lte => try sema.zirCmp(block, inst, .lte), 1186 .cmp_eq => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .optimized)), 1187 .cmp_gte => try sema.zirCmp(block, inst, .gte), 1188 .cmp_gt => try sema.zirCmp(block, inst, .gt), 1189 .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .optimized)), 1190 .decl_ref => try sema.zirDeclRef(block, inst), 1191 .decl_val => try sema.zirDeclVal(block, inst), 1192 .load => try sema.zirLoad(block, inst), 1193 .elem_ptr => try sema.zirElemPtr(block, inst), 1194 .elem_ptr_node => try sema.zirElemPtrNode(block, inst), 1195 .elem_val => try sema.zirElemVal(block, inst), 1196 .elem_val_node => try sema.zirElemValNode(block, inst), 1197 .elem_val_imm => try sema.zirElemValImm(block, inst), 1198 .elem_type => try sema.zirElemType(block, inst), 1199 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), 1200 .vec_arr_elem_type => try sema.zirVecArrElemType(block, inst), 1201 .enum_literal => try sema.zirEnumLiteral(block, inst), 1202 .decl_literal => try sema.zirDeclLiteral(block, inst, true), 1203 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false), 1204 .int_from_enum => try sema.zirIntFromEnum(block, inst), 1205 .enum_from_int => try sema.zirEnumFromInt(block, inst), 1206 .err_union_code => try sema.zirErrUnionCode(block, inst), 1207 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), 1208 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst), 1209 .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst), 1210 .error_union_type => try sema.zirErrorUnionType(block, inst), 1211 .error_value => try sema.zirErrorValue(block, inst), 1212 .field_ptr => try sema.zirFieldPtr(block, inst), 1213 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), 1214 .field_val => try sema.zirFieldVal(block, inst), 1215 .field_val_named => try sema.zirFieldValNamed(block, inst), 1216 .func => try sema.zirFunc(block, inst, false), 1217 .func_inferred => try sema.zirFunc(block, inst, true), 1218 .func_fancy => try sema.zirFuncFancy(block, inst), 1219 .import => try sema.zirImport(block, inst), 1220 .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), 1221 .int => try sema.zirInt(block, inst), 1222 .int_big => try sema.zirIntBig(block, inst), 1223 .float => try sema.zirFloat(block, inst), 1224 .float128 => try sema.zirFloat128(block, inst), 1225 .int_type => try sema.zirIntType(inst), 1226 .is_non_err => try sema.zirIsNonErr(block, inst), 1227 .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), 1228 .ret_is_non_err => try sema.zirRetIsNonErr(block, inst), 1229 .is_non_null => try sema.zirIsNonNull(block, inst), 1230 .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), 1231 .merge_error_sets => try sema.zirMergeErrorSets(block, inst), 1232 .negate => try sema.zirNegate(block, inst), 1233 .negate_wrap => try sema.zirNegateWrap(block, inst), 1234 .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), 1235 .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), 1236 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), 1237 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), 1238 .optional_type => try sema.zirOptionalType(block, inst), 1239 .ptr_type => try sema.zirPtrType(block, inst), 1240 .ref => try sema.zirRef(block, inst), 1241 .ret_err_value_code => try sema.zirRetErrValueCode(inst), 1242 .shr => try sema.zirShr(block, inst, .shr), 1243 .shr_exact => try sema.zirShr(block, inst, .shr_exact), 1244 .slice_end => try sema.zirSliceEnd(block, inst), 1245 .slice_sentinel => try sema.zirSliceSentinel(block, inst), 1246 .slice_start => try sema.zirSliceStart(block, inst), 1247 .slice_length => try sema.zirSliceLength(block, inst), 1248 .slice_sentinel_ty => try sema.zirSliceSentinelTy(block, inst), 1249 .str => try sema.zirStr(inst), 1250 .switch_block => try sema.zirSwitchBlock(block, inst, false), 1251 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), 1252 .switch_block_err_union => try sema.zirSwitchBlockErrUnion(block, inst), 1253 .type_info => try sema.zirTypeInfo(block, inst), 1254 .size_of => try sema.zirSizeOf(block, inst), 1255 .bit_size_of => try sema.zirBitSizeOf(block, inst), 1256 .typeof => try sema.zirTypeof(block, inst), 1257 .typeof_builtin => try sema.zirTypeofBuiltin(block, inst), 1258 .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), 1259 .xor => try sema.zirBitwise(block, inst, .xor), 1260 .struct_init_empty => try sema.zirStructInitEmpty(block, inst), 1261 .struct_init_empty_result => try sema.zirStructInitEmptyResult(block, inst, false), 1262 .struct_init_empty_ref_result => try sema.zirStructInitEmptyResult(block, inst, true), 1263 .struct_init_anon => try sema.zirStructInitAnon(block, inst), 1264 .struct_init => try sema.zirStructInit(block, inst, false), 1265 .struct_init_ref => try sema.zirStructInit(block, inst, true), 1266 .struct_init_field_type => try sema.zirStructInitFieldType(block, inst), 1267 .struct_init_field_ptr => try sema.zirStructInitFieldPtr(block, inst), 1268 .array_init_anon => try sema.zirArrayInitAnon(block, inst), 1269 .array_init => try sema.zirArrayInit(block, inst, false), 1270 .array_init_ref => try sema.zirArrayInit(block, inst, true), 1271 .array_init_elem_type => try sema.zirArrayInitElemType(block, inst), 1272 .array_init_elem_ptr => try sema.zirArrayInitElemPtr(block, inst), 1273 .union_init => try sema.zirUnionInit(block, inst), 1274 .field_type_ref => try sema.zirFieldTypeRef(block, inst), 1275 .int_from_ptr => try sema.zirIntFromPtr(block, inst), 1276 .align_of => try sema.zirAlignOf(block, inst), 1277 .int_from_bool => try sema.zirIntFromBool(block, inst), 1278 .embed_file => try sema.zirEmbedFile(block, inst), 1279 .error_name => try sema.zirErrorName(block, inst), 1280 .tag_name => try sema.zirTagName(block, inst), 1281 .type_name => try sema.zirTypeName(block, inst), 1282 .frame_type => try sema.zirFrameType(block, inst), 1283 .int_from_float => try sema.zirIntFromFloat(block, inst), 1284 .float_from_int => try sema.zirFloatFromInt(block, inst), 1285 .ptr_from_int => try sema.zirPtrFromInt(block, inst), 1286 .float_cast => try sema.zirFloatCast(block, inst), 1287 .int_cast => try sema.zirIntCast(block, inst), 1288 .ptr_cast => try sema.zirPtrCast(block, inst), 1289 .truncate => try sema.zirTruncate(block, inst), 1290 .has_decl => try sema.zirHasDecl(block, inst), 1291 .has_field => try sema.zirHasField(block, inst), 1292 .byte_swap => try sema.zirByteSwap(block, inst), 1293 .bit_reverse => try sema.zirBitReverse(block, inst), 1294 .bit_offset_of => try sema.zirBitOffsetOf(block, inst), 1295 .offset_of => try sema.zirOffsetOf(block, inst), 1296 .splat => try sema.zirSplat(block, inst), 1297 .reduce => try sema.zirReduce(block, inst), 1298 .shuffle => try sema.zirShuffle(block, inst), 1299 .atomic_load => try sema.zirAtomicLoad(block, inst), 1300 .atomic_rmw => try sema.zirAtomicRmw(block, inst), 1301 .mul_add => try sema.zirMulAdd(block, inst), 1302 .builtin_call => try sema.zirBuiltinCall(block, inst), 1303 .@"resume" => try sema.zirResume(block, inst), 1304 .for_len => try sema.zirForLen(block, inst), 1305 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), 1306 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), 1307 .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst), 1308 1309 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), 1310 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), 1311 .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount), 1312 .abs => try sema.zirAbs(block, inst), 1313 1314 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), 1315 .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), 1316 .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos), 1317 .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan), 1318 .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp), 1319 .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2), 1320 .log => try sema.zirUnaryMath(block, inst, .log, Value.log), 1321 .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2), 1322 .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10), 1323 .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor), 1324 .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil), 1325 .round => try sema.zirUnaryMath(block, inst, .round, Value.round), 1326 .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc), 1327 1328 .error_set_decl => try sema.zirErrorSetDecl(inst), 1329 1330 .add => try sema.zirArithmetic(block, inst, .add, true), 1331 .addwrap => try sema.zirArithmetic(block, inst, .addwrap, true), 1332 .add_sat => try sema.zirArithmetic(block, inst, .add_sat, true), 1333 .add_unsafe => try sema.zirArithmetic(block, inst, .add_unsafe, false), 1334 .mul => try sema.zirArithmetic(block, inst, .mul, true), 1335 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap, true), 1336 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat, true), 1337 .sub => try sema.zirArithmetic(block, inst, .sub, true), 1338 .subwrap => try sema.zirArithmetic(block, inst, .subwrap, true), 1339 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat, true), 1340 1341 .div => try sema.zirDiv(block, inst), 1342 .div_exact => try sema.zirDivExact(block, inst), 1343 .div_floor => try sema.zirDivFloor(block, inst), 1344 .div_trunc => try sema.zirDivTrunc(block, inst), 1345 1346 .mod_rem => try sema.zirModRem(block, inst), 1347 .mod => try sema.zirMod(block, inst), 1348 .rem => try sema.zirRem(block, inst), 1349 1350 .max => try sema.zirMinMax(block, inst, .max), 1351 .min => try sema.zirMinMax(block, inst, .min), 1352 1353 .shl => try sema.zirShl(block, inst, .shl), 1354 .shl_exact => try sema.zirShl(block, inst, .shl_exact), 1355 .shl_sat => try sema.zirShl(block, inst, .shl_sat), 1356 1357 .ret_ptr => try sema.zirRetPtr(block, inst), 1358 .ret_type => Air.internedToRef(sema.fn_ret_ty.toIntern()), 1359 1360 // Instructions that we know to *always* be noreturn based solely on their tag. 1361 // These functions match the return type of analyzeBody so that we can 1362 // tail call them here. 1363 .compile_error => break try sema.zirCompileError(block, inst), 1364 .ret_implicit => break try sema.zirRetImplicit(block, inst), 1365 .ret_node => break try sema.zirRetNode(block, inst), 1366 .ret_load => break try sema.zirRetLoad(block, inst), 1367 .ret_err_value => break try sema.zirRetErrValue(block, inst), 1368 .@"unreachable" => break try sema.zirUnreachable(block, inst), 1369 .panic => break try sema.zirPanic(block, inst), 1370 .trap => break try sema.zirTrap(block, inst), 1371 // zig fmt: on 1372 1373 // This instruction never exists in an analyzed body. It exists only in the declaration 1374 // list for a container type. 1375 .declaration => unreachable, 1376 1377 .extended => ext: { 1378 const extended = datas[@intFromEnum(inst)].extended; 1379 break :ext switch (extended.opcode) { 1380 // zig fmt: off 1381 .struct_decl => try sema.zirStructDecl( block, extended, inst), 1382 .enum_decl => try sema.zirEnumDecl( block, extended, inst), 1383 .union_decl => try sema.zirUnionDecl( block, extended, inst), 1384 .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst), 1385 .tuple_decl => try sema.zirTupleDecl( block, extended), 1386 .this => try sema.zirThis( block, extended), 1387 .ret_addr => try sema.zirRetAddr( block, extended), 1388 .builtin_src => try sema.zirBuiltinSrc( block, extended), 1389 .error_return_trace => try sema.zirErrorReturnTrace( block), 1390 .frame => try sema.zirFrame( block, extended), 1391 .frame_address => try sema.zirFrameAddress( block, extended), 1392 .alloc => try sema.zirAllocExtended( block, extended), 1393 .builtin_extern => try sema.zirBuiltinExtern( block, extended), 1394 .@"asm" => try sema.zirAsm( block, extended, false), 1395 .asm_expr => try sema.zirAsm( block, extended, true), 1396 .typeof_peer => try sema.zirTypeofPeer( block, extended, inst), 1397 .compile_log => try sema.zirCompileLog( block, extended), 1398 .min_multi => try sema.zirMinMaxMulti( block, extended, .min), 1399 .max_multi => try sema.zirMinMaxMulti( block, extended, .max), 1400 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1401 .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1402 .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1403 .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1404 .c_undef => try sema.zirCUndef( block, extended), 1405 .c_include => try sema.zirCInclude( block, extended), 1406 .c_define => try sema.zirCDefine( block, extended), 1407 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), 1408 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), 1409 .prefetch => try sema.zirPrefetch( block, extended), 1410 .error_cast => try sema.zirErrorCast( block, extended), 1411 .select => try sema.zirSelect( block, extended), 1412 .int_from_error => try sema.zirIntFromError( block, extended), 1413 .error_from_int => try sema.zirErrorFromInt( block, extended), 1414 .reify => try sema.zirReify( block, extended, inst), 1415 .cmpxchg => try sema.zirCmpxchg( block, extended), 1416 .c_va_arg => try sema.zirCVaArg( block, extended), 1417 .c_va_copy => try sema.zirCVaCopy( block, extended), 1418 .c_va_end => try sema.zirCVaEnd( block, extended), 1419 .c_va_start => try sema.zirCVaStart( block, extended), 1420 .ptr_cast_full => try sema.zirPtrCastFull( block, extended), 1421 .ptr_cast_no_dest => try sema.zirPtrCastNoDest( block, extended), 1422 .work_item_id => try sema.zirWorkItem( block, extended, extended.opcode), 1423 .work_group_size => try sema.zirWorkItem( block, extended, extended.opcode), 1424 .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode), 1425 .in_comptime => try sema.zirInComptime( block), 1426 .closure_get => try sema.zirClosureGet( block, extended), 1427 // zig fmt: on 1428 1429 .set_float_mode => { 1430 try sema.zirSetFloatMode(block, extended); 1431 i += 1; 1432 continue; 1433 }, 1434 .breakpoint => { 1435 if (!block.isComptime()) { 1436 _ = try block.addNoOp(.breakpoint); 1437 } 1438 i += 1; 1439 continue; 1440 }, 1441 .disable_instrumentation => { 1442 try sema.zirDisableInstrumentation(); 1443 i += 1; 1444 continue; 1445 }, 1446 .disable_intrinsics => { 1447 try sema.zirDisableIntrinsics(); 1448 i += 1; 1449 continue; 1450 }, 1451 .restore_err_ret_index => { 1452 try sema.zirRestoreErrRetIndex(block, extended); 1453 i += 1; 1454 continue; 1455 }, 1456 .branch_hint => { 1457 try sema.zirBranchHint(block, extended); 1458 i += 1; 1459 continue; 1460 }, 1461 .value_placeholder => unreachable, // never appears in a body 1462 .field_parent_ptr => try sema.zirFieldParentPtr(block, extended), 1463 .builtin_value => try sema.zirBuiltinValue(block, extended), 1464 .inplace_arith_result_ty => try sema.zirInplaceArithResultTy(extended), 1465 .dbg_empty_stmt => { 1466 try sema.zirDbgEmptyStmt(block, inst); 1467 i += 1; 1468 continue; 1469 }, 1470 .astgen_error => return error.AnalysisFail, 1471 }; 1472 }, 1473 1474 // Instructions that we know can *never* be noreturn based solely on 1475 // their tag. We avoid needlessly checking if they are noreturn and 1476 // continue the loop. 1477 // We also know that they cannot be referenced later, so we avoid 1478 // putting them into the map. 1479 .dbg_stmt => { 1480 try sema.zirDbgStmt(block, inst); 1481 i += 1; 1482 continue; 1483 }, 1484 .dbg_var_ptr => { 1485 try sema.zirDbgVar(block, inst, .dbg_var_ptr); 1486 i += 1; 1487 continue; 1488 }, 1489 .dbg_var_val => { 1490 try sema.zirDbgVar(block, inst, .dbg_var_val); 1491 i += 1; 1492 continue; 1493 }, 1494 .ensure_err_union_payload_void => { 1495 try sema.zirEnsureErrUnionPayloadVoid(block, inst); 1496 i += 1; 1497 continue; 1498 }, 1499 .ensure_result_non_error => { 1500 try sema.zirEnsureResultNonError(block, inst); 1501 i += 1; 1502 continue; 1503 }, 1504 .ensure_result_used => { 1505 try sema.zirEnsureResultUsed(block, inst); 1506 i += 1; 1507 continue; 1508 }, 1509 .set_eval_branch_quota => { 1510 try sema.zirSetEvalBranchQuota(block, inst); 1511 i += 1; 1512 continue; 1513 }, 1514 .atomic_store => { 1515 try sema.zirAtomicStore(block, inst); 1516 i += 1; 1517 continue; 1518 }, 1519 .store_node => { 1520 try sema.zirStoreNode(block, inst); 1521 i += 1; 1522 continue; 1523 }, 1524 .store_to_inferred_ptr => { 1525 try sema.zirStoreToInferredPtr(block, inst); 1526 i += 1; 1527 continue; 1528 }, 1529 .validate_struct_init_ty => { 1530 try sema.zirValidateStructInitTy(block, inst, false); 1531 i += 1; 1532 continue; 1533 }, 1534 .validate_struct_init_result_ty => { 1535 try sema.zirValidateStructInitTy(block, inst, true); 1536 i += 1; 1537 continue; 1538 }, 1539 .validate_array_init_ty => { 1540 try sema.zirValidateArrayInitTy(block, inst, false); 1541 i += 1; 1542 continue; 1543 }, 1544 .validate_array_init_result_ty => { 1545 try sema.zirValidateArrayInitTy(block, inst, true); 1546 i += 1; 1547 continue; 1548 }, 1549 .validate_ptr_struct_init => { 1550 try sema.zirValidatePtrStructInit(block, inst); 1551 i += 1; 1552 continue; 1553 }, 1554 .validate_ptr_array_init => { 1555 try sema.zirValidatePtrArrayInit(block, inst); 1556 i += 1; 1557 continue; 1558 }, 1559 .validate_deref => { 1560 try sema.zirValidateDeref(block, inst); 1561 i += 1; 1562 continue; 1563 }, 1564 .validate_destructure => { 1565 try sema.zirValidateDestructure(block, inst); 1566 i += 1; 1567 continue; 1568 }, 1569 .validate_ref_ty => { 1570 try sema.zirValidateRefTy(block, inst); 1571 i += 1; 1572 continue; 1573 }, 1574 .validate_const => { 1575 try sema.zirValidateConst(block, inst); 1576 i += 1; 1577 continue; 1578 }, 1579 .@"export" => { 1580 try sema.zirExport(block, inst); 1581 i += 1; 1582 continue; 1583 }, 1584 .set_runtime_safety => { 1585 try sema.zirSetRuntimeSafety(block, inst); 1586 i += 1; 1587 continue; 1588 }, 1589 .param => { 1590 try sema.zirParam(block, inst, false); 1591 i += 1; 1592 continue; 1593 }, 1594 .param_comptime => { 1595 try sema.zirParam(block, inst, true); 1596 i += 1; 1597 continue; 1598 }, 1599 .param_anytype => { 1600 try sema.zirParamAnytype(block, inst, false); 1601 i += 1; 1602 continue; 1603 }, 1604 .param_anytype_comptime => { 1605 try sema.zirParamAnytype(block, inst, true); 1606 i += 1; 1607 continue; 1608 }, 1609 .memcpy => { 1610 try sema.zirMemcpy(block, inst, .memcpy, true); 1611 i += 1; 1612 continue; 1613 }, 1614 .memmove => { 1615 try sema.zirMemcpy(block, inst, .memmove, false); 1616 i += 1; 1617 continue; 1618 }, 1619 .memset => { 1620 try sema.zirMemset(block, inst); 1621 i += 1; 1622 continue; 1623 }, 1624 .check_comptime_control_flow => { 1625 if (!block.isComptime()) { 1626 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 1627 const src = block.nodeOffset(inst_data.src_node); 1628 const inline_block = inst_data.operand.toIndex().?; 1629 1630 var check_block = block; 1631 const target_runtime_index = while (true) { 1632 if (check_block.inline_block == inline_block.toOptional()) { 1633 break check_block.runtime_index; 1634 } 1635 check_block = check_block.parent.?; 1636 }; 1637 1638 if (@intFromEnum(target_runtime_index) < @intFromEnum(block.runtime_index)) { 1639 const runtime_src = block.runtime_cond orelse block.runtime_loop.?; 1640 const msg = msg: { 1641 const msg = try sema.errMsg(src, "comptime control flow inside runtime block", .{}); 1642 errdefer msg.destroy(sema.gpa); 1643 try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); 1644 break :msg msg; 1645 }; 1646 return sema.failWithOwnedErrorMsg(block, msg); 1647 } 1648 } 1649 i += 1; 1650 continue; 1651 }, 1652 .save_err_ret_index => { 1653 try sema.zirSaveErrRetIndex(block, inst); 1654 i += 1; 1655 continue; 1656 }, 1657 .restore_err_ret_index_unconditional => { 1658 const un_node = datas[@intFromEnum(inst)].un_node; 1659 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), un_node.operand, .none); 1660 i += 1; 1661 continue; 1662 }, 1663 .restore_err_ret_index_fn_entry => { 1664 const un_node = datas[@intFromEnum(inst)].un_node; 1665 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), .none, un_node.operand); 1666 i += 1; 1667 continue; 1668 }, 1669 1670 // Special case instructions to handle comptime control flow. 1671 .@"break" => { 1672 if (block.isComptime()) { 1673 sema.comptime_break_inst = inst; 1674 return error.ComptimeBreak; 1675 } else { 1676 try sema.zirBreak(block, inst); 1677 break; 1678 } 1679 }, 1680 .break_inline => { 1681 sema.comptime_break_inst = inst; 1682 return error.ComptimeBreak; 1683 }, 1684 .repeat => { 1685 if (block.isComptime()) { 1686 // Send comptime control flow back to the beginning of this block. 1687 const src = block.nodeOffset(datas[@intFromEnum(inst)].node); 1688 try sema.emitBackwardBranch(block, src); 1689 i = 0; 1690 continue; 1691 } else { 1692 // We are definitely called by `zirLoop`, which will treat the 1693 // fact that this body does not terminate `noreturn` as an 1694 // implicit repeat. 1695 // TODO: since AIR has `repeat` now, we could change ZIR to generate 1696 // more optimal code utilizing `repeat` instructions across blocks! 1697 break; 1698 } 1699 }, 1700 .repeat_inline => { 1701 // Send comptime control flow back to the beginning of this block. 1702 const src = block.nodeOffset(datas[@intFromEnum(inst)].node); 1703 try sema.emitBackwardBranch(block, src); 1704 i = 0; 1705 continue; 1706 }, 1707 .switch_continue => if (block.isComptime()) { 1708 sema.comptime_break_inst = inst; 1709 return error.ComptimeBreak; 1710 } else { 1711 try sema.zirSwitchContinue(block, inst); 1712 break; 1713 }, 1714 1715 .loop => if (block.isComptime()) { 1716 continue :inst .block_inline; 1717 } else try sema.zirLoop(block, inst), 1718 1719 .block => if (block.isComptime()) { 1720 continue :inst .block_inline; 1721 } else try sema.zirBlock(block, inst), 1722 1723 .block_comptime => { 1724 const pl_node = datas[@intFromEnum(inst)].pl_node; 1725 const src = block.nodeOffset(pl_node.src_node); 1726 const extra = sema.code.extraData(Zir.Inst.BlockComptime, pl_node.payload_index); 1727 const block_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1728 1729 var child_block = block.makeSubBlock(); 1730 defer child_block.instructions.deinit(sema.gpa); 1731 1732 // We won't have any merges, but we must ensure this block is properly labeled for 1733 // any `.restore_err_ret_index_*` instructions. 1734 var label: Block.Label = .{ 1735 .zir_block = inst, 1736 .merges = undefined, 1737 }; 1738 child_block.label = &label; 1739 1740 child_block.comptime_reason = .{ .reason = .{ 1741 .src = src, 1742 .r = .{ .simple = extra.data.reason }, 1743 } }; 1744 1745 const result = try sema.resolveInlineBody(&child_block, block_body, inst); 1746 1747 // Only check for the result being comptime-known in the outermost `block_comptime`. 1748 // That way, AstGen can safely elide redundant `block_comptime` without affecting semantics. 1749 if (!block.isComptime() and !try sema.isComptimeKnown(result)) { 1750 return sema.failWithNeededComptime(&child_block, src, null); 1751 } 1752 1753 break :inst result; 1754 }, 1755 1756 .block_inline => blk: { 1757 // Directly analyze the block body without introducing a new block. 1758 // However, in the case of a corresponding break_inline which reaches 1759 // through a runtime conditional branch, we must retroactively emit 1760 // a block, so we remember the block index here just in case. 1761 const block_index = block.instructions.items.len; 1762 const inst_data = datas[@intFromEnum(inst)].pl_node; 1763 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 1764 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1765 const gpa = sema.gpa; 1766 1767 const BreakResult = struct { 1768 block_inst: Zir.Inst.Index, 1769 operand: Zir.Inst.Ref, 1770 }; 1771 1772 const opt_break_data: ?BreakResult, const need_debug_scope = b: { 1773 // Create a temporary child block so that this inline block is properly 1774 // labeled for any .restore_err_ret_index instructions 1775 var child_block = block.makeSubBlock(); 1776 var need_debug_scope = false; 1777 child_block.need_debug_scope = &need_debug_scope; 1778 1779 // If this block contains a function prototype, we need to reset the 1780 // current list of parameters and restore it later. 1781 // Note: this probably needs to be resolved in a more general manner. 1782 const tag_index = @intFromEnum(inline_body[inline_body.len - 1]); 1783 child_block.inline_block = (if (tags[tag_index] == .repeat_inline) 1784 inline_body[0] 1785 else 1786 inst).toOptional(); 1787 1788 var label: Block.Label = .{ 1789 .zir_block = inst, 1790 .merges = undefined, 1791 }; 1792 child_block.label = &label; 1793 1794 // Write these instructions directly into the parent block 1795 child_block.instructions = block.instructions; 1796 defer block.instructions = child_block.instructions; 1797 1798 const break_result: ?BreakResult = if (sema.analyzeBodyInner(&child_block, inline_body)) |_| r: { 1799 break :r null; 1800 } else |err| switch (err) { 1801 error.ComptimeBreak => brk_res: { 1802 const break_inst = sema.comptime_break_inst; 1803 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; 1804 const break_extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 1805 break :brk_res .{ 1806 .block_inst = break_extra.block_inst, 1807 .operand = break_data.operand, 1808 }; 1809 }, 1810 else => |e| return e, 1811 }; 1812 1813 if (need_debug_scope) { 1814 _ = try sema.ensurePostHoc(block, inst); 1815 } 1816 1817 break :b .{ break_result, need_debug_scope }; 1818 }; 1819 1820 // A runtime conditional branch that needs a post-hoc block to be 1821 // emitted communicates this by mapping the block index into the inst map. 1822 if (map.get(inst)) |new_block_ref| ph: { 1823 // Comptime control flow populates the map, so we don't actually know 1824 // if this is a post-hoc runtime block until we check the 1825 // post_hoc_block map. 1826 const new_block_inst = new_block_ref.toIndex() orelse break :ph; 1827 const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse 1828 break :ph; 1829 1830 // In this case we need to move all the instructions starting at 1831 // block_index from the current block into this new one. 1832 1833 if (opt_break_data) |break_data| { 1834 // This is a comptime break which we now change to a runtime break 1835 // since it crosses a runtime branch. 1836 // It may pass through our currently being analyzed block_inline or it 1837 // may point directly to it. In the latter case, this modifies the 1838 // block that we looked up in the post_hoc_blocks map above. 1839 try sema.addRuntimeBreak(block, break_data.block_inst, break_data.operand); 1840 } 1841 1842 try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]); 1843 block.instructions.items.len = block_index; 1844 1845 const block_result = try sema.resolveAnalyzedBlock(block, block.nodeOffset(inst_data.src_node), &labeled_block.block, &labeled_block.label.merges, need_debug_scope); 1846 { 1847 // Destroy the ad-hoc block entry so that it does not interfere with 1848 // the next iteration of comptime control flow, if any. 1849 labeled_block.destroy(gpa); 1850 assert(sema.post_hoc_blocks.remove(new_block_inst)); 1851 } 1852 1853 break :blk block_result; 1854 } 1855 1856 const break_data = opt_break_data orelse break; 1857 if (inst == break_data.block_inst) { 1858 break :blk try sema.resolveInst(break_data.operand); 1859 } else { 1860 // `comptime_break_inst` preserved from `analyzeBodyInner` above. 1861 return error.ComptimeBreak; 1862 } 1863 }, 1864 .condbr => if (block.isComptime()) { 1865 continue :inst .condbr_inline; 1866 } else { 1867 try sema.zirCondbr(block, inst); 1868 break; 1869 }, 1870 .condbr_inline => { 1871 const inst_data = datas[@intFromEnum(inst)].pl_node; 1872 const cond_src = block.src(.{ .node_offset_if_cond = inst_data.src_node }); 1873 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 1874 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); 1875 const else_body = sema.code.bodySlice( 1876 extra.end + then_body.len, 1877 extra.data.else_body_len, 1878 ); 1879 const uncasted_cond = try sema.resolveInst(extra.data.condition); 1880 const cond = try sema.coerce(block, .bool, uncasted_cond, cond_src); 1881 const cond_val = try sema.resolveConstDefinedValue( 1882 block, 1883 cond_src, 1884 cond, 1885 // If this block is comptime, it's more helpful to just give the outer message. 1886 // This is particularly true if this came from a comptime `condbr` above. 1887 if (block.isComptime()) null else .{ .simple = .inline_loop_operand }, 1888 ); 1889 const inline_body = if (cond_val.toBool()) then_body else else_body; 1890 1891 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src); 1892 const old_runtime_index = block.runtime_index; 1893 defer block.runtime_index = old_runtime_index; 1894 1895 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; 1896 break :inst result; 1897 }, 1898 .@"try" => blk: { 1899 if (!block.isComptime()) break :blk try sema.zirTry(block, inst); 1900 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 1901 const src = block.nodeOffset(inst_data.src_node); 1902 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node }); 1903 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 1904 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1905 const err_union = try sema.resolveInst(extra.data.operand); 1906 const err_union_ty = sema.typeOf(err_union); 1907 if (err_union_ty.zigTypeTag(zcu) != .error_union) { 1908 return sema.failWithOwnedErrorMsg(block, msg: { 1909 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); 1910 errdefer msg.destroy(sema.gpa); 1911 try sema.addDeclaredHereNote(msg, err_union_ty); 1912 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); 1913 break :msg msg; 1914 }); 1915 } 1916 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); 1917 assert(is_non_err != .none); 1918 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null); 1919 if (is_non_err_val.toBool()) { 1920 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); 1921 } 1922 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; 1923 break :blk result; 1924 }, 1925 .try_ptr => blk: { 1926 if (!block.isComptime()) break :blk try sema.zirTryPtr(block, inst); 1927 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 1928 const src = block.nodeOffset(inst_data.src_node); 1929 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node }); 1930 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 1931 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1932 const operand = try sema.resolveInst(extra.data.operand); 1933 const err_union = try sema.analyzeLoad(block, src, operand, operand_src); 1934 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); 1935 assert(is_non_err != .none); 1936 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null); 1937 if (is_non_err_val.toBool()) { 1938 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); 1939 } 1940 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; 1941 break :blk result; 1942 }, 1943 .@"defer" => blk: { 1944 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"defer"; 1945 const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len); 1946 if (sema.analyzeBodyInner(block, defer_body)) |_| { 1947 // The defer terminated noreturn - no more analysis needed. 1948 break; 1949 } else |err| switch (err) { 1950 error.ComptimeBreak => {}, 1951 else => |e| return e, 1952 } 1953 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) { 1954 return error.ComptimeBreak; 1955 } 1956 break :blk .void_value; 1957 }, 1958 .defer_err_code => blk: { 1959 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code; 1960 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; 1961 const defer_body = sema.code.bodySlice(extra.index, extra.len); 1962 const err_code = try sema.resolveInst(inst_data.err_code); 1963 try map.ensureSpaceForInstructions(sema.gpa, defer_body); 1964 map.putAssumeCapacity(extra.remapped_err_code, err_code); 1965 if (sema.analyzeBodyInner(block, defer_body)) |_| { 1966 // The defer terminated noreturn - no more analysis needed. 1967 break; 1968 } else |err| switch (err) { 1969 error.ComptimeBreak => {}, 1970 else => |e| return e, 1971 } 1972 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) { 1973 return error.ComptimeBreak; 1974 } 1975 break :blk .void_value; 1976 }, 1977 }; 1978 if (sema.isNoReturn(air_inst)) { 1979 // We're going to assume that the body itself is noreturn, so let's ensure that now 1980 assert(block.instructions.items.len > 0); 1981 assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef())); 1982 break; 1983 } 1984 map.putAssumeCapacity(inst, air_inst); 1985 i += 1; 1986 } 1987 } 1988 1989 pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { 1990 if (zir_ref == .none) { 1991 return .none; 1992 } else { 1993 return resolveInst(sema, zir_ref); 1994 } 1995 } 1996 1997 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { 1998 assert(zir_ref != .none); 1999 if (zir_ref.toIndex()) |i| { 2000 return sema.inst_map.get(i).?; 2001 } 2002 // First section of indexes correspond to a set number of constant values. 2003 // We intentionally map the same indexes to the same values between ZIR and AIR. 2004 return @enumFromInt(@intFromEnum(zir_ref)); 2005 } 2006 2007 fn resolveConstBool( 2008 sema: *Sema, 2009 block: *Block, 2010 src: LazySrcLoc, 2011 zir_ref: Zir.Inst.Ref, 2012 reason: ComptimeReason, 2013 ) !bool { 2014 const air_inst = try sema.resolveInst(zir_ref); 2015 const wanted_type: Type = .bool; 2016 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 2017 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 2018 return val.toBool(); 2019 } 2020 2021 fn resolveConstString( 2022 sema: *Sema, 2023 block: *Block, 2024 src: LazySrcLoc, 2025 zir_ref: Zir.Inst.Ref, 2026 reason: ComptimeReason, 2027 ) ![]u8 { 2028 const air_inst = try sema.resolveInst(zir_ref); 2029 return sema.toConstString(block, src, air_inst, reason); 2030 } 2031 2032 pub fn toConstString( 2033 sema: *Sema, 2034 block: *Block, 2035 src: LazySrcLoc, 2036 air_inst: Air.Inst.Ref, 2037 reason: ComptimeReason, 2038 ) ![]u8 { 2039 const pt = sema.pt; 2040 const coerced_inst = try sema.coerce(block, .slice_const_u8, air_inst, src); 2041 const slice_val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 2042 const arr_val = try sema.derefSliceAsArray(block, src, slice_val, reason); 2043 return arr_val.toAllocatedBytes(arr_val.typeOf(pt.zcu), sema.arena, pt); 2044 } 2045 2046 pub fn resolveConstStringIntern( 2047 sema: *Sema, 2048 block: *Block, 2049 src: LazySrcLoc, 2050 zir_ref: Zir.Inst.Ref, 2051 reason: ComptimeReason, 2052 ) !InternPool.NullTerminatedString { 2053 const air_inst = try sema.resolveInst(zir_ref); 2054 const wanted_type: Type = .slice_const_u8; 2055 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 2056 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 2057 return sema.sliceToIpString(block, src, val, reason); 2058 } 2059 2060 fn resolveTypeOrPoison(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !?Type { 2061 const air_inst = try sema.resolveInst(zir_ref); 2062 const ty = try sema.analyzeAsType(block, src, air_inst); 2063 if (ty.isGenericPoison()) return null; 2064 return ty; 2065 } 2066 2067 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { 2068 return (try sema.resolveTypeOrPoison(block, src, zir_ref)).?; 2069 } 2070 2071 fn resolveDestType( 2072 sema: *Sema, 2073 block: *Block, 2074 src: LazySrcLoc, 2075 zir_ref: Zir.Inst.Ref, 2076 strat: enum { remove_eu_opt, remove_eu, remove_opt }, 2077 builtin_name: []const u8, 2078 ) !Type { 2079 const pt = sema.pt; 2080 const zcu = pt.zcu; 2081 const remove_eu = switch (strat) { 2082 .remove_eu_opt, .remove_eu => true, 2083 .remove_opt => false, 2084 }; 2085 const remove_opt = switch (strat) { 2086 .remove_eu_opt, .remove_opt => true, 2087 .remove_eu => false, 2088 }; 2089 2090 const raw_ty = try sema.resolveTypeOrPoison(block, src, zir_ref) orelse { 2091 // Cast builtins use their result type as the destination type, but 2092 // it could be an anytype argument, which we can't catch in AstGen. 2093 const msg = msg: { 2094 const msg = try sema.errMsg(src, "{s} must have a known result type", .{builtin_name}); 2095 errdefer msg.destroy(sema.gpa); 2096 switch (sema.genericPoisonReason(block, zir_ref)) { 2097 .anytype_param => |call_src| try sema.errNote(call_src, msg, "result type is unknown due to anytype parameter", .{}), 2098 .anyopaque_ptr => |ptr_src| try sema.errNote(ptr_src, msg, "result type is unknown due to opaque pointer type", .{}), 2099 .unknown => {}, 2100 } 2101 try sema.errNote(src, msg, "use @as to provide explicit result type", .{}); 2102 break :msg msg; 2103 }; 2104 return sema.failWithOwnedErrorMsg(block, msg); 2105 }; 2106 2107 if (remove_eu and raw_ty.zigTypeTag(zcu) == .error_union) { 2108 const eu_child = raw_ty.errorUnionPayload(zcu); 2109 if (remove_opt and eu_child.zigTypeTag(zcu) == .optional) { 2110 return eu_child.childType(zcu); 2111 } 2112 return eu_child; 2113 } 2114 if (remove_opt and raw_ty.zigTypeTag(zcu) == .optional) { 2115 return raw_ty.childType(zcu); 2116 } 2117 return raw_ty; 2118 } 2119 2120 const GenericPoisonReason = union(enum) { 2121 anytype_param: LazySrcLoc, 2122 anyopaque_ptr: LazySrcLoc, 2123 unknown, 2124 }; 2125 2126 /// Backtracks through ZIR instructions to determine the reason a generic poison 2127 /// type was created. Used for error reporting. 2128 fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoisonReason { 2129 var cur = ref; 2130 while (true) { 2131 const inst = cur.toIndex() orelse return .unknown; 2132 switch (sema.code.instructions.items(.tag)[@intFromEnum(inst)]) { 2133 .validate_array_init_ref_ty => { 2134 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 2135 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; 2136 cur = extra.ptr_ty; 2137 }, 2138 .array_init_elem_type => { 2139 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 2140 cur = bin.lhs; 2141 }, 2142 .indexable_ptr_elem_type, .vec_arr_elem_type => { 2143 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 2144 cur = un_node.operand; 2145 }, 2146 .struct_init_field_type => { 2147 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 2148 const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data; 2149 cur = extra.container_type; 2150 }, 2151 .elem_type => { 2152 // There are two cases here: the pointer type may already have been 2153 // generic poison, or it may have been an anyopaque pointer. 2154 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 2155 const operand_ref = try sema.resolveInst(un_node.operand); 2156 const operand_val = operand_ref.toInterned() orelse return .unknown; 2157 if (operand_val == .generic_poison_type) { 2158 // The pointer was generic poison - keep looking. 2159 cur = un_node.operand; 2160 } else { 2161 // This must be an anyopaque pointer! 2162 return .{ .anyopaque_ptr = block.nodeOffset(un_node.src_node) }; 2163 } 2164 }, 2165 .call, .field_call => { 2166 // A function call can never return generic poison, so we must be 2167 // evaluating an `anytype` function parameter. 2168 // TODO: better source location - function decl rather than call 2169 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 2170 return .{ .anytype_param = block.nodeOffset(pl_node.src_node) }; 2171 }, 2172 else => return .unknown, 2173 } 2174 } 2175 } 2176 2177 fn analyzeAsType( 2178 sema: *Sema, 2179 block: *Block, 2180 src: LazySrcLoc, 2181 air_inst: Air.Inst.Ref, 2182 ) !Type { 2183 const wanted_type: Type = .type; 2184 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 2185 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ .simple = .type }); 2186 return val.toType(); 2187 } 2188 2189 pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { 2190 const pt = sema.pt; 2191 const zcu = pt.zcu; 2192 const comp = zcu.comp; 2193 const gpa = sema.gpa; 2194 const ip = &zcu.intern_pool; 2195 if (!comp.config.any_error_tracing) return; 2196 2197 assert(!block.isComptime()); 2198 var err_trace_block = block.makeSubBlock(); 2199 defer err_trace_block.instructions.deinit(gpa); 2200 2201 const src: LazySrcLoc = LazySrcLoc.unneeded; 2202 2203 // var addrs: [err_return_trace_addr_count]usize = undefined; 2204 const err_return_trace_addr_count = 32; 2205 const addr_arr_ty = try pt.arrayType(.{ 2206 .len = err_return_trace_addr_count, 2207 .child = .usize_type, 2208 }); 2209 const addrs_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(addr_arr_ty)); 2210 2211 // var st: StackTrace = undefined; 2212 const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); 2213 try stack_trace_ty.resolveFields(pt); 2214 const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty)); 2215 2216 // st.instruction_addresses = &addrs; 2217 const instruction_addresses_field_name = try ip.getOrPutString(gpa, pt.tid, "instruction_addresses", .no_embedded_nulls); 2218 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true); 2219 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); 2220 2221 // st.index = 0; 2222 const index_field_name = try ip.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); 2223 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true); 2224 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store); 2225 2226 // @errorReturnTrace() = &st; 2227 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr); 2228 2229 try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items); 2230 } 2231 2232 /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value. 2233 fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2234 const zcu = sema.pt.zcu; 2235 assert(inst != .none); 2236 2237 if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { 2238 return opv; 2239 } 2240 2241 if (inst.toInterned()) |ip_index| { 2242 const val: Value = .fromInterned(ip_index); 2243 assert(val.getVariable(zcu) == null); 2244 return val; 2245 } else { 2246 // Runtime-known value. 2247 const air_tags = sema.air_instructions.items(.tag); 2248 switch (air_tags[@intFromEnum(inst.toIndex().?)]) { 2249 .inferred_alloc => unreachable, // assertion failure 2250 .inferred_alloc_comptime => unreachable, // assertion failure 2251 else => {}, 2252 } 2253 return null; 2254 } 2255 } 2256 2257 /// Like `resolveValue`, but emits an error if the value is not comptime-known. 2258 fn resolveConstValue( 2259 sema: *Sema, 2260 block: *Block, 2261 src: LazySrcLoc, 2262 inst: Air.Inst.Ref, 2263 reason: ?ComptimeReason, 2264 ) CompileError!Value { 2265 return try sema.resolveValue(inst) orelse { 2266 return sema.failWithNeededComptime(block, src, reason); 2267 }; 2268 } 2269 2270 /// Like `resolveValue`, but emits an error if the value is comptime-known to be undefined. 2271 fn resolveDefinedValue( 2272 sema: *Sema, 2273 block: *Block, 2274 src: LazySrcLoc, 2275 air_ref: Air.Inst.Ref, 2276 ) CompileError!?Value { 2277 const pt = sema.pt; 2278 const zcu = pt.zcu; 2279 const val = try sema.resolveValue(air_ref) orelse return null; 2280 if (val.isUndef(zcu)) { 2281 return sema.failWithUseOfUndef(block, src); 2282 } 2283 return val; 2284 } 2285 2286 /// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined. 2287 fn resolveConstDefinedValue( 2288 sema: *Sema, 2289 block: *Block, 2290 src: LazySrcLoc, 2291 air_ref: Air.Inst.Ref, 2292 reason: ?ComptimeReason, 2293 ) CompileError!Value { 2294 const val = try sema.resolveConstValue(block, src, air_ref, reason); 2295 if (val.isUndef(sema.pt.zcu)) return sema.failWithUseOfUndef(block, src); 2296 return val; 2297 } 2298 2299 /// Like `resolveValue`, but recursively resolves lazy values before returning. 2300 fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2301 return try sema.resolveLazyValue((try sema.resolveValue(inst)) orelse return null); 2302 } 2303 2304 /// Value Tag may be `undef` or `variable`. 2305 pub fn resolveFinalDeclValue( 2306 sema: *Sema, 2307 block: *Block, 2308 src: LazySrcLoc, 2309 air_ref: Air.Inst.Ref, 2310 ) CompileError!Value { 2311 const zcu = sema.pt.zcu; 2312 const val = try sema.resolveConstValue(block, src, air_ref, .{ .simple = .container_var_init }); 2313 if (val.canMutateComptimeVarState(zcu)) { 2314 const ip = &zcu.intern_pool; 2315 const nav = ip.getNav(sema.owner.unwrap().nav_val); 2316 return sema.failWithContainsReferenceToComptimeVar(block, src, nav.name, "global variable", val); 2317 } 2318 return val; 2319 } 2320 2321 fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: ?ComptimeReason) CompileError { 2322 const msg, const fail_block = msg: { 2323 const msg = try sema.errMsg(src, "unable to resolve comptime value", .{}); 2324 errdefer msg.destroy(sema.gpa); 2325 const fail_block = if (reason) |r| b: { 2326 try r.explain(sema, src, msg); 2327 break :b block; 2328 } else b: { 2329 break :b try block.explainWhyBlockIsComptime(msg); 2330 }; 2331 break :msg .{ msg, fail_block }; 2332 }; 2333 return sema.failWithOwnedErrorMsg(fail_block, msg); 2334 } 2335 2336 pub fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2337 return sema.fail(block, src, "use of undefined value here causes illegal behavior", .{}); 2338 } 2339 2340 pub fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2341 return sema.fail(block, src, "division by zero here causes illegal behavior", .{}); 2342 } 2343 2344 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { 2345 const pt = sema.pt; 2346 return sema.fail(block, src, "remainder division with '{f}' and '{f}': signed integers and floats must use @rem or @mod", .{ 2347 lhs_ty.fmt(pt), rhs_ty.fmt(pt), 2348 }); 2349 } 2350 2351 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError { 2352 const pt = sema.pt; 2353 const msg = msg: { 2354 const msg = try sema.errMsg(src, "expected optional type, found '{f}'", .{ 2355 non_optional_ty.fmt(pt), 2356 }); 2357 errdefer msg.destroy(sema.gpa); 2358 if (non_optional_ty.zigTypeTag(pt.zcu) == .error_union) { 2359 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 2360 } 2361 try addDeclaredHereNote(sema, msg, non_optional_ty); 2362 break :msg msg; 2363 }; 2364 return sema.failWithOwnedErrorMsg(block, msg); 2365 } 2366 2367 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { 2368 const pt = sema.pt; 2369 const msg = msg: { 2370 const msg = try sema.errMsg(src, "type '{f}' does not support array initialization syntax", .{ 2371 ty.fmt(pt), 2372 }); 2373 errdefer msg.destroy(sema.gpa); 2374 if (ty.isSlice(pt.zcu)) { 2375 try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.elemType2(pt.zcu).fmt(pt)}); 2376 } 2377 break :msg msg; 2378 }; 2379 return sema.failWithOwnedErrorMsg(block, msg); 2380 } 2381 2382 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { 2383 const pt = sema.pt; 2384 return sema.fail(block, src, "type '{f}' does not support struct initialization syntax", .{ 2385 ty.fmt(pt), 2386 }); 2387 } 2388 2389 fn failWithErrorSetCodeMissing( 2390 sema: *Sema, 2391 block: *Block, 2392 src: LazySrcLoc, 2393 dest_err_set_ty: Type, 2394 src_err_set_ty: Type, 2395 ) CompileError { 2396 const pt = sema.pt; 2397 return sema.fail(block, src, "expected type '{f}', found type '{f}'", .{ 2398 dest_err_set_ty.fmt(pt), src_err_set_ty.fmt(pt), 2399 }); 2400 } 2401 2402 pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError { 2403 const pt = sema.pt; 2404 return sema.failWithOwnedErrorMsg(block, msg: { 2405 const msg = try sema.errMsg(src, "overflow of integer type '{f}' with value '{f}'", .{ 2406 int_ty.fmt(pt), val.fmtValueSema(pt, sema), 2407 }); 2408 errdefer msg.destroy(sema.gpa); 2409 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); 2410 break :msg msg; 2411 }); 2412 } 2413 2414 fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError { 2415 const pt = sema.pt; 2416 const zcu = pt.zcu; 2417 const msg = msg: { 2418 const msg = try sema.errMsg(init_src, "value stored in comptime field does not match the default value of the field", .{}); 2419 errdefer msg.destroy(sema.gpa); 2420 2421 const struct_type = zcu.typeToStruct(container_ty) orelse break :msg msg; 2422 try sema.errNote(.{ 2423 .base_node_inst = struct_type.zir_index, 2424 .offset = .{ .container_field_value = @intCast(field_index) }, 2425 }, msg, "default value set here", .{}); 2426 break :msg msg; 2427 }; 2428 return sema.failWithOwnedErrorMsg(block, msg); 2429 } 2430 2431 fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2432 const msg = msg: { 2433 const msg = try sema.errMsg(src, "async has not been implemented in the self-hosted compiler yet", .{}); 2434 errdefer msg.destroy(sema.gpa); 2435 break :msg msg; 2436 }; 2437 return sema.failWithOwnedErrorMsg(block, msg); 2438 } 2439 2440 fn failWithInvalidFieldAccess( 2441 sema: *Sema, 2442 block: *Block, 2443 src: LazySrcLoc, 2444 object_ty: Type, 2445 field_name: InternPool.NullTerminatedString, 2446 ) CompileError { 2447 const pt = sema.pt; 2448 const zcu = pt.zcu; 2449 const inner_ty = if (object_ty.isSinglePointer(zcu)) object_ty.childType(zcu) else object_ty; 2450 2451 if (inner_ty.zigTypeTag(zcu) == .optional) opt: { 2452 const child_ty = inner_ty.optionalChild(zcu); 2453 if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :opt; 2454 const msg = msg: { 2455 const msg = try sema.errMsg(src, "optional type '{f}' does not support field access", .{object_ty.fmt(pt)}); 2456 errdefer msg.destroy(sema.gpa); 2457 try sema.errNote(src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 2458 break :msg msg; 2459 }; 2460 return sema.failWithOwnedErrorMsg(block, msg); 2461 } else if (inner_ty.zigTypeTag(zcu) == .error_union) err: { 2462 const child_ty = inner_ty.errorUnionPayload(zcu); 2463 if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :err; 2464 const msg = msg: { 2465 const msg = try sema.errMsg(src, "error union type '{f}' does not support field access", .{object_ty.fmt(pt)}); 2466 errdefer msg.destroy(sema.gpa); 2467 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 2468 break :msg msg; 2469 }; 2470 return sema.failWithOwnedErrorMsg(block, msg); 2471 } 2472 return sema.fail(block, src, "type '{f}' does not support field access", .{object_ty.fmt(pt)}); 2473 } 2474 2475 fn typeSupportsFieldAccess(zcu: *const Zcu, ty: Type, field_name: InternPool.NullTerminatedString) bool { 2476 const ip = &zcu.intern_pool; 2477 switch (ty.zigTypeTag(zcu)) { 2478 .array => return field_name.eqlSlice("len", ip), 2479 .pointer => { 2480 const ptr_info = ty.ptrInfo(zcu); 2481 if (ptr_info.flags.size == .slice) { 2482 return field_name.eqlSlice("ptr", ip) or field_name.eqlSlice("len", ip); 2483 } else if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { 2484 return field_name.eqlSlice("len", ip); 2485 } else return false; 2486 }, 2487 .type, .@"struct", .@"union" => return true, 2488 else => return false, 2489 } 2490 } 2491 2492 fn failWithComptimeErrorRetTrace( 2493 sema: *Sema, 2494 block: *Block, 2495 src: LazySrcLoc, 2496 name: InternPool.NullTerminatedString, 2497 ) CompileError { 2498 const pt = sema.pt; 2499 const zcu = pt.zcu; 2500 const msg = msg: { 2501 const msg = try sema.errMsg(src, "caught unexpected error '{f}'", .{name.fmt(&zcu.intern_pool)}); 2502 errdefer msg.destroy(sema.gpa); 2503 2504 for (sema.comptime_err_ret_trace.items) |src_loc| { 2505 try sema.errNote(src_loc, msg, "error returned here", .{}); 2506 } 2507 break :msg msg; 2508 }; 2509 return sema.failWithOwnedErrorMsg(block, msg); 2510 } 2511 2512 fn failWithInvalidPtrArithmetic(sema: *Sema, block: *Block, src: LazySrcLoc, arithmetic: []const u8, supports: []const u8) CompileError { 2513 const msg = msg: { 2514 const msg = try sema.errMsg(src, "invalid {s} arithmetic operator", .{arithmetic}); 2515 errdefer msg.destroy(sema.gpa); 2516 try sema.errNote(src, msg, "{s} arithmetic only supports {s}", .{ arithmetic, supports }); 2517 break :msg msg; 2518 }; 2519 return sema.failWithOwnedErrorMsg(block, msg); 2520 } 2521 2522 /// We don't return a pointer to the new error note because the pointer 2523 /// becomes invalid when you add another one. 2524 pub fn errNote( 2525 sema: *Sema, 2526 src: LazySrcLoc, 2527 parent: *Zcu.ErrorMsg, 2528 comptime format: []const u8, 2529 args: anytype, 2530 ) error{OutOfMemory}!void { 2531 return sema.pt.zcu.errNote(src, parent, format, args); 2532 } 2533 2534 fn addFieldErrNote( 2535 sema: *Sema, 2536 container_ty: Type, 2537 field_index: usize, 2538 parent: *Zcu.ErrorMsg, 2539 comptime format: []const u8, 2540 args: anytype, 2541 ) !void { 2542 @branchHint(.cold); 2543 const type_src = container_ty.srcLocOrNull(sema.pt.zcu) orelse return; 2544 const field_src: LazySrcLoc = .{ 2545 .base_node_inst = type_src.base_node_inst, 2546 .offset = .{ .container_field_name = @intCast(field_index) }, 2547 }; 2548 try sema.errNote(field_src, parent, format, args); 2549 } 2550 2551 pub fn errMsg( 2552 sema: *Sema, 2553 src: LazySrcLoc, 2554 comptime format: []const u8, 2555 args: anytype, 2556 ) Allocator.Error!*Zcu.ErrorMsg { 2557 assert(src.offset != .unneeded); 2558 return Zcu.ErrorMsg.create(sema.gpa, src, format, args); 2559 } 2560 2561 pub fn fail( 2562 sema: *Sema, 2563 block: *Block, 2564 src: LazySrcLoc, 2565 comptime format: []const u8, 2566 args: anytype, 2567 ) CompileError { 2568 const err_msg = try sema.errMsg(src, format, args); 2569 inline for (args) |arg| { 2570 if (@TypeOf(arg) == Type.Formatter) { 2571 try addDeclaredHereNote(sema, err_msg, arg.data.ty); 2572 } 2573 } 2574 return sema.failWithOwnedErrorMsg(block, err_msg); 2575 } 2576 2577 pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) error{ AnalysisFail, OutOfMemory } { 2578 @branchHint(.cold); 2579 const gpa = sema.gpa; 2580 const zcu = sema.pt.zcu; 2581 2582 if (build_options.enable_debug_extensions and zcu.comp.debug_compile_errors) { 2583 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 2584 wip_errors.init(gpa) catch @panic("out of memory"); 2585 Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory"); 2586 std.debug.print("compile error during Sema:\n", .{}); 2587 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory"); 2588 error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); 2589 crash_report.compilerPanic("unexpected compile error occurred", null); 2590 } 2591 2592 if (block) |start_block| { 2593 var block_it = start_block; 2594 while (block_it.inlining) |inlining| { 2595 const note_str = note: { 2596 if (inlining.is_generic_instantiation) break :note "generic function instantiated here"; 2597 if (inlining.call_block.isComptime()) break :note "called at comptime here"; 2598 break :note "called inline here"; 2599 }; 2600 try sema.errNote(inlining.call_src, err_msg, "{s}", .{note_str}); 2601 block_it = inlining.call_block; 2602 } 2603 } 2604 2605 err_msg.reference_trace_root = sema.owner.toOptional(); 2606 2607 const gop = try zcu.failed_analysis.getOrPut(gpa, sema.owner); 2608 if (gop.found_existing) { 2609 // If there are multiple errors for the same Decl, prefer the first one added. 2610 sema.err = null; 2611 err_msg.destroy(gpa); 2612 } else { 2613 sema.err = err_msg; 2614 gop.value_ptr.* = err_msg; 2615 } 2616 2617 return error.AnalysisFail; 2618 } 2619 2620 /// Given an ErrorMsg, modify its message and source location to the given values, turning the 2621 /// original message into a note. Notes on the original message are preserved as further notes. 2622 /// Reference trace is preserved. 2623 fn reparentOwnedErrorMsg( 2624 sema: *Sema, 2625 src: LazySrcLoc, 2626 msg: *Zcu.ErrorMsg, 2627 comptime format: []const u8, 2628 args: anytype, 2629 ) !void { 2630 const msg_str = try std.fmt.allocPrint(sema.gpa, format, args); 2631 2632 const orig_notes = msg.notes.len; 2633 msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1); 2634 std.mem.copyBackwards(Zcu.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]); 2635 msg.notes[0] = .{ 2636 .src_loc = msg.src_loc, 2637 .msg = msg.msg, 2638 }; 2639 2640 msg.src_loc = src; 2641 msg.msg = msg_str; 2642 } 2643 2644 const align_ty: Type = .u29; 2645 2646 pub fn analyzeAsAlign( 2647 sema: *Sema, 2648 block: *Block, 2649 src: LazySrcLoc, 2650 air_ref: Air.Inst.Ref, 2651 ) !Alignment { 2652 const alignment_big = try sema.analyzeAsInt( 2653 block, 2654 src, 2655 air_ref, 2656 align_ty, 2657 .{ .simple = .@"align" }, 2658 ); 2659 return sema.validateAlign(block, src, alignment_big); 2660 } 2661 2662 fn validateAlign( 2663 sema: *Sema, 2664 block: *Block, 2665 src: LazySrcLoc, 2666 alignment: u64, 2667 ) !Alignment { 2668 if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); 2669 if (!std.math.isPowerOfTwo(alignment)) { 2670 return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{ 2671 alignment, 2672 }); 2673 } 2674 return Alignment.fromNonzeroByteUnits(alignment); 2675 } 2676 2677 fn resolveAlign( 2678 sema: *Sema, 2679 block: *Block, 2680 src: LazySrcLoc, 2681 zir_ref: Zir.Inst.Ref, 2682 ) !Alignment { 2683 const air_ref = try sema.resolveInst(zir_ref); 2684 return sema.analyzeAsAlign(block, src, air_ref); 2685 } 2686 2687 fn resolveInt( 2688 sema: *Sema, 2689 block: *Block, 2690 src: LazySrcLoc, 2691 zir_ref: Zir.Inst.Ref, 2692 dest_ty: Type, 2693 reason: ComptimeReason, 2694 ) !u64 { 2695 const air_ref = try sema.resolveInst(zir_ref); 2696 return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason); 2697 } 2698 2699 fn analyzeAsInt( 2700 sema: *Sema, 2701 block: *Block, 2702 src: LazySrcLoc, 2703 air_ref: Air.Inst.Ref, 2704 dest_ty: Type, 2705 reason: ComptimeReason, 2706 ) !u64 { 2707 const coerced = try sema.coerce(block, dest_ty, air_ref, src); 2708 const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); 2709 return try val.toUnsignedIntSema(sema.pt); 2710 } 2711 2712 fn analyzeValueAsCallconv( 2713 sema: *Sema, 2714 block: *Block, 2715 src: LazySrcLoc, 2716 unresolved_val: Value, 2717 ) !std.builtin.CallingConvention { 2718 return interpretBuiltinType(sema, block, src, unresolved_val, std.builtin.CallingConvention); 2719 } 2720 2721 fn interpretBuiltinType( 2722 sema: *Sema, 2723 block: *Block, 2724 src: LazySrcLoc, 2725 unresolved_val: Value, 2726 comptime T: type, 2727 ) !T { 2728 const resolved_val = try sema.resolveLazyValue(unresolved_val); 2729 return resolved_val.interpret(T, sema.pt) catch |err| switch (err) { 2730 error.OutOfMemory => |e| return e, 2731 error.UndefinedValue => return sema.failWithUseOfUndef(block, src), 2732 error.TypeMismatch => @panic("std.builtin is corrupt"), 2733 }; 2734 } 2735 2736 fn zirTupleDecl( 2737 sema: *Sema, 2738 block: *Block, 2739 extended: Zir.Inst.Extended.InstData, 2740 ) CompileError!Air.Inst.Ref { 2741 const gpa = sema.gpa; 2742 const pt = sema.pt; 2743 const zcu = pt.zcu; 2744 const fields_len = extended.small; 2745 const extra = sema.code.extraData(Zir.Inst.TupleDecl, extended.operand); 2746 var extra_index = extra.end; 2747 2748 const types = try sema.arena.alloc(InternPool.Index, fields_len); 2749 const inits = try sema.arena.alloc(InternPool.Index, fields_len); 2750 2751 const extra_as_refs: []const Zir.Inst.Ref = @ptrCast(sema.code.extra); 2752 2753 for (types, inits, 0..) |*field_ty, *field_init, field_index| { 2754 const zir_field_ty, const zir_field_init = extra_as_refs[extra_index..][0..2].*; 2755 extra_index += 2; 2756 2757 const type_src = block.src(.{ .tuple_field_type = .{ 2758 .tuple_decl_node_offset = extra.data.src_node, 2759 .elem_index = @intCast(field_index), 2760 } }); 2761 const init_src = block.src(.{ .tuple_field_init = .{ 2762 .tuple_decl_node_offset = extra.data.src_node, 2763 .elem_index = @intCast(field_index), 2764 } }); 2765 2766 const field_type = try sema.resolveType(block, type_src, zir_field_ty); 2767 try sema.validateTupleFieldType(block, field_type, type_src); 2768 2769 field_ty.* = field_type.toIntern(); 2770 field_init.* = init: { 2771 if (zir_field_init != .none) { 2772 const uncoerced_field_init = try sema.resolveInst(zir_field_init); 2773 const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src); 2774 const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value }); 2775 if (field_init_val.canMutateComptimeVarState(zcu)) { 2776 const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); 2777 return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val); 2778 } 2779 break :init field_init_val.toIntern(); 2780 } 2781 if (try sema.typeHasOnePossibleValue(field_type)) |opv| { 2782 break :init opv.toIntern(); 2783 } 2784 break :init .none; 2785 }; 2786 } 2787 2788 return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{ 2789 .types = types, 2790 .values = inits, 2791 })); 2792 } 2793 2794 fn validateTupleFieldType( 2795 sema: *Sema, 2796 block: *Block, 2797 field_ty: Type, 2798 field_ty_src: LazySrcLoc, 2799 ) CompileError!void { 2800 const gpa = sema.gpa; 2801 const zcu = sema.pt.zcu; 2802 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 2803 return sema.failWithOwnedErrorMsg(block, msg: { 2804 const msg = try sema.errMsg(field_ty_src, "opaque types have unknown size and therefore cannot be directly embedded in tuples", .{}); 2805 errdefer msg.destroy(gpa); 2806 2807 try sema.addDeclaredHereNote(msg, field_ty); 2808 break :msg msg; 2809 }); 2810 } 2811 if (field_ty.zigTypeTag(zcu) == .noreturn) { 2812 return sema.failWithOwnedErrorMsg(block, msg: { 2813 const msg = try sema.errMsg(field_ty_src, "tuple fields cannot be 'noreturn'", .{}); 2814 errdefer msg.destroy(gpa); 2815 2816 try sema.addDeclaredHereNote(msg, field_ty); 2817 break :msg msg; 2818 }); 2819 } 2820 } 2821 2822 /// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`, 2823 /// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`. 2824 fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue { 2825 const pt = sema.pt; 2826 const zcu = pt.zcu; 2827 const ip = &zcu.intern_pool; 2828 const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type); 2829 const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu); 2830 2831 const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len); 2832 2833 for (sema.code.extra[extra_index..][0..captures_len], sema.code.extra[extra_index + captures_len ..][0..captures_len], captures) |raw, raw_name, *capture| { 2834 const zir_capture: Zir.Inst.Capture = @bitCast(raw); 2835 const zir_name: Zir.NullTerminatedString = @enumFromInt(raw_name); 2836 const zir_name_slice = sema.code.nullTerminatedString(zir_name); 2837 capture.* = switch (zir_capture.unwrap()) { 2838 .nested => |parent_idx| parent_captures.get(ip)[parent_idx], 2839 .instruction_load => |ptr_inst| InternPool.CaptureValue.wrap(capture: { 2840 const ptr_ref = try sema.resolveInst(ptr_inst.toRef()); 2841 const ptr_val = try sema.resolveValue(ptr_ref) orelse { 2842 break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }; 2843 }; 2844 // TODO: better source location 2845 const unresolved_loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse { 2846 break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }; 2847 }; 2848 const loaded_val = try sema.resolveLazyValue(unresolved_loaded_val); 2849 if (loaded_val.canMutateComptimeVarState(zcu)) { 2850 const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls); 2851 return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val); 2852 } 2853 break :capture .{ .@"comptime" = loaded_val.toIntern() }; 2854 }), 2855 .instruction => |inst| InternPool.CaptureValue.wrap(capture: { 2856 const air_ref = try sema.resolveInst(inst.toRef()); 2857 if (try sema.resolveValueResolveLazy(air_ref)) |val| { 2858 if (val.canMutateComptimeVarState(zcu)) { 2859 const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls); 2860 return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val); 2861 } 2862 break :capture .{ .@"comptime" = val.toIntern() }; 2863 } 2864 break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() }; 2865 }), 2866 .decl_val => |str| capture: { 2867 const decl_name = try ip.getOrPutString( 2868 sema.gpa, 2869 pt.tid, 2870 sema.code.nullTerminatedString(str), 2871 .no_embedded_nulls, 2872 ); 2873 const nav = try sema.lookupIdentifier(block, decl_name); 2874 break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav }); 2875 }, 2876 .decl_ref => |str| capture: { 2877 const decl_name = try ip.getOrPutString( 2878 sema.gpa, 2879 pt.tid, 2880 sema.code.nullTerminatedString(str), 2881 .no_embedded_nulls, 2882 ); 2883 const nav = try sema.lookupIdentifier(block, decl_name); 2884 break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav }); 2885 }, 2886 }; 2887 } 2888 2889 return captures; 2890 } 2891 2892 fn zirStructDecl( 2893 sema: *Sema, 2894 block: *Block, 2895 extended: Zir.Inst.Extended.InstData, 2896 inst: Zir.Inst.Index, 2897 ) CompileError!Air.Inst.Ref { 2898 const pt = sema.pt; 2899 const zcu = pt.zcu; 2900 const gpa = sema.gpa; 2901 const ip = &zcu.intern_pool; 2902 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 2903 const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand); 2904 2905 const tracked_inst = try block.trackZir(inst); 2906 const src: LazySrcLoc = .{ 2907 .base_node_inst = tracked_inst, 2908 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 2909 }; 2910 2911 var extra_index = extra.end; 2912 2913 const captures_len = if (small.has_captures_len) blk: { 2914 const captures_len = sema.code.extra[extra_index]; 2915 extra_index += 1; 2916 break :blk captures_len; 2917 } else 0; 2918 const fields_len = if (small.has_fields_len) blk: { 2919 const fields_len = sema.code.extra[extra_index]; 2920 extra_index += 1; 2921 break :blk fields_len; 2922 } else 0; 2923 const decls_len = if (small.has_decls_len) blk: { 2924 const decls_len = sema.code.extra[extra_index]; 2925 extra_index += 1; 2926 break :blk decls_len; 2927 } else 0; 2928 2929 const captures = try sema.getCaptures(block, src, extra_index, captures_len); 2930 extra_index += captures_len * 2; 2931 2932 if (small.has_backing_int) { 2933 const backing_int_body_len = sema.code.extra[extra_index]; 2934 extra_index += 1; // backing_int_body_len 2935 if (backing_int_body_len == 0) { 2936 extra_index += 1; // backing_int_ref 2937 } else { 2938 extra_index += backing_int_body_len; // backing_int_body_inst 2939 } 2940 } 2941 2942 const struct_init: InternPool.StructTypeInit = .{ 2943 .layout = small.layout, 2944 .fields_len = fields_len, 2945 .known_non_opv = small.known_non_opv, 2946 .requires_comptime = if (small.known_comptime_only) .yes else .unknown, 2947 .any_comptime_fields = small.any_comptime_fields, 2948 .any_default_inits = small.any_default_inits, 2949 .inits_resolved = false, 2950 .any_aligned_fields = small.any_aligned_fields, 2951 .key = .{ .declared = .{ 2952 .zir_index = tracked_inst, 2953 .captures = captures, 2954 } }, 2955 }; 2956 const wip_ty = switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) { 2957 .existing => |ty| { 2958 const new_ty = try pt.ensureTypeUpToDate(ty); 2959 2960 // Make sure we update the namespace if the declaration is re-analyzed, to pick 2961 // up on e.g. changed comptime decls. 2962 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); 2963 2964 try sema.declareDependency(.{ .interned = new_ty }); 2965 try sema.addTypeReferenceEntry(src, new_ty); 2966 return Air.internedToRef(new_ty); 2967 }, 2968 .wip => |wip| wip, 2969 }; 2970 errdefer wip_ty.cancel(ip, pt.tid); 2971 2972 const type_name = try sema.createTypeName( 2973 block, 2974 small.name_strategy, 2975 "struct", 2976 inst, 2977 wip_ty.index, 2978 ); 2979 wip_ty.setName(ip, type_name.name, type_name.nav); 2980 2981 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ 2982 .parent = block.namespace.toOptional(), 2983 .owner_type = wip_ty.index, 2984 .file_scope = block.getFileScopeIndex(zcu), 2985 .generation = zcu.generation, 2986 }); 2987 errdefer pt.destroyNamespace(new_namespace_index); 2988 2989 if (pt.zcu.comp.incremental) { 2990 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); 2991 } 2992 2993 const decls = sema.code.bodySlice(extra_index, decls_len); 2994 try pt.scanNamespace(new_namespace_index, decls); 2995 2996 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 2997 codegen_type: { 2998 if (zcu.comp.config.use_llvm) break :codegen_type; 2999 if (block.ownerModule().strip) break :codegen_type; 3000 // This job depends on any resolve_type_fully jobs queued up before it. 3001 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 3002 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 3003 } 3004 try sema.declareDependency(.{ .interned = wip_ty.index }); 3005 try sema.addTypeReferenceEntry(src, wip_ty.index); 3006 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 3007 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 3008 } 3009 3010 pub fn createTypeName( 3011 sema: *Sema, 3012 block: *Block, 3013 name_strategy: Zir.Inst.NameStrategy, 3014 anon_prefix: []const u8, 3015 inst: ?Zir.Inst.Index, 3016 /// This is used purely to give the type a unique name in the `anon` case. 3017 type_index: InternPool.Index, 3018 ) CompileError!struct { 3019 name: InternPool.NullTerminatedString, 3020 nav: InternPool.Nav.Index.Optional, 3021 } { 3022 const pt = sema.pt; 3023 const zcu = pt.zcu; 3024 const gpa = zcu.gpa; 3025 const ip = &zcu.intern_pool; 3026 3027 switch (name_strategy) { 3028 .anon => {}, // handled after switch 3029 .parent => return .{ 3030 .name = block.type_name_ctx, 3031 .nav = sema.owner.unwrap().nav_val.toOptional(), 3032 }, 3033 .func => func_strat: { 3034 const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail); 3035 const zir_tags = sema.code.instructions.items(.tag); 3036 3037 var aw: std.io.Writer.Allocating = .init(gpa); 3038 defer aw.deinit(); 3039 const w = &aw.writer; 3040 w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; 3041 3042 var arg_i: usize = 0; 3043 for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) { 3044 .param, .param_comptime, .param_anytype, .param_anytype_comptime => { 3045 const arg = sema.inst_map.get(zir_inst).?; 3046 // If this is being called in a generic function then analyzeCall will 3047 // have already resolved the args and this will work. 3048 // If not then this is a struct type being returned from a non-generic 3049 // function and the name doesn't matter since it will later 3050 // result in a compile error. 3051 const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat 3052 3053 if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory; 3054 3055 // Limiting the depth here helps avoid type names getting too long, which 3056 // in turn helps to avoid unreasonably long symbol names for namespaced 3057 // symbols. Such names should ideally be human-readable, and additionally, 3058 // some tooling may not support very long symbol names. 3059 w.print("{f}", .{Value.fmtValueSemaFull(.{ 3060 .val = arg_val, 3061 .pt = pt, 3062 .opt_sema = sema, 3063 .depth = 1, 3064 })}) catch return error.OutOfMemory; 3065 3066 arg_i += 1; 3067 continue; 3068 }, 3069 else => continue, 3070 }; 3071 3072 w.writeByte(')') catch return error.OutOfMemory; 3073 return .{ 3074 .name = try ip.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls), 3075 .nav = .none, 3076 }; 3077 }, 3078 .dbg_var => { 3079 // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions. 3080 const ref = inst.?.toRef(); 3081 const zir_tags = sema.code.instructions.items(.tag); 3082 const zir_data = sema.code.instructions.items(.data); 3083 for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) { 3084 .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) { 3085 return .{ 3086 .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}.{s}", .{ 3087 block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code), 3088 }, .no_embedded_nulls), 3089 .nav = .none, 3090 }; 3091 }, 3092 else => {}, 3093 }; 3094 // fall through to anon strat 3095 }, 3096 } 3097 3098 // anon strat handling 3099 3100 // It would be neat to have "struct:line:column" but this name has 3101 // to survive incremental updates, where it may have been shifted down 3102 // or up to a different line, but unchanged, and thus not unnecessarily 3103 // semantically analyzed. 3104 // TODO: that would be possible, by detecting line number changes and renaming 3105 // types appropriately. However, `@typeName` becomes a problem then. If we remove 3106 // that builtin from the language, we can consider this. 3107 3108 return .{ 3109 .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}__{s}_{d}", .{ 3110 block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index), 3111 }, .no_embedded_nulls), 3112 .nav = .none, 3113 }; 3114 } 3115 3116 fn zirEnumDecl( 3117 sema: *Sema, 3118 block: *Block, 3119 extended: Zir.Inst.Extended.InstData, 3120 inst: Zir.Inst.Index, 3121 ) CompileError!Air.Inst.Ref { 3122 const tracy = trace(@src()); 3123 defer tracy.end(); 3124 3125 const pt = sema.pt; 3126 const zcu = pt.zcu; 3127 const gpa = sema.gpa; 3128 const ip = &zcu.intern_pool; 3129 const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); 3130 const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand); 3131 var extra_index: usize = extra.end; 3132 3133 const tracked_inst = try block.trackZir(inst); 3134 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; 3135 3136 const tag_type_ref = if (small.has_tag_type) blk: { 3137 const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3138 extra_index += 1; 3139 break :blk tag_type_ref; 3140 } else .none; 3141 3142 const captures_len = if (small.has_captures_len) blk: { 3143 const captures_len = sema.code.extra[extra_index]; 3144 extra_index += 1; 3145 break :blk captures_len; 3146 } else 0; 3147 3148 const body_len = if (small.has_body_len) blk: { 3149 const body_len = sema.code.extra[extra_index]; 3150 extra_index += 1; 3151 break :blk body_len; 3152 } else 0; 3153 3154 const fields_len = if (small.has_fields_len) blk: { 3155 const fields_len = sema.code.extra[extra_index]; 3156 extra_index += 1; 3157 break :blk fields_len; 3158 } else 0; 3159 3160 const decls_len = if (small.has_decls_len) blk: { 3161 const decls_len = sema.code.extra[extra_index]; 3162 extra_index += 1; 3163 break :blk decls_len; 3164 } else 0; 3165 3166 const captures = try sema.getCaptures(block, src, extra_index, captures_len); 3167 extra_index += captures_len * 2; 3168 3169 const decls = sema.code.bodySlice(extra_index, decls_len); 3170 extra_index += decls_len; 3171 3172 const body = sema.code.bodySlice(extra_index, body_len); 3173 extra_index += body.len; 3174 3175 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; 3176 const body_end = extra_index; 3177 extra_index += bit_bags_count; 3178 3179 const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { 3180 if (bag != 0) break true; 3181 } else false; 3182 3183 const enum_init: InternPool.EnumTypeInit = .{ 3184 .has_values = any_values, 3185 .tag_mode = if (small.nonexhaustive) 3186 .nonexhaustive 3187 else if (tag_type_ref == .none) 3188 .auto 3189 else 3190 .explicit, 3191 .fields_len = fields_len, 3192 .key = .{ .declared = .{ 3193 .zir_index = tracked_inst, 3194 .captures = captures, 3195 } }, 3196 }; 3197 const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) { 3198 .existing => |ty| { 3199 const new_ty = try pt.ensureTypeUpToDate(ty); 3200 3201 // Make sure we update the namespace if the declaration is re-analyzed, to pick 3202 // up on e.g. changed comptime decls. 3203 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); 3204 3205 try sema.declareDependency(.{ .interned = new_ty }); 3206 try sema.addTypeReferenceEntry(src, new_ty); 3207 3208 // Since this is an enum, it has to be resolved immediately. 3209 // `ensureTypeUpToDate` has resolved the new type if necessary. 3210 // We just need to check for resolution failures. 3211 const ty_unit: AnalUnit = .wrap(.{ .type = new_ty }); 3212 if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) { 3213 return error.AnalysisFail; 3214 } 3215 3216 return Air.internedToRef(new_ty); 3217 }, 3218 .wip => |wip| wip, 3219 }; 3220 3221 // Once this is `true`, we will not delete the decl or type even upon failure, since we 3222 // have finished constructing the type and are in the process of analyzing it. 3223 var done = false; 3224 3225 errdefer if (!done) wip_ty.cancel(ip, pt.tid); 3226 3227 const type_name = try sema.createTypeName( 3228 block, 3229 small.name_strategy, 3230 "enum", 3231 inst, 3232 wip_ty.index, 3233 ); 3234 wip_ty.setName(ip, type_name.name, type_name.nav); 3235 3236 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ 3237 .parent = block.namespace.toOptional(), 3238 .owner_type = wip_ty.index, 3239 .file_scope = block.getFileScopeIndex(zcu), 3240 .generation = zcu.generation, 3241 }); 3242 errdefer if (!done) pt.destroyNamespace(new_namespace_index); 3243 3244 try pt.scanNamespace(new_namespace_index, decls); 3245 3246 try sema.declareDependency(.{ .interned = wip_ty.index }); 3247 try sema.addTypeReferenceEntry(src, wip_ty.index); 3248 3249 // We've finished the initial construction of this type, and are about to perform analysis. 3250 // Set the namespace appropriately, and don't destroy anything on failure. 3251 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 3252 wip_ty.prepare(ip, new_namespace_index); 3253 done = true; 3254 3255 try Sema.resolveDeclaredEnum( 3256 pt, 3257 wip_ty, 3258 inst, 3259 tracked_inst, 3260 new_namespace_index, 3261 type_name.name, 3262 small, 3263 body, 3264 tag_type_ref, 3265 any_values, 3266 fields_len, 3267 sema.code, 3268 body_end, 3269 ); 3270 3271 codegen_type: { 3272 if (zcu.comp.config.use_llvm) break :codegen_type; 3273 if (block.ownerModule().strip) break :codegen_type; 3274 // This job depends on any resolve_type_fully jobs queued up before it. 3275 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 3276 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 3277 } 3278 return Air.internedToRef(wip_ty.index); 3279 } 3280 3281 fn zirUnionDecl( 3282 sema: *Sema, 3283 block: *Block, 3284 extended: Zir.Inst.Extended.InstData, 3285 inst: Zir.Inst.Index, 3286 ) CompileError!Air.Inst.Ref { 3287 const tracy = trace(@src()); 3288 defer tracy.end(); 3289 3290 const pt = sema.pt; 3291 const zcu = pt.zcu; 3292 const gpa = sema.gpa; 3293 const ip = &zcu.intern_pool; 3294 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); 3295 const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand); 3296 var extra_index: usize = extra.end; 3297 3298 const tracked_inst = try block.trackZir(inst); 3299 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; 3300 3301 extra_index += @intFromBool(small.has_tag_type); 3302 const captures_len = if (small.has_captures_len) blk: { 3303 const captures_len = sema.code.extra[extra_index]; 3304 extra_index += 1; 3305 break :blk captures_len; 3306 } else 0; 3307 extra_index += @intFromBool(small.has_body_len); 3308 const fields_len = if (small.has_fields_len) blk: { 3309 const fields_len = sema.code.extra[extra_index]; 3310 extra_index += 1; 3311 break :blk fields_len; 3312 } else 0; 3313 3314 const decls_len = if (small.has_decls_len) blk: { 3315 const decls_len = sema.code.extra[extra_index]; 3316 extra_index += 1; 3317 break :blk decls_len; 3318 } else 0; 3319 3320 const captures = try sema.getCaptures(block, src, extra_index, captures_len); 3321 extra_index += captures_len * 2; 3322 3323 const union_init: InternPool.UnionTypeInit = .{ 3324 .flags = .{ 3325 .layout = small.layout, 3326 .status = .none, 3327 .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) 3328 .tagged 3329 else if (small.layout != .auto) 3330 .none 3331 else switch (block.wantSafeTypes()) { 3332 true => .safety, 3333 false => .none, 3334 }, 3335 .any_aligned_fields = small.any_aligned_fields, 3336 .requires_comptime = .unknown, 3337 .assumed_runtime_bits = false, 3338 .assumed_pointer_aligned = false, 3339 .alignment = .none, 3340 }, 3341 .fields_len = fields_len, 3342 .enum_tag_ty = .none, // set later 3343 .field_types = &.{}, // set later 3344 .field_aligns = &.{}, // set later 3345 .key = .{ .declared = .{ 3346 .zir_index = tracked_inst, 3347 .captures = captures, 3348 } }, 3349 }; 3350 const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) { 3351 .existing => |ty| { 3352 const new_ty = try pt.ensureTypeUpToDate(ty); 3353 3354 // Make sure we update the namespace if the declaration is re-analyzed, to pick 3355 // up on e.g. changed comptime decls. 3356 try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu)); 3357 3358 try sema.declareDependency(.{ .interned = new_ty }); 3359 try sema.addTypeReferenceEntry(src, new_ty); 3360 return Air.internedToRef(new_ty); 3361 }, 3362 .wip => |wip| wip, 3363 }; 3364 errdefer wip_ty.cancel(ip, pt.tid); 3365 3366 const type_name = try sema.createTypeName( 3367 block, 3368 small.name_strategy, 3369 "union", 3370 inst, 3371 wip_ty.index, 3372 ); 3373 wip_ty.setName(ip, type_name.name, type_name.nav); 3374 3375 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ 3376 .parent = block.namespace.toOptional(), 3377 .owner_type = wip_ty.index, 3378 .file_scope = block.getFileScopeIndex(zcu), 3379 .generation = zcu.generation, 3380 }); 3381 errdefer pt.destroyNamespace(new_namespace_index); 3382 3383 if (pt.zcu.comp.incremental) { 3384 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); 3385 } 3386 3387 const decls = sema.code.bodySlice(extra_index, decls_len); 3388 try pt.scanNamespace(new_namespace_index, decls); 3389 3390 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 3391 codegen_type: { 3392 if (zcu.comp.config.use_llvm) break :codegen_type; 3393 if (block.ownerModule().strip) break :codegen_type; 3394 // This job depends on any resolve_type_fully jobs queued up before it. 3395 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 3396 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 3397 } 3398 try sema.declareDependency(.{ .interned = wip_ty.index }); 3399 try sema.addTypeReferenceEntry(src, wip_ty.index); 3400 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 3401 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 3402 } 3403 3404 fn zirOpaqueDecl( 3405 sema: *Sema, 3406 block: *Block, 3407 extended: Zir.Inst.Extended.InstData, 3408 inst: Zir.Inst.Index, 3409 ) CompileError!Air.Inst.Ref { 3410 const tracy = trace(@src()); 3411 defer tracy.end(); 3412 3413 const pt = sema.pt; 3414 const zcu = pt.zcu; 3415 const gpa = sema.gpa; 3416 const ip = &zcu.intern_pool; 3417 3418 const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small); 3419 const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand); 3420 var extra_index: usize = extra.end; 3421 3422 const tracked_inst = try block.trackZir(inst); 3423 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; 3424 3425 const captures_len = if (small.has_captures_len) blk: { 3426 const captures_len = sema.code.extra[extra_index]; 3427 extra_index += 1; 3428 break :blk captures_len; 3429 } else 0; 3430 3431 const decls_len = if (small.has_decls_len) blk: { 3432 const decls_len = sema.code.extra[extra_index]; 3433 extra_index += 1; 3434 break :blk decls_len; 3435 } else 0; 3436 3437 const captures = try sema.getCaptures(block, src, extra_index, captures_len); 3438 extra_index += captures_len * 2; 3439 3440 const opaque_init: InternPool.OpaqueTypeInit = .{ 3441 .key = .{ .declared = .{ 3442 .zir_index = tracked_inst, 3443 .captures = captures, 3444 } }, 3445 }; 3446 const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) { 3447 .existing => |ty| { 3448 // Make sure we update the namespace if the declaration is re-analyzed, to pick 3449 // up on e.g. changed comptime decls. 3450 try pt.ensureNamespaceUpToDate(Type.fromInterned(ty).getNamespaceIndex(zcu)); 3451 3452 try sema.declareDependency(.{ .interned = ty }); 3453 try sema.addTypeReferenceEntry(src, ty); 3454 return Air.internedToRef(ty); 3455 }, 3456 .wip => |wip| wip, 3457 }; 3458 errdefer wip_ty.cancel(ip, pt.tid); 3459 3460 const type_name = try sema.createTypeName( 3461 block, 3462 small.name_strategy, 3463 "opaque", 3464 inst, 3465 wip_ty.index, 3466 ); 3467 wip_ty.setName(ip, type_name.name, type_name.nav); 3468 3469 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ 3470 .parent = block.namespace.toOptional(), 3471 .owner_type = wip_ty.index, 3472 .file_scope = block.getFileScopeIndex(zcu), 3473 .generation = zcu.generation, 3474 }); 3475 errdefer pt.destroyNamespace(new_namespace_index); 3476 3477 const decls = sema.code.bodySlice(extra_index, decls_len); 3478 try pt.scanNamespace(new_namespace_index, decls); 3479 3480 codegen_type: { 3481 if (zcu.comp.config.use_llvm) break :codegen_type; 3482 if (block.ownerModule().strip) break :codegen_type; 3483 // This job depends on any resolve_type_fully jobs queued up before it. 3484 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 3485 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 3486 } 3487 try sema.addTypeReferenceEntry(src, wip_ty.index); 3488 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 3489 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 3490 } 3491 3492 fn zirErrorSetDecl( 3493 sema: *Sema, 3494 inst: Zir.Inst.Index, 3495 ) CompileError!Air.Inst.Ref { 3496 const tracy = trace(@src()); 3497 defer tracy.end(); 3498 3499 const pt = sema.pt; 3500 const zcu = pt.zcu; 3501 const gpa = sema.gpa; 3502 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 3503 const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); 3504 3505 var names: InferredErrorSet.NameMap = .{}; 3506 try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len); 3507 3508 var extra_index: u32 = @intCast(extra.end); 3509 const extra_index_end = extra_index + extra.data.fields_len; 3510 while (extra_index < extra_index_end) : (extra_index += 1) { 3511 const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]); 3512 const name = sema.code.nullTerminatedString(name_index); 3513 const name_ip = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); 3514 _ = try pt.getErrorValue(name_ip); 3515 const result = names.getOrPutAssumeCapacity(name_ip); 3516 assert(!result.found_existing); // verified in AstGen 3517 } 3518 3519 return Air.internedToRef((try pt.errorSetFromUnsortedNames(names.keys())).toIntern()); 3520 } 3521 3522 fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3523 const tracy = trace(@src()); 3524 defer tracy.end(); 3525 3526 const pt = sema.pt; 3527 3528 const src = block.nodeOffset(sema.code.instructions.items(.data)[@intFromEnum(inst)].node); 3529 3530 if (block.isComptime() or try sema.fn_ret_ty.comptimeOnlySema(pt)) { 3531 try sema.fn_ret_ty.resolveFields(pt); 3532 return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none); 3533 } 3534 3535 const target = pt.zcu.getTarget(); 3536 const ptr_type = try pt.ptrTypeSema(.{ 3537 .child = sema.fn_ret_ty.toIntern(), 3538 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 3539 }); 3540 3541 if (block.inlining != null) { 3542 // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr. 3543 // TODO when functions gain result location support, the inlining struct in 3544 // Block should contain the return pointer, and we would pass that through here. 3545 return block.addTy(.alloc, ptr_type); 3546 } 3547 3548 return block.addTy(.ret_ptr, ptr_type); 3549 } 3550 3551 fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3552 const tracy = trace(@src()); 3553 defer tracy.end(); 3554 3555 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 3556 const operand = try sema.resolveInst(inst_data.operand); 3557 return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand); 3558 } 3559 3560 fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3561 const tracy = trace(@src()); 3562 defer tracy.end(); 3563 3564 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3565 const operand = try sema.resolveInst(inst_data.operand); 3566 const src = block.nodeOffset(inst_data.src_node); 3567 3568 return sema.ensureResultUsed(block, sema.typeOf(operand), src); 3569 } 3570 3571 fn ensureResultUsed( 3572 sema: *Sema, 3573 block: *Block, 3574 ty: Type, 3575 src: LazySrcLoc, 3576 ) CompileError!void { 3577 const pt = sema.pt; 3578 const zcu = pt.zcu; 3579 switch (ty.zigTypeTag(zcu)) { 3580 .void, .noreturn => return, 3581 .error_set => return sema.fail(block, src, "error set is ignored", .{}), 3582 .error_union => { 3583 const msg = msg: { 3584 const msg = try sema.errMsg(src, "error union is ignored", .{}); 3585 errdefer msg.destroy(sema.gpa); 3586 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 3587 break :msg msg; 3588 }; 3589 return sema.failWithOwnedErrorMsg(block, msg); 3590 }, 3591 else => { 3592 const msg = msg: { 3593 const msg = try sema.errMsg(src, "value of type '{f}' ignored", .{ty.fmt(pt)}); 3594 errdefer msg.destroy(sema.gpa); 3595 try sema.errNote(src, msg, "all non-void values must be used", .{}); 3596 try sema.errNote(src, msg, "to discard the value, assign it to '_'", .{}); 3597 break :msg msg; 3598 }; 3599 return sema.failWithOwnedErrorMsg(block, msg); 3600 }, 3601 } 3602 } 3603 3604 fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3605 const tracy = trace(@src()); 3606 defer tracy.end(); 3607 3608 const pt = sema.pt; 3609 const zcu = pt.zcu; 3610 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3611 const operand = try sema.resolveInst(inst_data.operand); 3612 const src = block.nodeOffset(inst_data.src_node); 3613 const operand_ty = sema.typeOf(operand); 3614 switch (operand_ty.zigTypeTag(zcu)) { 3615 .error_set => return sema.fail(block, src, "error set is discarded", .{}), 3616 .error_union => { 3617 const msg = msg: { 3618 const msg = try sema.errMsg(src, "error union is discarded", .{}); 3619 errdefer msg.destroy(sema.gpa); 3620 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 3621 break :msg msg; 3622 }; 3623 return sema.failWithOwnedErrorMsg(block, msg); 3624 }, 3625 else => return, 3626 } 3627 } 3628 3629 fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3630 const tracy = trace(@src()); 3631 defer tracy.end(); 3632 3633 const pt = sema.pt; 3634 const zcu = pt.zcu; 3635 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3636 const src = block.nodeOffset(inst_data.src_node); 3637 const operand = try sema.resolveInst(inst_data.operand); 3638 const operand_ty = sema.typeOf(operand); 3639 const err_union_ty = if (operand_ty.zigTypeTag(zcu) == .pointer) 3640 operand_ty.childType(zcu) 3641 else 3642 operand_ty; 3643 if (err_union_ty.zigTypeTag(zcu) != .error_union) return; 3644 const payload_ty = err_union_ty.errorUnionPayload(zcu).zigTypeTag(zcu); 3645 if (payload_ty != .void and payload_ty != .noreturn) { 3646 const msg = msg: { 3647 const msg = try sema.errMsg(src, "error union payload is ignored", .{}); 3648 errdefer msg.destroy(sema.gpa); 3649 try sema.errNote(src, msg, "payload value can be explicitly ignored with '|_|'", .{}); 3650 break :msg msg; 3651 }; 3652 return sema.failWithOwnedErrorMsg(block, msg); 3653 } 3654 } 3655 3656 fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3657 const tracy = trace(@src()); 3658 defer tracy.end(); 3659 3660 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3661 const src = block.nodeOffset(inst_data.src_node); 3662 const object = try sema.resolveInst(inst_data.operand); 3663 3664 return indexablePtrLen(sema, block, src, object); 3665 } 3666 3667 fn indexablePtrLen( 3668 sema: *Sema, 3669 block: *Block, 3670 src: LazySrcLoc, 3671 object: Air.Inst.Ref, 3672 ) CompileError!Air.Inst.Ref { 3673 const pt = sema.pt; 3674 const zcu = pt.zcu; 3675 const object_ty = sema.typeOf(object); 3676 const is_pointer_to = object_ty.isSinglePointer(zcu); 3677 const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty; 3678 try sema.checkIndexable(block, src, indexable_ty); 3679 const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls); 3680 return sema.fieldVal(block, src, object, field_name, src); 3681 } 3682 3683 fn indexablePtrLenOrNone( 3684 sema: *Sema, 3685 block: *Block, 3686 src: LazySrcLoc, 3687 operand: Air.Inst.Ref, 3688 ) CompileError!Air.Inst.Ref { 3689 const pt = sema.pt; 3690 const zcu = pt.zcu; 3691 const operand_ty = sema.typeOf(operand); 3692 try checkMemOperand(sema, block, src, operand_ty); 3693 switch (operand_ty.ptrSize(zcu)) { 3694 .many, .c => return .none, 3695 .one, .slice => {}, 3696 } 3697 const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls); 3698 return sema.fieldVal(block, src, operand, field_name, src); 3699 } 3700 3701 fn zirAllocExtended( 3702 sema: *Sema, 3703 block: *Block, 3704 extended: Zir.Inst.Extended.InstData, 3705 ) CompileError!Air.Inst.Ref { 3706 const pt = sema.pt; 3707 const gpa = sema.gpa; 3708 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); 3709 const ty_src = block.src(.{ .node_offset_var_decl_ty = extra.data.src_node }); 3710 const align_src = block.src(.{ .node_offset_var_decl_align = extra.data.src_node }); 3711 const init_src = block.src(.{ .node_offset_var_decl_init = extra.data.src_node }); 3712 const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small); 3713 3714 var extra_index: usize = extra.end; 3715 3716 const var_ty: Type = if (small.has_type) blk: { 3717 const type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3718 extra_index += 1; 3719 break :blk try sema.resolveType(block, ty_src, type_ref); 3720 } else undefined; 3721 3722 const alignment = if (small.has_align) blk: { 3723 const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3724 extra_index += 1; 3725 break :blk try sema.resolveAlign(block, align_src, align_ref); 3726 } else .none; 3727 3728 if (block.isComptime() or small.is_comptime) { 3729 if (small.has_type) { 3730 return sema.analyzeComptimeAlloc(block, init_src, var_ty, alignment); 3731 } else { 3732 try sema.air_instructions.append(gpa, .{ 3733 .tag = .inferred_alloc_comptime, 3734 .data = .{ .inferred_alloc_comptime = .{ 3735 .alignment = alignment, 3736 .is_const = small.is_const, 3737 .ptr = undefined, 3738 } }, 3739 }); 3740 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 3741 } 3742 } 3743 3744 if (small.has_type and try var_ty.comptimeOnlySema(pt)) { 3745 return sema.analyzeComptimeAlloc(block, init_src, var_ty, alignment); 3746 } 3747 3748 if (small.has_type) { 3749 if (!small.is_const) { 3750 try sema.validateVarType(block, ty_src, var_ty, false); 3751 } 3752 const target = pt.zcu.getTarget(); 3753 try var_ty.resolveLayout(pt); 3754 if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { 3755 const var_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node }); 3756 return sema.fail(block, var_src, "local variable in naked function", .{}); 3757 } 3758 const ptr_type = try sema.pt.ptrTypeSema(.{ 3759 .child = var_ty.toIntern(), 3760 .flags = .{ 3761 .alignment = alignment, 3762 .address_space = target_util.defaultAddressSpace(target, .local), 3763 }, 3764 }); 3765 const ptr = try block.addTy(.alloc, ptr_type); 3766 if (small.is_const) { 3767 const ptr_inst = ptr.toIndex().?; 3768 try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); 3769 try sema.base_allocs.put(gpa, ptr_inst, ptr_inst); 3770 } 3771 return ptr; 3772 } 3773 3774 const result_index = try block.addInstAsIndex(.{ 3775 .tag = .inferred_alloc, 3776 .data = .{ .inferred_alloc = .{ 3777 .alignment = alignment, 3778 .is_const = small.is_const, 3779 } }, 3780 }); 3781 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); 3782 if (small.is_const) { 3783 try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); 3784 try sema.base_allocs.put(gpa, result_index, result_index); 3785 } 3786 return result_index.toRef(); 3787 } 3788 3789 fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3790 const tracy = trace(@src()); 3791 defer tracy.end(); 3792 3793 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3794 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); 3795 const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node }); 3796 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 3797 return sema.analyzeComptimeAlloc(block, init_src, var_ty, .none); 3798 } 3799 3800 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3801 const pt = sema.pt; 3802 const zcu = pt.zcu; 3803 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3804 const alloc = try sema.resolveInst(inst_data.operand); 3805 const alloc_ty = sema.typeOf(alloc); 3806 const ptr_info = alloc_ty.ptrInfo(zcu); 3807 const elem_ty: Type = .fromInterned(ptr_info.child); 3808 3809 // If the alloc was created in a comptime scope, we already created a comptime alloc for it. 3810 // However, if the final constructed value does not reference comptime-mutable memory, we wish 3811 // to promote it to an anon decl. 3812 already_ct: { 3813 const ptr_val = try sema.resolveValue(alloc) orelse break :already_ct; 3814 3815 // If this was a comptime inferred alloc, then `storeToInferredAllocComptime` 3816 // might have already done our job and created an anon decl ref. 3817 switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) { 3818 .ptr => |ptr| switch (ptr.base_addr) { 3819 .uav => { 3820 // The comptime-ification was already done for us. 3821 // Just make sure the pointer is const. 3822 return sema.makePtrConst(block, alloc); 3823 }, 3824 else => {}, 3825 }, 3826 else => {}, 3827 } 3828 3829 if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct; 3830 const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; 3831 assert(ptr.byte_offset == 0); 3832 const alloc_index = ptr.base_addr.comptime_alloc; 3833 const ct_alloc = sema.getComptimeAlloc(alloc_index); 3834 const interned = try ct_alloc.val.intern(pt, sema.arena); 3835 if (interned.canMutateComptimeVarState(zcu)) { 3836 // Preserve the comptime alloc, just make the pointer const. 3837 ct_alloc.val = .{ .interned = interned.toIntern() }; 3838 ct_alloc.is_const = true; 3839 return sema.makePtrConst(block, alloc); 3840 } else { 3841 // Promote the constant to an anon decl. 3842 const new_mut_ptr = Air.internedToRef(try pt.intern(.{ .ptr = .{ 3843 .ty = alloc_ty.toIntern(), 3844 .base_addr = .{ .uav = .{ 3845 .val = interned.toIntern(), 3846 .orig_ty = alloc_ty.toIntern(), 3847 } }, 3848 .byte_offset = 0, 3849 } })); 3850 return sema.makePtrConst(block, new_mut_ptr); 3851 } 3852 } 3853 3854 // Otherwise, check if the alloc is comptime-known despite being in a runtime scope. 3855 if (try sema.resolveComptimeKnownAllocPtr(block, alloc, null)) |ptr_val| { 3856 return sema.makePtrConst(block, Air.internedToRef(ptr_val)); 3857 } 3858 3859 if (try elem_ty.comptimeOnlySema(pt)) { 3860 // The value was initialized through RLS, so we didn't detect the runtime condition earlier. 3861 // TODO: source location of runtime control flow 3862 const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node }); 3863 return sema.fail(block, init_src, "value with comptime-only type '{f}' depends on runtime control flow", .{elem_ty.fmt(pt)}); 3864 } 3865 3866 // This is a runtime value. 3867 return sema.makePtrConst(block, alloc); 3868 } 3869 3870 /// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved 3871 /// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`. 3872 fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index { 3873 const pt = sema.pt; 3874 const zcu = pt.zcu; 3875 3876 const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc); 3877 const ptr_info = alloc_ty.ptrInfo(zcu); 3878 const elem_ty: Type = .fromInterned(ptr_info.child); 3879 3880 const alloc_inst = alloc.toIndex() orelse return null; 3881 const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null; 3882 const stores = comptime_info.value.stores.items(.inst); 3883 3884 // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known. 3885 // We will resolve and return its value. 3886 3887 // We expect to have emitted at least one store, unless the elem type is OPV. 3888 if (stores.len == 0) { 3889 const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern(); 3890 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value); 3891 } 3892 3893 // In general, we want to create a comptime alloc of the correct type and 3894 // apply the stores to that alloc in order. However, before going to all 3895 // that effort, let's optimize for the common case of a single store. 3896 3897 simple: { 3898 if (stores.len != 1) break :simple; 3899 const store_inst = sema.air_instructions.get(@intFromEnum(stores[0])); 3900 switch (store_inst.tag) { 3901 .store, .store_safe => {}, 3902 .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => break :simple, // there's OPV stuff going on! 3903 else => unreachable, 3904 } 3905 if (store_inst.data.bin_op.lhs != alloc) break :simple; 3906 3907 const val = store_inst.data.bin_op.rhs.toInterned().?; 3908 assert(zcu.intern_pool.typeOf(val) == elem_ty.toIntern()); 3909 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value); 3910 } 3911 3912 // The simple strategy failed: we must create a mutable comptime alloc and 3913 // perform all of the runtime store operations at comptime. 3914 3915 const ct_alloc = try sema.newComptimeAlloc(block, .unneeded, elem_ty, ptr_info.flags.alignment); 3916 3917 const alloc_ptr = try pt.intern(.{ .ptr = .{ 3918 .ty = alloc_ty.toIntern(), 3919 .base_addr = .{ .comptime_alloc = ct_alloc }, 3920 .byte_offset = 0, 3921 } }); 3922 3923 // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the comptime alloc 3924 var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena); 3925 try ptr_mapping.ensureTotalCapacity(@intCast(stores.len)); 3926 ptr_mapping.putAssumeCapacity(alloc_inst, alloc_ptr); 3927 3928 // Whilst constructing our mapping, we will also initialize optional and error union payloads when 3929 // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters. 3930 var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len); 3931 for (stores) |store_inst_idx| { 3932 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx)); 3933 const ptr_to_map = switch (store_inst.tag) { 3934 .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to. 3935 .set_union_tag => continue, // Ignore for now; handled after we map pointers 3936 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself. 3937 else => unreachable, 3938 }; 3939 to_map.appendAssumeCapacity(ptr_to_map); 3940 } 3941 3942 const tmp_air = sema.getTmpAir(); 3943 3944 while (to_map.pop()) |air_ptr| { 3945 if (ptr_mapping.contains(air_ptr)) continue; 3946 const PointerMethod = union(enum) { 3947 same_addr, 3948 opt_payload, 3949 eu_payload, 3950 field: u32, 3951 elem: u64, 3952 }; 3953 const inst_tag = tmp_air.instructions.items(.tag)[@intFromEnum(air_ptr)]; 3954 const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) { 3955 .struct_field_ptr => blk: { 3956 const data = tmp_air.extraData( 3957 Air.StructField, 3958 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload, 3959 ).data; 3960 break :blk .{ 3961 data.struct_operand, 3962 .{ .field = data.field_index }, 3963 }; 3964 }, 3965 .struct_field_ptr_index_0, 3966 .struct_field_ptr_index_1, 3967 .struct_field_ptr_index_2, 3968 .struct_field_ptr_index_3, 3969 => .{ 3970 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3971 .{ .field = switch (inst_tag) { 3972 .struct_field_ptr_index_0 => 0, 3973 .struct_field_ptr_index_1 => 1, 3974 .struct_field_ptr_index_2 => 2, 3975 .struct_field_ptr_index_3 => 3, 3976 else => unreachable, 3977 } }, 3978 }, 3979 .ptr_slice_ptr_ptr => .{ 3980 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3981 .{ .field = Value.slice_ptr_index }, 3982 }, 3983 .ptr_slice_len_ptr => .{ 3984 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3985 .{ .field = Value.slice_len_index }, 3986 }, 3987 .ptr_elem_ptr => blk: { 3988 const data = tmp_air.extraData( 3989 Air.Bin, 3990 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload, 3991 ).data; 3992 const idx_val = (try sema.resolveValue(data.rhs)).?; 3993 break :blk .{ 3994 data.lhs, 3995 .{ .elem = try idx_val.toUnsignedIntSema(pt) }, 3996 }; 3997 }, 3998 .bitcast => .{ 3999 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4000 .same_addr, 4001 }, 4002 .optional_payload_ptr_set => .{ 4003 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4004 .opt_payload, 4005 }, 4006 .errunion_payload_ptr_set => .{ 4007 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 4008 .eu_payload, 4009 }, 4010 else => unreachable, 4011 }; 4012 4013 const decl_parent_ptr = ptr_mapping.get(air_parent_ptr.toIndex().?) orelse { 4014 // Resolve the parent pointer first. 4015 // Note that we add in what seems like the wrong order, because we're popping from the end of this array. 4016 try to_map.appendSlice(&.{ air_ptr, air_parent_ptr.toIndex().? }); 4017 continue; 4018 }; 4019 const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &zcu.intern_pool).toIntern(); 4020 const new_ptr = switch (method) { 4021 .same_addr => try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, decl_parent_ptr, new_ptr_ty), 4022 .opt_payload => ptr: { 4023 // Set the optional to non-null at comptime. 4024 // If the payload is OPV, we must use that value instead of undef. 4025 const opt_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); 4026 const payload_ty = opt_ty.optionalChild(zcu); 4027 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); 4028 const opt_val = try pt.intern(.{ .opt = .{ 4029 .ty = opt_ty.toIntern(), 4030 .val = payload_val.toIntern(), 4031 } }); 4032 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(opt_val), opt_ty); 4033 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrOptPayload(pt)).toIntern(); 4034 }, 4035 .eu_payload => ptr: { 4036 // Set the error union to non-error at comptime. 4037 // If the payload is OPV, we must use that value instead of undef. 4038 const eu_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); 4039 const payload_ty = eu_ty.errorUnionPayload(zcu); 4040 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); 4041 const eu_val = try pt.intern(.{ .error_union = .{ 4042 .ty = eu_ty.toIntern(), 4043 .val = .{ .payload = payload_val.toIntern() }, 4044 } }); 4045 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(eu_val), eu_ty); 4046 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrEuPayload(pt)).toIntern(); 4047 }, 4048 .field => |idx| ptr: { 4049 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); 4050 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| { 4051 // As this is a union field, we must store to the pointer now to set the tag. 4052 // If the payload is OPV, there will not be a payload store, so we store that value. 4053 // Otherwise, there will be a payload store to process later, so undef will suffice. 4054 const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]); 4055 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); 4056 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx); 4057 const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val); 4058 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), store_val, maybe_union_ty); 4059 } 4060 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern(); 4061 }, 4062 .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(), 4063 }; 4064 try ptr_mapping.put(air_ptr, new_ptr); 4065 } 4066 4067 // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime. 4068 // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set` 4069 // instructions were already done above. 4070 4071 for (stores) |store_inst_idx| { 4072 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx)); 4073 switch (store_inst.tag) { 4074 .optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above 4075 .set_union_tag => { 4076 // Usually, we can ignore these, because the creation of the field pointer above 4077 // already did it for us. However, if the field is OPV, this is relevant, because 4078 // there is not going to be a store to the field. So we must initialize the union 4079 // tag if the field is OPV. 4080 const union_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?; 4081 const union_ptr_val: Value = .fromInterned(ptr_mapping.get(union_ptr_inst).?); 4082 const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?); 4083 const union_ty = union_ptr_val.typeOf(zcu).childType(zcu); 4084 const field_ty = union_ty.unionFieldType(tag_val, zcu).?; 4085 if (try sema.typeHasOnePossibleValue(field_ty)) |payload_val| { 4086 const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val); 4087 try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty); 4088 } 4089 }, 4090 .store, .store_safe => { 4091 const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?; 4092 const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?; 4093 const new_ptr = ptr_mapping.get(air_ptr_inst).?; 4094 try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu)); 4095 }, 4096 else => unreachable, 4097 } 4098 } 4099 4100 // The value is finalized - load it! 4101 const val = (try sema.pointerDeref(block, LazySrcLoc.unneeded, Value.fromInterned(alloc_ptr), alloc_ty)).?.toIntern(); 4102 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, ct_alloc, alloc_inst, comptime_info.value); 4103 } 4104 4105 /// Given the resolved comptime-known value, rewrites the dead AIR to not 4106 /// create a runtime stack allocation. Also places the resulting value into 4107 /// either an anon decl ref or a comptime alloc depending on whether it 4108 /// references comptime-mutable memory. If `existing_comptime_alloc` is 4109 /// passed, it is a scratch allocation which already contains `result_val`. 4110 /// Same return type as `resolveComptimeKnownAllocPtr` so we can tail call. 4111 fn finishResolveComptimeKnownAllocPtr( 4112 sema: *Sema, 4113 block: *Block, 4114 alloc_ty: Type, 4115 result_val: InternPool.Index, 4116 existing_comptime_alloc: ?ComptimeAllocIndex, 4117 alloc_inst: Air.Inst.Index, 4118 comptime_info: MaybeComptimeAlloc, 4119 ) CompileError!?InternPool.Index { 4120 const pt = sema.pt; 4121 const zcu = pt.zcu; 4122 4123 // We're almost done - we have the resolved comptime value. We just need to 4124 // eliminate the now-dead runtime instructions. 4125 4126 // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to 4127 // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this 4128 // too because it doesn't really matter what they become. 4129 const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{ 4130 .ty = .fromIntern(alloc_ty.toIntern()), 4131 .operand = .zero_usize, 4132 } } }; 4133 4134 sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst); 4135 for (comptime_info.stores.items(.inst)) |store_inst| { 4136 sema.air_instructions.set(@intFromEnum(store_inst), nop_inst); 4137 } 4138 4139 if (Value.fromInterned(result_val).canMutateComptimeVarState(zcu)) { 4140 const alloc_index = existing_comptime_alloc orelse a: { 4141 const idx = try sema.newComptimeAlloc(block, .unneeded, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu)); 4142 const alloc = sema.getComptimeAlloc(idx); 4143 alloc.val = .{ .interned = result_val }; 4144 break :a idx; 4145 }; 4146 sema.getComptimeAlloc(alloc_index).is_const = true; 4147 return try pt.intern(.{ .ptr = .{ 4148 .ty = alloc_ty.toIntern(), 4149 .base_addr = .{ .comptime_alloc = alloc_index }, 4150 .byte_offset = 0, 4151 } }); 4152 } else { 4153 return try pt.intern(.{ .ptr = .{ 4154 .ty = alloc_ty.toIntern(), 4155 .base_addr = .{ .uav = .{ 4156 .orig_ty = alloc_ty.toIntern(), 4157 .val = result_val, 4158 } }, 4159 .byte_offset = 0, 4160 } }); 4161 } 4162 } 4163 4164 fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type { 4165 var ptr_info = ptr_ty.ptrInfo(sema.pt.zcu); 4166 ptr_info.flags.is_const = true; 4167 return sema.pt.ptrTypeSema(ptr_info); 4168 } 4169 4170 fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { 4171 const alloc_ty = sema.typeOf(alloc); 4172 const const_ptr_ty = try sema.makePtrTyConst(alloc_ty); 4173 4174 // Detect if a comptime value simply needs to have its type changed. 4175 if (try sema.resolveValue(alloc)) |val| { 4176 return Air.internedToRef((try sema.pt.getCoerced(val, const_ptr_ty)).toIntern()); 4177 } 4178 4179 return block.addBitCast(const_ptr_ty, alloc); 4180 } 4181 4182 fn zirAllocInferredComptime( 4183 sema: *Sema, 4184 is_const: bool, 4185 ) CompileError!Air.Inst.Ref { 4186 const gpa = sema.gpa; 4187 4188 try sema.air_instructions.append(gpa, .{ 4189 .tag = .inferred_alloc_comptime, 4190 .data = .{ .inferred_alloc_comptime = .{ 4191 .alignment = .none, 4192 .is_const = is_const, 4193 .ptr = undefined, 4194 } }, 4195 }); 4196 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 4197 } 4198 4199 fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4200 const tracy = trace(@src()); 4201 defer tracy.end(); 4202 4203 const pt = sema.pt; 4204 4205 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4206 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); 4207 const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node }); 4208 4209 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 4210 if (block.isComptime() or try var_ty.comptimeOnlySema(pt)) { 4211 return sema.analyzeComptimeAlloc(block, init_src, var_ty, .none); 4212 } 4213 if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { 4214 const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); 4215 return sema.fail(block, mut_src, "local variable in naked function", .{}); 4216 } 4217 const target = pt.zcu.getTarget(); 4218 const ptr_type = try pt.ptrTypeSema(.{ 4219 .child = var_ty.toIntern(), 4220 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 4221 }); 4222 const ptr = try block.addTy(.alloc, ptr_type); 4223 const ptr_inst = ptr.toIndex().?; 4224 try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); 4225 try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst); 4226 return ptr; 4227 } 4228 4229 fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4230 const tracy = trace(@src()); 4231 defer tracy.end(); 4232 4233 const pt = sema.pt; 4234 4235 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4236 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); 4237 const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node }); 4238 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 4239 if (block.isComptime()) { 4240 return sema.analyzeComptimeAlloc(block, init_src, var_ty, .none); 4241 } 4242 if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { 4243 const var_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); 4244 return sema.fail(block, var_src, "local variable in naked function", .{}); 4245 } 4246 try sema.validateVarType(block, ty_src, var_ty, false); 4247 const target = pt.zcu.getTarget(); 4248 const ptr_type = try pt.ptrTypeSema(.{ 4249 .child = var_ty.toIntern(), 4250 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 4251 }); 4252 return block.addTy(.alloc, ptr_type); 4253 } 4254 4255 fn zirAllocInferred( 4256 sema: *Sema, 4257 block: *Block, 4258 is_const: bool, 4259 ) CompileError!Air.Inst.Ref { 4260 const tracy = trace(@src()); 4261 defer tracy.end(); 4262 4263 const gpa = sema.gpa; 4264 4265 if (block.isComptime()) { 4266 try sema.air_instructions.append(gpa, .{ 4267 .tag = .inferred_alloc_comptime, 4268 .data = .{ .inferred_alloc_comptime = .{ 4269 .alignment = .none, 4270 .is_const = is_const, 4271 .ptr = undefined, 4272 } }, 4273 }); 4274 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 4275 } 4276 4277 const result_index = try block.addInstAsIndex(.{ 4278 .tag = .inferred_alloc, 4279 .data = .{ .inferred_alloc = .{ 4280 .alignment = .none, 4281 .is_const = is_const, 4282 } }, 4283 }); 4284 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); 4285 if (is_const) { 4286 try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); 4287 try sema.base_allocs.put(sema.gpa, result_index, result_index); 4288 } 4289 return result_index.toRef(); 4290 } 4291 4292 fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4293 const tracy = trace(@src()); 4294 defer tracy.end(); 4295 4296 const pt = sema.pt; 4297 const zcu = pt.zcu; 4298 const gpa = sema.gpa; 4299 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4300 const src = block.nodeOffset(inst_data.src_node); 4301 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); 4302 const ptr = try sema.resolveInst(inst_data.operand); 4303 const ptr_inst = ptr.toIndex().?; 4304 const target = zcu.getTarget(); 4305 4306 switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) { 4307 .inferred_alloc_comptime => { 4308 // The work was already done for us by `Sema.storeToInferredAllocComptime`. 4309 // All we need to do is return the pointer. 4310 const iac = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc_comptime; 4311 const resolved_ptr = iac.ptr; 4312 4313 if (std.debug.runtime_safety) { 4314 // The inferred_alloc_comptime should never be referenced again 4315 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ .tag = undefined, .data = undefined }); 4316 } 4317 4318 const val = switch (zcu.intern_pool.indexToKey(resolved_ptr).ptr.base_addr) { 4319 .uav => |a| a.val, 4320 .comptime_alloc => |i| val: { 4321 const alloc = sema.getComptimeAlloc(i); 4322 break :val (try alloc.val.intern(pt, sema.arena)).toIntern(); 4323 }, 4324 else => unreachable, 4325 }; 4326 if (zcu.intern_pool.isFuncBody(val)) { 4327 const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val)); 4328 if (try ty.fnHasRuntimeBitsSema(pt)) { 4329 try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = val })); 4330 try zcu.ensureFuncBodyAnalysisQueued(val); 4331 } 4332 } 4333 4334 return Air.internedToRef(resolved_ptr); 4335 }, 4336 .inferred_alloc => { 4337 const ia1 = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc; 4338 const ia2 = sema.unresolved_inferred_allocs.fetchSwapRemove(ptr_inst).?.value; 4339 const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len); 4340 for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| { 4341 assert(sema.air_instructions.items(.tag)[@intFromEnum(store_inst)] == .store); 4342 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 4343 peer_val.* = bin_op.rhs; 4344 } 4345 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none); 4346 4347 const final_ptr_ty = try pt.ptrTypeSema(.{ 4348 .child = final_elem_ty.toIntern(), 4349 .flags = .{ 4350 .alignment = ia1.alignment, 4351 .address_space = target_util.defaultAddressSpace(target, .local), 4352 }, 4353 }); 4354 4355 if (!ia1.is_const) { 4356 try sema.validateVarType(block, ty_src, final_elem_ty, false); 4357 } else if (try sema.resolveComptimeKnownAllocPtr(block, ptr, final_ptr_ty)) |ptr_val| { 4358 const const_ptr_ty = try sema.makePtrTyConst(final_ptr_ty); 4359 const new_const_ptr = try pt.getCoerced(Value.fromInterned(ptr_val), const_ptr_ty); 4360 4361 // Unless the block is comptime, `alloc_inferred` always produces 4362 // a runtime constant. The final inferred type needs to be 4363 // fully resolved so it can be lowered in codegen. 4364 try final_elem_ty.resolveFully(pt); 4365 4366 return Air.internedToRef(new_const_ptr.toIntern()); 4367 } 4368 4369 if (try final_elem_ty.comptimeOnlySema(pt)) { 4370 // The alloc wasn't comptime-known per the above logic, so the 4371 // type cannot be comptime-only. 4372 // TODO: source location of runtime control flow 4373 return sema.fail(block, src, "value with comptime-only type '{f}' depends on runtime control flow", .{final_elem_ty.fmt(pt)}); 4374 } 4375 if (sema.func_is_naked and try final_elem_ty.hasRuntimeBitsSema(pt)) { 4376 const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); 4377 return sema.fail(block, mut_src, "local variable in naked function", .{}); 4378 } 4379 // Change it to a normal alloc. 4380 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ 4381 .tag = .alloc, 4382 .data = .{ .ty = final_ptr_ty }, 4383 }); 4384 4385 // Now we need to go back over all the store instructions, and do the logic as if 4386 // the new result ptr type was available. 4387 4388 for (ia2.prongs.items) |placeholder_inst| { 4389 var replacement_block = block.makeSubBlock(); 4390 defer replacement_block.instructions.deinit(gpa); 4391 4392 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .store); 4393 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].bin_op; 4394 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store); 4395 4396 // If only one instruction is produced then we can replace the store 4397 // placeholder instruction with this instruction; no need for an entire block. 4398 if (replacement_block.instructions.items.len == 1) { 4399 const only_inst = replacement_block.instructions.items[0]; 4400 sema.air_instructions.set(@intFromEnum(placeholder_inst), sema.air_instructions.get(@intFromEnum(only_inst))); 4401 continue; 4402 } 4403 4404 // Here we replace the placeholder store instruction with a block 4405 // that does the actual store logic. 4406 _ = try replacement_block.addBr(placeholder_inst, .void_value); 4407 try sema.air_extra.ensureUnusedCapacity( 4408 gpa, 4409 @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len, 4410 ); 4411 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{ 4412 .tag = .block, 4413 .data = .{ .ty_pl = .{ 4414 .ty = .void_type, 4415 .payload = sema.addExtraAssumeCapacity(Air.Block{ 4416 .body_len = @intCast(replacement_block.instructions.items.len), 4417 }), 4418 } }, 4419 }); 4420 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); 4421 } 4422 4423 if (ia1.is_const) { 4424 return sema.makePtrConst(block, ptr); 4425 } else { 4426 return ptr; 4427 } 4428 }, 4429 else => unreachable, 4430 } 4431 } 4432 4433 fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4434 const pt = sema.pt; 4435 const zcu = pt.zcu; 4436 const gpa = sema.gpa; 4437 const ip = &zcu.intern_pool; 4438 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4439 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 4440 const all_args = sema.code.refSlice(extra.end, extra.data.operands_len); 4441 const arg_pairs: []const [2]Zir.Inst.Ref = @as([*]const [2]Zir.Inst.Ref, @ptrCast(all_args))[0..@divExact(all_args.len, 2)]; 4442 const src = block.nodeOffset(inst_data.src_node); 4443 4444 var len: Air.Inst.Ref = .none; 4445 var len_val: ?Value = null; 4446 var len_idx: u32 = undefined; 4447 var any_runtime = false; 4448 4449 const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, arg_pairs.len); 4450 defer gpa.free(runtime_arg_lens); 4451 4452 // First pass to look for comptime values. 4453 for (arg_pairs, 0..) |zir_arg_pair, i_usize| { 4454 const i: u32 = @intCast(i_usize); 4455 runtime_arg_lens[i] = .none; 4456 if (zir_arg_pair[0] == .none) continue; 4457 4458 const arg_src = block.src(.{ .for_input = .{ 4459 .for_node_offset = inst_data.src_node, 4460 .input_index = i, 4461 } }); 4462 4463 const arg_len_uncoerced = if (zir_arg_pair[1] == .none) l: { 4464 // This argument is an indexable. 4465 const object = try sema.resolveInst(zir_arg_pair[0]); 4466 const object_ty = sema.typeOf(object); 4467 if (!object_ty.isIndexable(zcu)) { 4468 // Instead of using checkIndexable we customize this error. 4469 const msg = msg: { 4470 const msg = try sema.errMsg(arg_src, "type '{f}' is not indexable and not a range", .{object_ty.fmt(pt)}); 4471 errdefer msg.destroy(sema.gpa); 4472 try sema.errNote(arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{}); 4473 4474 if (object_ty.zigTypeTag(zcu) == .error_union) { 4475 try sema.errNote(arg_src, msg, "consider using 'try', 'catch', or 'if'", .{}); 4476 } 4477 4478 break :msg msg; 4479 }; 4480 return sema.failWithOwnedErrorMsg(block, msg); 4481 } 4482 if (!object_ty.indexableHasLen(zcu)) continue; 4483 break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), arg_src); 4484 } else l: { 4485 // This argument is a range. 4486 const range_start = try sema.resolveInst(zir_arg_pair[0]); 4487 const range_end = try sema.resolveInst(zir_arg_pair[1]); 4488 break :l try sema.analyzeArithmetic(block, .sub, range_end, range_start, arg_src, arg_src, arg_src, true); 4489 }; 4490 const arg_len = try sema.coerce(block, .usize, arg_len_uncoerced, arg_src); 4491 if (len == .none) { 4492 len = arg_len; 4493 len_idx = i; 4494 } 4495 if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| { 4496 if (len_val) |v| { 4497 if (!(try sema.valuesEqual(arg_val, v, .usize))) { 4498 const msg = msg: { 4499 const msg = try sema.errMsg(src, "non-matching for loop lengths", .{}); 4500 errdefer msg.destroy(gpa); 4501 const a_src = block.src(.{ .for_input = .{ 4502 .for_node_offset = inst_data.src_node, 4503 .input_index = len_idx, 4504 } }); 4505 try sema.errNote(a_src, msg, "length {f} here", .{ 4506 v.fmtValueSema(pt, sema), 4507 }); 4508 try sema.errNote(arg_src, msg, "length {f} here", .{ 4509 arg_val.fmtValueSema(pt, sema), 4510 }); 4511 break :msg msg; 4512 }; 4513 return sema.failWithOwnedErrorMsg(block, msg); 4514 } 4515 } else { 4516 len = arg_len; 4517 len_val = arg_val; 4518 len_idx = i; 4519 } 4520 continue; 4521 } 4522 runtime_arg_lens[i] = arg_len; 4523 any_runtime = true; 4524 } 4525 4526 if (len == .none) { 4527 const msg = msg: { 4528 const msg = try sema.errMsg(src, "unbounded for loop", .{}); 4529 errdefer msg.destroy(gpa); 4530 for (arg_pairs, 0..) |zir_arg_pair, i_usize| { 4531 const i: u32 = @intCast(i_usize); 4532 if (zir_arg_pair[0] == .none) continue; 4533 if (zir_arg_pair[1] != .none) continue; 4534 const object = try sema.resolveInst(zir_arg_pair[0]); 4535 const object_ty = sema.typeOf(object); 4536 const arg_src = block.src(.{ .for_input = .{ 4537 .for_node_offset = inst_data.src_node, 4538 .input_index = i, 4539 } }); 4540 try sema.errNote(arg_src, msg, "type '{f}' has no upper bound", .{ 4541 object_ty.fmt(pt), 4542 }); 4543 } 4544 break :msg msg; 4545 }; 4546 return sema.failWithOwnedErrorMsg(block, msg); 4547 } 4548 4549 // Now for the runtime checks. 4550 if (any_runtime and block.wantSafety()) { 4551 for (runtime_arg_lens, 0..) |arg_len, i| { 4552 if (arg_len == .none) continue; 4553 if (i == len_idx) continue; 4554 const ok = try block.addBinOp(.cmp_eq, len, arg_len); 4555 try sema.addSafetyCheck(block, src, ok, .for_len_mismatch); 4556 } 4557 } 4558 4559 return len; 4560 } 4561 4562 /// Given any single pointer, retrieve a pointer to the payload of any optional 4563 /// or error union pointed to, initializing these pointers along the way. 4564 /// Given a `*E!?T`, returns a (valid) `*T`. 4565 /// May invalidate already-stored payload data. 4566 fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref { 4567 const pt = sema.pt; 4568 const zcu = pt.zcu; 4569 var base_ptr = ptr; 4570 while (true) switch (sema.typeOf(base_ptr).childType(zcu).zigTypeTag(zcu)) { 4571 .error_union => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), 4572 .optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), 4573 else => break, 4574 }; 4575 try sema.checkKnownAllocPtr(block, ptr, base_ptr); 4576 return base_ptr; 4577 } 4578 4579 fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4580 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4581 const ptr = try sema.resolveInst(un_node.operand); 4582 return sema.optEuBasePtrInit(block, ptr, block.nodeOffset(un_node.src_node)); 4583 } 4584 4585 fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4586 const pt = sema.pt; 4587 const zcu = pt.zcu; 4588 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4589 const src = block.nodeOffset(pl_node.src_node); 4590 const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; 4591 const uncoerced_val = try sema.resolveInst(extra.rhs); 4592 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.lhs) orelse return uncoerced_val; 4593 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); 4594 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction 4595 const elem_ty = ptr_ty.childType(zcu); 4596 switch (ptr_ty.ptrSize(zcu)) { 4597 .one => { 4598 const uncoerced_ty = sema.typeOf(uncoerced_val); 4599 if (elem_ty.zigTypeTag(zcu) == .array and elem_ty.childType(zcu).toIntern() == uncoerced_ty.toIntern()) { 4600 // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion. 4601 return uncoerced_val; 4602 } 4603 // If the destination type is anyopaque, don't coerce - the pointer will coerce instead. 4604 if (elem_ty.toIntern() == .anyopaque_type) { 4605 return uncoerced_val; 4606 } else { 4607 return sema.coerce(block, elem_ty, uncoerced_val, src); 4608 } 4609 }, 4610 .slice, .many => { 4611 // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`. 4612 const val_ty = sema.typeOf(uncoerced_val); 4613 switch (val_ty.zigTypeTag(zcu)) { 4614 .array, .vector => {}, 4615 else => if (!val_ty.isTuple(zcu)) { 4616 return sema.fail(block, src, "expected array of '{f}', found '{f}'", .{ elem_ty.fmt(pt), val_ty.fmt(pt) }); 4617 }, 4618 } 4619 const want_ty = try pt.arrayType(.{ 4620 .len = val_ty.arrayLen(zcu), 4621 .child = elem_ty.toIntern(), 4622 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 4623 }); 4624 return sema.coerce(block, want_ty, uncoerced_val, src); 4625 }, 4626 .c => { 4627 // There's nothing meaningful to do here, because we don't know if this is meant to be a 4628 // single-pointer or a many-pointer. 4629 return uncoerced_val; 4630 }, 4631 } 4632 } 4633 4634 fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { 4635 const pt = sema.pt; 4636 const zcu = pt.zcu; 4637 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4638 const src = block.nodeOffset(un_node.src_node); 4639 4640 const operand_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type; 4641 4642 const payload_ty = if (is_ref) ty: { 4643 if (!operand_ty.isSinglePointer(zcu)) { 4644 return .generic_poison_type; // we can't get a meaningful result type here, since it will be `*E![n]T`, and we don't know `n`. 4645 } 4646 break :ty operand_ty.childType(zcu); 4647 } else operand_ty; 4648 4649 const err_set_ty: Type = err_set: { 4650 // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals 4651 // until we hit an error union or set. 4652 var cur_ty = sema.fn_ret_ty; 4653 while (true) { 4654 switch (cur_ty.zigTypeTag(zcu)) { 4655 .error_set => break :err_set cur_ty, 4656 .error_union => break :err_set cur_ty.errorUnionSet(zcu), 4657 .optional => cur_ty = cur_ty.optionalChild(zcu), 4658 else => { 4659 // This function cannot return an error. 4660 // `try` is still valid if the error case is impossible, i.e. no error is returned. 4661 // So, the result type has an error set of `error{}`. 4662 break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(zcu.gpa, pt.tid, &.{})); 4663 }, 4664 } 4665 } 4666 }; 4667 4668 const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty); 4669 4670 if (is_ref) { 4671 var ptr_info = operand_ty.ptrInfo(zcu); 4672 ptr_info.child = eu_ty.toIntern(); 4673 const eu_ptr_ty = try pt.ptrTypeSema(ptr_info); 4674 return Air.internedToRef(eu_ptr_ty.toIntern()); 4675 } else { 4676 return Air.internedToRef(eu_ty.toIntern()); 4677 } 4678 } 4679 4680 fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 4681 const pt = sema.pt; 4682 const zcu = pt.zcu; 4683 const un_tok = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 4684 const src = block.tokenOffset(un_tok.src_tok); 4685 // In case of GenericPoison, we don't actually have a type, so this will be 4686 // treated as an untyped address-of operator. 4687 const ty_operand = try sema.resolveTypeOrPoison(block, src, un_tok.operand) orelse return; 4688 if (ty_operand.optEuBaseType(zcu).zigTypeTag(zcu) != .pointer) { 4689 return sema.failWithOwnedErrorMsg(block, msg: { 4690 const msg = try sema.errMsg(src, "expected type '{f}', found pointer", .{ty_operand.fmt(pt)}); 4691 errdefer msg.destroy(sema.gpa); 4692 try sema.errNote(src, msg, "address-of operator always returns a pointer", .{}); 4693 break :msg msg; 4694 }); 4695 } 4696 } 4697 4698 fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 4699 if (!block.isComptime()) return; 4700 4701 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4702 const src = block.nodeOffset(un_node.src_node); 4703 const init_ref = try sema.resolveInst(un_node.operand); 4704 if (!try sema.isComptimeKnown(init_ref)) { 4705 return sema.failWithNeededComptime(block, src, null); 4706 } 4707 } 4708 4709 fn zirValidateArrayInitRefTy( 4710 sema: *Sema, 4711 block: *Block, 4712 inst: Zir.Inst.Index, 4713 ) CompileError!Air.Inst.Ref { 4714 const pt = sema.pt; 4715 const zcu = pt.zcu; 4716 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4717 const src = block.nodeOffset(pl_node.src_node); 4718 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; 4719 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.ptr_ty) orelse return .generic_poison_type; 4720 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); 4721 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction 4722 switch (zcu.intern_pool.indexToKey(ptr_ty.toIntern())) { 4723 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 4724 .slice, .many => { 4725 // Use array of correct length 4726 const arr_ty = try pt.arrayType(.{ 4727 .len = extra.elem_count, 4728 .child = ptr_ty.childType(zcu).toIntern(), 4729 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 4730 }); 4731 return Air.internedToRef(arr_ty.toIntern()); 4732 }, 4733 else => {}, 4734 }, 4735 else => {}, 4736 } 4737 // Otherwise, we just want the pointer child type 4738 const ret_ty = ptr_ty.childType(zcu); 4739 if (ret_ty.toIntern() == .anyopaque_type) { 4740 // The actual array type is unknown, which we represent with a generic poison. 4741 return .generic_poison_type; 4742 } 4743 const arr_ty = ret_ty.optEuBaseType(zcu); 4744 try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty); 4745 return Air.internedToRef(ret_ty.toIntern()); 4746 } 4747 4748 fn zirValidateArrayInitTy( 4749 sema: *Sema, 4750 block: *Block, 4751 inst: Zir.Inst.Index, 4752 is_result_ty: bool, 4753 ) CompileError!void { 4754 const pt = sema.pt; 4755 const zcu = pt.zcu; 4756 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4757 const src = block.nodeOffset(inst_data.src_node); 4758 const ty_src: LazySrcLoc = if (is_result_ty) src else block.src(.{ .node_offset_init_ty = inst_data.src_node }); 4759 const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data; 4760 // It's okay for the type to be poison: this will result in an anonymous array init. 4761 const ty = try sema.resolveTypeOrPoison(block, ty_src, extra.ty) orelse return; 4762 const arr_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty; 4763 return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty); 4764 } 4765 4766 fn validateArrayInitTy( 4767 sema: *Sema, 4768 block: *Block, 4769 src: LazySrcLoc, 4770 ty_src: LazySrcLoc, 4771 init_count: u32, 4772 ty: Type, 4773 ) CompileError!void { 4774 const pt = sema.pt; 4775 const zcu = pt.zcu; 4776 switch (ty.zigTypeTag(zcu)) { 4777 .array => { 4778 const array_len = ty.arrayLen(zcu); 4779 if (init_count != array_len) { 4780 return sema.fail(block, src, "expected {d} array elements; found {d}", .{ 4781 array_len, init_count, 4782 }); 4783 } 4784 return; 4785 }, 4786 .vector => { 4787 const array_len = ty.arrayLen(zcu); 4788 if (init_count != array_len) { 4789 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{ 4790 array_len, init_count, 4791 }); 4792 } 4793 return; 4794 }, 4795 .@"struct" => if (ty.isTuple(zcu)) { 4796 try ty.resolveFields(pt); 4797 const array_len = ty.arrayLen(zcu); 4798 if (init_count > array_len) { 4799 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ 4800 array_len, init_count, 4801 }); 4802 } 4803 return; 4804 }, 4805 else => {}, 4806 } 4807 return sema.failWithArrayInitNotSupported(block, ty_src, ty); 4808 } 4809 4810 fn zirValidateStructInitTy( 4811 sema: *Sema, 4812 block: *Block, 4813 inst: Zir.Inst.Index, 4814 is_result_ty: bool, 4815 ) CompileError!void { 4816 const pt = sema.pt; 4817 const zcu = pt.zcu; 4818 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4819 const src = block.nodeOffset(inst_data.src_node); 4820 // It's okay for the type to be poison: this will result in an anonymous struct init. 4821 const ty = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return; 4822 const struct_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty; 4823 4824 switch (struct_ty.zigTypeTag(zcu)) { 4825 .@"struct", .@"union" => return, 4826 else => {}, 4827 } 4828 return sema.failWithStructInitNotSupported(block, src, struct_ty); 4829 } 4830 4831 fn zirValidatePtrStructInit( 4832 sema: *Sema, 4833 block: *Block, 4834 inst: Zir.Inst.Index, 4835 ) CompileError!void { 4836 const tracy = trace(@src()); 4837 defer tracy.end(); 4838 4839 const pt = sema.pt; 4840 const zcu = pt.zcu; 4841 const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4842 const init_src = block.nodeOffset(validate_inst.src_node); 4843 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); 4844 const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); 4845 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node; 4846 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4847 const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); 4848 const agg_ty = sema.typeOf(object_ptr).childType(zcu).optEuBaseType(zcu); 4849 switch (agg_ty.zigTypeTag(zcu)) { 4850 .@"struct" => return sema.validateStructInit( 4851 block, 4852 agg_ty, 4853 init_src, 4854 instrs, 4855 object_ptr, 4856 ), 4857 .@"union" => return sema.validateUnionInit( 4858 block, 4859 agg_ty, 4860 init_src, 4861 instrs, 4862 ), 4863 else => unreachable, 4864 } 4865 } 4866 4867 fn validateUnionInit( 4868 sema: *Sema, 4869 block: *Block, 4870 union_ty: Type, 4871 init_src: LazySrcLoc, 4872 instrs: []const Zir.Inst.Index, 4873 ) CompileError!void { 4874 if (instrs.len == 1) { 4875 // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`. 4876 return; 4877 } 4878 const msg = msg: { 4879 const msg = try sema.errMsg( 4880 init_src, 4881 "cannot initialize multiple union fields at once; unions can only have one active field", 4882 .{}, 4883 ); 4884 errdefer msg.destroy(sema.gpa); 4885 4886 for (instrs[1..]) |inst| { 4887 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4888 const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node }); 4889 try sema.errNote(inst_src, msg, "additional initializer here", .{}); 4890 } 4891 try sema.addDeclaredHereNote(msg, union_ty); 4892 break :msg msg; 4893 }; 4894 return sema.failWithOwnedErrorMsg(block, msg); 4895 } 4896 4897 fn validateStructInit( 4898 sema: *Sema, 4899 block: *Block, 4900 struct_ty: Type, 4901 init_src: LazySrcLoc, 4902 instrs: []const Zir.Inst.Index, 4903 struct_ptr: Air.Inst.Ref, 4904 ) CompileError!void { 4905 const pt = sema.pt; 4906 const zcu = pt.zcu; 4907 const gpa = sema.gpa; 4908 const ip = &zcu.intern_pool; 4909 4910 // Tracks whether each field was explicitly initialized. 4911 const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu)); 4912 defer gpa.free(found_fields); 4913 @memset(found_fields, false); 4914 4915 for (instrs) |field_ptr| { 4916 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node; 4917 const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node }); 4918 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4919 const field_name = try ip.getOrPutString( 4920 gpa, 4921 pt.tid, 4922 sema.code.nullTerminatedString(field_ptr_extra.field_name_start), 4923 .no_embedded_nulls, 4924 ); 4925 const field_index = if (struct_ty.isTuple(zcu)) 4926 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src) 4927 else 4928 try sema.structFieldIndex(block, struct_ty, field_name, field_src); 4929 assert(found_fields[field_index] == false); 4930 found_fields[field_index] = true; 4931 } 4932 4933 // Our job is simply to deal with default field values. Specifically, any field which was not 4934 // explicitly initialized must have its default value stored to the field pointer, or, if the 4935 // field has no default value, a compile error must be emitted instead. 4936 4937 // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However, 4938 // that work was actually all redundant: 4939 // 4940 // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing 4941 // the struct through RLS; there is no need to turn the field stores into one store. Comptime-known 4942 // consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends. 4943 // 4944 // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known. 4945 // But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the 4946 // field stores were to comptime-mutable pointers, so have already errored if not comptime-known. 4947 // 4948 // * If the value is runtime-known, then comptime-known fields must be validated as runtime values. 4949 // But this was already handled for every field store by the machinery in `checkComptimeKnownStore`. 4950 4951 var root_msg: ?*Zcu.ErrorMsg = null; 4952 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 4953 4954 for (found_fields, 0..) |explicit, i_usize| { 4955 if (explicit) continue; 4956 const i: u32 = @intCast(i_usize); 4957 4958 try struct_ty.resolveStructFieldInits(pt); 4959 const default_val = struct_ty.structFieldDefaultValue(i, zcu); 4960 if (default_val.toIntern() == .unreachable_value) { 4961 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse { 4962 const template = "missing tuple field with index {d}"; 4963 if (root_msg) |msg| { 4964 try sema.errNote(init_src, msg, template, .{i}); 4965 } else { 4966 root_msg = try sema.errMsg(init_src, template, .{i}); 4967 } 4968 continue; 4969 }; 4970 const template = "missing struct field: {f}"; 4971 const args = .{field_name.fmt(ip)}; 4972 if (root_msg) |msg| { 4973 try sema.errNote(init_src, msg, template, args); 4974 } else { 4975 root_msg = try sema.errMsg(init_src, template, args); 4976 } 4977 continue; 4978 } 4979 4980 const field_src = init_src; // TODO better source location 4981 const default_field_ptr = if (struct_ty.isTuple(zcu)) 4982 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) 4983 else 4984 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty); 4985 try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr); 4986 try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store); 4987 } 4988 4989 if (root_msg) |msg| { 4990 try sema.addDeclaredHereNote(msg, struct_ty); 4991 root_msg = null; 4992 return sema.failWithOwnedErrorMsg(block, msg); 4993 } 4994 } 4995 4996 fn zirValidatePtrArrayInit( 4997 sema: *Sema, 4998 block: *Block, 4999 inst: Zir.Inst.Index, 5000 ) CompileError!void { 5001 const pt = sema.pt; 5002 const zcu = pt.zcu; 5003 const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5004 const init_src = block.nodeOffset(validate_inst.src_node); 5005 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); 5006 const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); 5007 const first_elem_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node; 5008 const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; 5009 const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr); 5010 const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu); 5011 const array_len = array_ty.arrayLen(zcu); 5012 5013 // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR 5014 // to initialize them, or emitting a compile error if an unspecified field has no default. For 5015 // tuples, there are literally default field values, although they're guaranteed to be comptime 5016 // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never 5017 // specified so we always need to initialize here. For vectors, there's no such thing. 5018 5019 switch (array_ty.zigTypeTag(zcu)) { 5020 .@"struct" => if (instrs.len != array_len) { 5021 var root_msg: ?*Zcu.ErrorMsg = null; 5022 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 5023 5024 try array_ty.resolveStructFieldInits(pt); 5025 var i = instrs.len; 5026 while (i < array_len) : (i += 1) { 5027 const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern(); 5028 if (default_val == .unreachable_value) { 5029 const template = "missing tuple field with index {d}"; 5030 if (root_msg) |msg| { 5031 try sema.errNote(init_src, msg, template, .{i}); 5032 } else { 5033 root_msg = try sema.errMsg(init_src, template, .{i}); 5034 } 5035 continue; 5036 } 5037 } 5038 5039 if (root_msg) |msg| { 5040 root_msg = null; 5041 return sema.failWithOwnedErrorMsg(block, msg); 5042 } 5043 }, 5044 5045 .array => if (instrs.len != array_len) { 5046 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{ 5047 array_len, instrs.len, 5048 }); 5049 } else if (array_ty.sentinel(zcu)) |sentinel| { 5050 const array_len_ref = try pt.intRef(.usize, array_len); 5051 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true); 5052 try sema.checkKnownAllocPtr(block, array_ptr, sentinel_ptr); 5053 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, .fromValue(sentinel), init_src, .store); 5054 }, 5055 5056 .vector => if (instrs.len != array_len) { 5057 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{ 5058 array_len, instrs.len, 5059 }); 5060 }, 5061 5062 else => unreachable, 5063 } 5064 } 5065 5066 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5067 const pt = sema.pt; 5068 const zcu = pt.zcu; 5069 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5070 const src = block.nodeOffset(inst_data.src_node); 5071 const operand = try sema.resolveInst(inst_data.operand); 5072 const operand_ty = sema.typeOf(operand); 5073 5074 if (operand_ty.zigTypeTag(zcu) != .pointer) { 5075 return sema.fail(block, src, "cannot dereference non-pointer type '{f}'", .{operand_ty.fmt(pt)}); 5076 } else switch (operand_ty.ptrSize(zcu)) { 5077 .one, .c => {}, 5078 .many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{f}'", .{operand_ty.fmt(pt)}), 5079 .slice => return sema.fail(block, src, "index syntax required for slice type '{f}'", .{operand_ty.fmt(pt)}), 5080 } 5081 5082 if ((try sema.typeHasOnePossibleValue(operand_ty.childType(zcu))) != null) { 5083 // No need to validate the actual pointer value, we don't need it! 5084 return; 5085 } 5086 5087 const elem_ty = operand_ty.elemType2(zcu); 5088 if (try sema.resolveValue(operand)) |val| { 5089 if (val.isUndef(zcu)) { 5090 return sema.fail(block, src, "cannot dereference undefined value", .{}); 5091 } 5092 } else if (try elem_ty.comptimeOnlySema(pt)) { 5093 const msg = msg: { 5094 const msg = try sema.errMsg( 5095 src, 5096 "values of type '{f}' must be comptime-known, but operand value is runtime-known", 5097 .{elem_ty.fmt(pt)}, 5098 ); 5099 errdefer msg.destroy(sema.gpa); 5100 5101 try sema.explainWhyTypeIsComptime(msg, src, elem_ty); 5102 break :msg msg; 5103 }; 5104 return sema.failWithOwnedErrorMsg(block, msg); 5105 } 5106 } 5107 5108 fn typeIsDestructurable(ty: Type, zcu: *const Zcu) bool { 5109 return switch (ty.zigTypeTag(zcu)) { 5110 .array, .vector => true, 5111 .@"struct" => ty.isTuple(zcu), 5112 else => false, 5113 }; 5114 } 5115 5116 fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5117 const pt = sema.pt; 5118 const zcu = pt.zcu; 5119 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5120 const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data; 5121 const src = block.nodeOffset(inst_data.src_node); 5122 const destructure_src = block.nodeOffset(extra.destructure_node); 5123 const operand = try sema.resolveInst(extra.operand); 5124 const operand_ty = sema.typeOf(operand); 5125 5126 if (!typeIsDestructurable(operand_ty, zcu)) { 5127 return sema.failWithOwnedErrorMsg(block, msg: { 5128 const msg = try sema.errMsg(src, "type '{f}' cannot be destructured", .{operand_ty.fmt(pt)}); 5129 errdefer msg.destroy(sema.gpa); 5130 try sema.errNote(destructure_src, msg, "result destructured here", .{}); 5131 if (operand_ty.zigTypeTag(pt.zcu) == .error_union) { 5132 const base_op_ty = operand_ty.errorUnionPayload(zcu); 5133 if (typeIsDestructurable(base_op_ty, zcu)) 5134 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 5135 } 5136 break :msg msg; 5137 }); 5138 } 5139 5140 if (operand_ty.arrayLen(zcu) != extra.expect_len) { 5141 return sema.failWithOwnedErrorMsg(block, msg: { 5142 const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{ 5143 extra.expect_len, operand_ty.arrayLen(zcu), 5144 }); 5145 errdefer msg.destroy(sema.gpa); 5146 try sema.errNote(destructure_src, msg, "result destructured here", .{}); 5147 break :msg msg; 5148 }); 5149 } 5150 } 5151 5152 fn failWithBadMemberAccess( 5153 sema: *Sema, 5154 block: *Block, 5155 agg_ty: Type, 5156 field_src: LazySrcLoc, 5157 field_name: InternPool.NullTerminatedString, 5158 ) CompileError { 5159 const pt = sema.pt; 5160 const zcu = pt.zcu; 5161 const ip = &zcu.intern_pool; 5162 const kw_name = switch (agg_ty.zigTypeTag(zcu)) { 5163 .@"union" => "union", 5164 .@"struct" => "struct", 5165 .@"opaque" => "opaque", 5166 .@"enum" => "enum", 5167 else => unreachable, 5168 }; 5169 if (agg_ty.typeDeclInst(zcu)) |inst| if ((inst.resolve(ip) orelse return error.AnalysisFail) == .main_struct_inst) { 5170 return sema.fail(block, field_src, "root source file struct '{f}' has no member named '{f}'", .{ 5171 agg_ty.fmt(pt), field_name.fmt(ip), 5172 }); 5173 }; 5174 5175 return sema.fail(block, field_src, "{s} '{f}' has no member named '{f}'", .{ 5176 kw_name, agg_ty.fmt(pt), field_name.fmt(ip), 5177 }); 5178 } 5179 5180 fn failWithBadStructFieldAccess( 5181 sema: *Sema, 5182 block: *Block, 5183 struct_ty: Type, 5184 struct_type: InternPool.LoadedStructType, 5185 field_src: LazySrcLoc, 5186 field_name: InternPool.NullTerminatedString, 5187 ) CompileError { 5188 const pt = sema.pt; 5189 const zcu = pt.zcu; 5190 const ip = &zcu.intern_pool; 5191 5192 const msg = msg: { 5193 const msg = try sema.errMsg( 5194 field_src, 5195 "no field named '{f}' in struct '{f}'", 5196 .{ field_name.fmt(ip), struct_type.name.fmt(ip) }, 5197 ); 5198 errdefer msg.destroy(sema.gpa); 5199 try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{}); 5200 break :msg msg; 5201 }; 5202 return sema.failWithOwnedErrorMsg(block, msg); 5203 } 5204 5205 fn failWithBadUnionFieldAccess( 5206 sema: *Sema, 5207 block: *Block, 5208 union_ty: Type, 5209 union_obj: InternPool.LoadedUnionType, 5210 field_src: LazySrcLoc, 5211 field_name: InternPool.NullTerminatedString, 5212 ) CompileError { 5213 const pt = sema.pt; 5214 const zcu = pt.zcu; 5215 const ip = &zcu.intern_pool; 5216 const gpa = sema.gpa; 5217 5218 const msg = msg: { 5219 const msg = try sema.errMsg( 5220 field_src, 5221 "no field named '{f}' in union '{f}'", 5222 .{ field_name.fmt(ip), union_obj.name.fmt(ip) }, 5223 ); 5224 errdefer msg.destroy(gpa); 5225 try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{}); 5226 break :msg msg; 5227 }; 5228 return sema.failWithOwnedErrorMsg(block, msg); 5229 } 5230 5231 fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void { 5232 const zcu = sema.pt.zcu; 5233 const src_loc = decl_ty.srcLocOrNull(zcu) orelse return; 5234 const category = switch (decl_ty.zigTypeTag(zcu)) { 5235 .@"union" => "union", 5236 .@"struct" => "struct", 5237 .@"enum" => "enum", 5238 .@"opaque" => "opaque", 5239 else => unreachable, 5240 }; 5241 try sema.errNote(src_loc, parent, "{s} declared here", .{category}); 5242 } 5243 5244 fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5245 const tracy = trace(@src()); 5246 defer tracy.end(); 5247 5248 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5249 const src = block.nodeOffset(pl_node.src_node); 5250 const bin = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; 5251 const ptr = try sema.resolveInst(bin.lhs); 5252 const operand = try sema.resolveInst(bin.rhs); 5253 const ptr_inst = ptr.toIndex().?; 5254 const air_datas = sema.air_instructions.items(.data); 5255 5256 switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) { 5257 .inferred_alloc_comptime => { 5258 const iac = &air_datas[@intFromEnum(ptr_inst)].inferred_alloc_comptime; 5259 return sema.storeToInferredAllocComptime(block, src, operand, iac); 5260 }, 5261 .inferred_alloc => { 5262 const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; 5263 return sema.storeToInferredAlloc(block, src, ptr, operand, ia); 5264 }, 5265 else => unreachable, 5266 } 5267 } 5268 5269 fn storeToInferredAlloc( 5270 sema: *Sema, 5271 block: *Block, 5272 src: LazySrcLoc, 5273 ptr: Air.Inst.Ref, 5274 operand: Air.Inst.Ref, 5275 inferred_alloc: *InferredAlloc, 5276 ) CompileError!void { 5277 // Create a store instruction as a placeholder. This will be replaced by a 5278 // proper store sequence once we know the stored type. 5279 const dummy_store = try block.addBinOp(.store, ptr, operand); 5280 try sema.checkComptimeKnownStore(block, dummy_store, src); 5281 // Add the stored instruction to the set we will use to resolve peer types 5282 // for the inferred allocation. 5283 try inferred_alloc.prongs.append(sema.arena, dummy_store.toIndex().?); 5284 } 5285 5286 fn storeToInferredAllocComptime( 5287 sema: *Sema, 5288 block: *Block, 5289 src: LazySrcLoc, 5290 operand: Air.Inst.Ref, 5291 iac: *Air.Inst.Data.InferredAllocComptime, 5292 ) CompileError!void { 5293 const pt = sema.pt; 5294 const zcu = pt.zcu; 5295 const operand_ty = sema.typeOf(operand); 5296 // There will be only one store_to_inferred_ptr because we are running at comptime. 5297 // The alloc will turn into a Decl or a ComptimeAlloc. 5298 const operand_val = try sema.resolveValue(operand) orelse { 5299 return sema.failWithNeededComptime(block, src, .{ .simple = .stored_to_comptime_var }); 5300 }; 5301 const alloc_ty = try pt.ptrTypeSema(.{ 5302 .child = operand_ty.toIntern(), 5303 .flags = .{ 5304 .alignment = iac.alignment, 5305 .is_const = iac.is_const, 5306 }, 5307 }); 5308 if (iac.is_const and !operand_val.canMutateComptimeVarState(zcu)) { 5309 iac.ptr = try pt.intern(.{ .ptr = .{ 5310 .ty = alloc_ty.toIntern(), 5311 .base_addr = .{ .uav = .{ 5312 .val = operand_val.toIntern(), 5313 .orig_ty = alloc_ty.toIntern(), 5314 } }, 5315 .byte_offset = 0, 5316 } }); 5317 } else { 5318 const alloc_index = try sema.newComptimeAlloc(block, src, operand_ty, iac.alignment); 5319 sema.getComptimeAlloc(alloc_index).val = .{ .interned = operand_val.toIntern() }; 5320 iac.ptr = try pt.intern(.{ .ptr = .{ 5321 .ty = alloc_ty.toIntern(), 5322 .base_addr = .{ .comptime_alloc = alloc_index }, 5323 .byte_offset = 0, 5324 } }); 5325 } 5326 } 5327 5328 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5329 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5330 const src = block.nodeOffset(inst_data.src_node); 5331 const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, .u32, .{ .simple = .operand_setEvalBranchQuota })); 5332 sema.branch_quota = @max(sema.branch_quota, quota); 5333 sema.allow_memoize = false; 5334 } 5335 5336 fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5337 const tracy = trace(@src()); 5338 defer tracy.end(); 5339 5340 const zir_tags = sema.code.instructions.items(.tag); 5341 const zir_datas = sema.code.instructions.items(.data); 5342 const inst_data = zir_datas[@intFromEnum(inst)].pl_node; 5343 const src = block.nodeOffset(inst_data.src_node); 5344 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 5345 const ptr = try sema.resolveInst(extra.lhs); 5346 const operand = try sema.resolveInst(extra.rhs); 5347 5348 const is_ret = if (extra.lhs.toIndex()) |ptr_index| 5349 zir_tags[@intFromEnum(ptr_index)] == .ret_ptr 5350 else 5351 false; 5352 5353 const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); 5354 const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node }); 5355 const air_tag: Air.Inst.Tag = if (is_ret) 5356 .ret_ptr 5357 else if (block.wantSafety()) 5358 .store_safe 5359 else 5360 .store; 5361 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 5362 } 5363 5364 fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5365 const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code); 5366 return sema.addStrLit( 5367 try sema.pt.zcu.intern_pool.getOrPutString(sema.gpa, sema.pt.tid, bytes, .maybe_embedded_nulls), 5368 bytes.len, 5369 ); 5370 } 5371 5372 fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) CompileError!Air.Inst.Ref { 5373 return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool)); 5374 } 5375 5376 pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref { 5377 const pt = sema.pt; 5378 const array_ty = try pt.arrayType(.{ 5379 .len = len, 5380 .sentinel = .zero_u8, 5381 .child = .u8_type, 5382 }); 5383 const val = try pt.intern(.{ .aggregate = .{ 5384 .ty = array_ty.toIntern(), 5385 .storage = .{ .bytes = string }, 5386 } }); 5387 return sema.uavRef(val); 5388 } 5389 5390 fn uavRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref { 5391 return Air.internedToRef(try sema.pt.refValue(val)); 5392 } 5393 5394 fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5395 _ = block; 5396 const tracy = trace(@src()); 5397 defer tracy.end(); 5398 5399 const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].int; 5400 return sema.pt.intRef(.comptime_int, int); 5401 } 5402 5403 fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5404 _ = block; 5405 const tracy = trace(@src()); 5406 defer tracy.end(); 5407 5408 const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].str; 5409 const byte_count = int.len * @sizeOf(std.math.big.Limb); 5410 const limb_bytes = sema.code.string_bytes[@intFromEnum(int.start)..][0..byte_count]; 5411 5412 // TODO: this allocation and copy is only needed because the limbs may be unaligned. 5413 // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these 5414 // two lines can be removed. 5415 const limbs = try sema.arena.alloc(std.math.big.Limb, int.len); 5416 @memcpy(mem.sliceAsBytes(limbs), limb_bytes); 5417 5418 return Air.internedToRef((try sema.pt.intValue_big(.comptime_int, .{ 5419 .limbs = limbs, 5420 .positive = true, 5421 })).toIntern()); 5422 } 5423 5424 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5425 _ = block; 5426 const number = sema.code.instructions.items(.data)[@intFromEnum(inst)].float; 5427 return Air.internedToRef((try sema.pt.floatValue( 5428 .comptime_float, 5429 number, 5430 )).toIntern()); 5431 } 5432 5433 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5434 _ = block; 5435 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5436 const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data; 5437 const number = extra.get(); 5438 return Air.internedToRef((try sema.pt.floatValue(.comptime_float, number)).toIntern()); 5439 } 5440 5441 fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5442 const tracy = trace(@src()); 5443 defer tracy.end(); 5444 5445 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5446 const src = block.nodeOffset(inst_data.src_node); 5447 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 5448 const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .compile_error_string }); 5449 return sema.fail(block, src, "{s}", .{msg}); 5450 } 5451 5452 fn zirCompileLog( 5453 sema: *Sema, 5454 block: *Block, 5455 extended: Zir.Inst.Extended.InstData, 5456 ) CompileError!Air.Inst.Ref { 5457 const pt = sema.pt; 5458 const zcu = pt.zcu; 5459 const gpa = zcu.gpa; 5460 5461 var aw: std.io.Writer.Allocating = .init(gpa); 5462 defer aw.deinit(); 5463 const writer = &aw.writer; 5464 5465 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 5466 const src_node = extra.data.src_node; 5467 const args = sema.code.refSlice(extra.end, extended.small); 5468 5469 for (args, 0..) |arg_ref, i| { 5470 if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory; 5471 5472 const arg = try sema.resolveInst(arg_ref); 5473 const arg_ty = sema.typeOf(arg); 5474 if (try sema.resolveValueResolveLazy(arg)) |val| { 5475 writer.print("@as({f}, {f})", .{ 5476 arg_ty.fmt(pt), val.fmtValueSema(pt, sema), 5477 }) catch return error.OutOfMemory; 5478 } else { 5479 writer.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory; 5480 } 5481 } 5482 5483 const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls); 5484 5485 const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: { 5486 zcu.compile_log_lines.items[@intFromEnum(idx)] = .{ 5487 .next = .none, 5488 .data = line_data, 5489 }; 5490 break :idx idx; 5491 } else idx: { 5492 try zcu.compile_log_lines.append(gpa, .{ 5493 .next = .none, 5494 .data = line_data, 5495 }); 5496 break :idx @enumFromInt(zcu.compile_log_lines.items.len - 1); 5497 }; 5498 5499 const gop = try zcu.compile_logs.getOrPut(gpa, sema.owner); 5500 if (gop.found_existing) { 5501 const prev_line = gop.value_ptr.last_line.get(zcu); 5502 assert(prev_line.next == .none); 5503 prev_line.next = line_idx.toOptional(); 5504 gop.value_ptr.last_line = line_idx; 5505 } else { 5506 gop.value_ptr.* = .{ 5507 .base_node_inst = block.src_base_inst, 5508 .node_offset = src_node, 5509 .first_line = line_idx, 5510 .last_line = line_idx, 5511 }; 5512 } 5513 return .void_value; 5514 } 5515 5516 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5517 const pt = sema.pt; 5518 const zcu = pt.zcu; 5519 5520 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5521 const src = block.nodeOffset(inst_data.src_node); 5522 const msg_inst = try sema.resolveInst(inst_data.operand); 5523 5524 const coerced_msg = try sema.coerce(block, .slice_const_u8, msg_inst, block.builtinCallArgSrc(inst_data.src_node, 0)); 5525 5526 if (block.isComptime()) { 5527 return sema.fail(block, src, "encountered @panic at comptime", .{}); 5528 } 5529 5530 // We only apply the first hint in a branch. 5531 // This allows user-provided hints to override implicit cold hints. 5532 if (sema.branch_hint == null) { 5533 sema.branch_hint = .cold; 5534 } 5535 5536 if (!zcu.backendSupportsFeature(.panic_fn)) { 5537 _ = try block.addNoOp(.trap); 5538 return; 5539 } 5540 5541 try sema.ensureMemoizedStateResolved(src, .panic); 5542 try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.get(.@"panic.call")); 5543 5544 const panic_fn = Air.internedToRef(zcu.builtin_decl_values.get(.@"panic.call")); 5545 5546 const opt_usize_ty = try pt.optionalType(.usize_type); 5547 const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{ 5548 .ty = opt_usize_ty.toIntern(), 5549 .val = .none, 5550 } }))); 5551 try sema.callBuiltin(block, src, panic_fn, .auto, &.{ coerced_msg, null_ret_addr }, .@"@panic"); 5552 } 5553 5554 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5555 const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node; 5556 const src = block.nodeOffset(src_node); 5557 if (block.isComptime()) 5558 return sema.fail(block, src, "encountered @trap at comptime", .{}); 5559 _ = try block.addNoOp(.trap); 5560 } 5561 5562 fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5563 const tracy = trace(@src()); 5564 defer tracy.end(); 5565 5566 const pt = sema.pt; 5567 const zcu = pt.zcu; 5568 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5569 const src = parent_block.nodeOffset(inst_data.src_node); 5570 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 5571 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5572 const gpa = sema.gpa; 5573 5574 // AIR expects a block outside the loop block too. 5575 // Reserve space for a Loop instruction so that generated Break instructions can 5576 // point to it, even if it doesn't end up getting used because the code ends up being 5577 // comptime evaluated. 5578 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 5579 const loop_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1); 5580 try sema.air_instructions.ensureUnusedCapacity(gpa, 2); 5581 sema.air_instructions.appendAssumeCapacity(.{ 5582 .tag = .block, 5583 .data = undefined, 5584 }); 5585 sema.air_instructions.appendAssumeCapacity(.{ 5586 .tag = .loop, 5587 .data = .{ .ty_pl = .{ 5588 .ty = .noreturn_type, 5589 .payload = undefined, 5590 } }, 5591 }); 5592 var label: Block.Label = .{ 5593 .zir_block = inst, 5594 .merges = .{ 5595 .src_locs = .{}, 5596 .results = .{}, 5597 .br_list = .{}, 5598 .block_inst = block_inst, 5599 }, 5600 }; 5601 var child_block = parent_block.makeSubBlock(); 5602 child_block.label = &label; 5603 child_block.runtime_cond = null; 5604 child_block.runtime_loop = src; 5605 child_block.runtime_index.increment(); 5606 const merges = &child_block.label.?.merges; 5607 5608 defer child_block.instructions.deinit(gpa); 5609 defer merges.deinit(gpa); 5610 5611 var loop_block = child_block.makeSubBlock(); 5612 defer loop_block.instructions.deinit(gpa); 5613 5614 // Use `analyzeBodyInner` directly to push any comptime control flow up the stack. 5615 try sema.analyzeBodyInner(&loop_block, body); 5616 5617 // TODO: since AIR has `repeat` now, we could change ZIR to generate 5618 // more optimal code utilizing `repeat` instructions across blocks! 5619 // For now, if the generated loop body does not terminate `noreturn`, 5620 // then `analyzeBodyInner` is signalling that it ended with `repeat`. 5621 5622 const loop_block_len = loop_block.instructions.items.len; 5623 if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(zcu)) { 5624 // If the loop ended with a noreturn terminator, then there is no way for it to loop, 5625 // so we can just use the block instead. 5626 try child_block.instructions.appendSlice(gpa, loop_block.instructions.items); 5627 } else { 5628 _ = try loop_block.addInst(.{ 5629 .tag = .repeat, 5630 .data = .{ .repeat = .{ 5631 .loop_inst = loop_inst, 5632 } }, 5633 }); 5634 // Note that `loop_block_len` is now off by one. 5635 5636 try child_block.instructions.append(gpa, loop_inst); 5637 5638 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + loop_block_len + 1); 5639 sema.air_instructions.items(.data)[@intFromEnum(loop_inst)].ty_pl.payload = sema.addExtraAssumeCapacity( 5640 Air.Block{ .body_len = @intCast(loop_block_len + 1) }, 5641 ); 5642 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items)); 5643 } 5644 return sema.resolveAnalyzedBlock(parent_block, src, &child_block, merges, false); 5645 } 5646 5647 fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5648 const tracy = trace(@src()); 5649 defer tracy.end(); 5650 5651 const pt = sema.pt; 5652 const zcu = pt.zcu; 5653 const comp = zcu.comp; 5654 const gpa = sema.gpa; 5655 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5656 const src = parent_block.nodeOffset(pl_node.src_node); 5657 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 5658 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5659 5660 // we check this here to avoid undefined symbols 5661 if (!build_options.have_llvm) 5662 return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{}); 5663 5664 var c_import_buf = std.ArrayList(u8).init(gpa); 5665 defer c_import_buf.deinit(); 5666 5667 var child_block: Block = .{ 5668 .parent = parent_block, 5669 .sema = sema, 5670 .namespace = parent_block.namespace, 5671 .instructions = .{}, 5672 .inlining = parent_block.inlining, 5673 .comptime_reason = .{ .reason = .{ 5674 .src = src, 5675 .r = .{ .simple = .operand_cImport }, 5676 } }, 5677 .c_import_buf = &c_import_buf, 5678 .runtime_cond = parent_block.runtime_cond, 5679 .runtime_loop = parent_block.runtime_loop, 5680 .runtime_index = parent_block.runtime_index, 5681 .src_base_inst = parent_block.src_base_inst, 5682 .type_name_ctx = parent_block.type_name_ctx, 5683 }; 5684 defer child_block.instructions.deinit(gpa); 5685 5686 _ = try sema.analyzeInlineBody(&child_block, body, inst); 5687 5688 var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err| 5689 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5690 defer c_import_res.deinit(gpa); 5691 5692 if (c_import_res.errors.errorMessageCount() != 0) { 5693 const msg = msg: { 5694 const msg = try sema.errMsg(src, "C import failed", .{}); 5695 errdefer msg.destroy(gpa); 5696 5697 if (!comp.config.link_libc) 5698 try sema.errNote(src, msg, "libc headers not available; compilation does not link against libc", .{}); 5699 5700 const gop = try zcu.cimport_errors.getOrPut(gpa, sema.owner); 5701 if (!gop.found_existing) { 5702 gop.value_ptr.* = c_import_res.errors; 5703 c_import_res.errors = std.zig.ErrorBundle.empty; 5704 } 5705 break :msg msg; 5706 }; 5707 return sema.failWithOwnedErrorMsg(&child_block, msg); 5708 } 5709 const parent_mod = parent_block.ownerModule(); 5710 const digest = Cache.binToHex(c_import_res.digest); 5711 5712 const new_file_index = file: { 5713 const c_import_zig_path = try comp.arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ digest); 5714 const c_import_mod = Package.Module.create(comp.arena, .{ 5715 .paths = .{ 5716 .root = try .fromRoot(comp.arena, comp.dirs, .local_cache, c_import_zig_path), 5717 .root_src_path = "cimport.zig", 5718 }, 5719 .fully_qualified_name = c_import_zig_path, 5720 .cc_argv = parent_mod.cc_argv, 5721 .inherited = .{}, 5722 .global = comp.config, 5723 .parent = parent_mod, 5724 }) catch |err| switch (err) { 5725 // None of these are possible because we are creating a package with 5726 // the exact same configuration as the parent package, which already 5727 // passed these checks. 5728 error.ValgrindUnsupportedOnTarget => unreachable, 5729 error.TargetRequiresSingleThreaded => unreachable, 5730 error.BackendRequiresSingleThreaded => unreachable, 5731 error.TargetRequiresPic => unreachable, 5732 error.PieRequiresPic => unreachable, 5733 error.DynamicLinkingRequiresPic => unreachable, 5734 error.TargetHasNoRedZone => unreachable, 5735 error.StackCheckUnsupportedByTarget => unreachable, 5736 error.StackProtectorUnsupportedByTarget => unreachable, 5737 error.StackProtectorUnavailableWithoutLibC => unreachable, 5738 5739 else => |e| return e, 5740 }; 5741 const c_import_file_path: Compilation.Path = try c_import_mod.root.join(gpa, comp.dirs, "cimport.zig"); 5742 errdefer c_import_file_path.deinit(gpa); 5743 const c_import_file = try gpa.create(Zcu.File); 5744 errdefer gpa.destroy(c_import_file); 5745 const c_import_file_index = try zcu.intern_pool.createFile(gpa, pt.tid, .{ 5746 .bin_digest = c_import_file_path.digest(), 5747 .file = c_import_file, 5748 .root_type = .none, 5749 }); 5750 c_import_file.* = .{ 5751 .status = .never_loaded, 5752 .stat = undefined, 5753 .is_builtin = false, 5754 .path = c_import_file_path, 5755 .source = null, 5756 .tree = null, 5757 .zir = null, 5758 .zoir = null, 5759 .mod = c_import_mod, 5760 .sub_file_path = "cimport.zig", 5761 .module_changed = false, 5762 .prev_zir = null, 5763 .zoir_invalidated = false, 5764 }; 5765 break :file c_import_file_index; 5766 }; 5767 pt.updateFile(new_file_index, zcu.fileByIndex(new_file_index)) catch |err| 5768 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5769 5770 try pt.ensureFileAnalyzed(new_file_index); 5771 const ty = zcu.fileRootType(new_file_index); 5772 try sema.declareDependency(.{ .interned = ty }); 5773 try sema.addTypeReferenceEntry(src, ty); 5774 return Air.internedToRef(ty); 5775 } 5776 5777 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5778 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5779 const src = parent_block.nodeOffset(inst_data.src_node); 5780 return sema.failWithUseOfAsync(parent_block, src); 5781 } 5782 5783 fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5784 const tracy = trace(@src()); 5785 defer tracy.end(); 5786 5787 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5788 const src = parent_block.nodeOffset(pl_node.src_node); 5789 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 5790 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5791 const gpa = sema.gpa; 5792 5793 // Reserve space for a Block instruction so that generated Break instructions can 5794 // point to it, even if it doesn't end up getting used because the code ends up being 5795 // comptime evaluated or is an unlabeled block. 5796 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 5797 try sema.air_instructions.append(gpa, .{ 5798 .tag = .block, 5799 .data = undefined, 5800 }); 5801 5802 var label: Block.Label = .{ 5803 .zir_block = inst, 5804 .merges = .{ 5805 .src_locs = .{}, 5806 .results = .{}, 5807 .br_list = .{}, 5808 .block_inst = block_inst, 5809 }, 5810 }; 5811 5812 var child_block: Block = .{ 5813 .parent = parent_block, 5814 .sema = sema, 5815 .namespace = parent_block.namespace, 5816 .instructions = .{}, 5817 .label = &label, 5818 .inlining = parent_block.inlining, 5819 .comptime_reason = parent_block.comptime_reason, 5820 .is_typeof = parent_block.is_typeof, 5821 .want_safety = parent_block.want_safety, 5822 .float_mode = parent_block.float_mode, 5823 .c_import_buf = parent_block.c_import_buf, 5824 .runtime_cond = parent_block.runtime_cond, 5825 .runtime_loop = parent_block.runtime_loop, 5826 .runtime_index = parent_block.runtime_index, 5827 .error_return_trace_index = parent_block.error_return_trace_index, 5828 .src_base_inst = parent_block.src_base_inst, 5829 .type_name_ctx = parent_block.type_name_ctx, 5830 }; 5831 5832 defer child_block.instructions.deinit(gpa); 5833 defer label.merges.deinit(gpa); 5834 5835 return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges); 5836 } 5837 5838 /// Semantically analyze the given ZIR body, emitting any resulting runtime code into the AIR block 5839 /// specified by `child_block` if necessary (and emitting this block into `parent_block`). 5840 /// TODO: `merges` is known from `child_block`, remove this parameter. 5841 fn resolveBlockBody( 5842 sema: *Sema, 5843 parent_block: *Block, 5844 src: LazySrcLoc, 5845 child_block: *Block, 5846 body: []const Zir.Inst.Index, 5847 /// This is the instruction that a break instruction within `body` can 5848 /// use to return from the body. 5849 body_inst: Zir.Inst.Index, 5850 merges: *Block.Merges, 5851 ) CompileError!Air.Inst.Ref { 5852 if (child_block.isComptime()) { 5853 return sema.resolveInlineBody(child_block, body, body_inst); 5854 } else { 5855 assert(sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)] == .block); 5856 var need_debug_scope = false; 5857 child_block.need_debug_scope = &need_debug_scope; 5858 if (sema.analyzeBodyInner(child_block, body)) |_| { 5859 return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope); 5860 } else |err| switch (err) { 5861 error.ComptimeBreak => { 5862 // Comptime control flow is happening, however child_block may still contain 5863 // runtime instructions which need to be copied to the parent block. 5864 if (need_debug_scope and child_block.instructions.items.len > 0) { 5865 // We need a runtime block for scoping reasons. 5866 _ = try child_block.addBr(merges.block_inst, .void_value); 5867 try parent_block.instructions.append(sema.gpa, merges.block_inst); 5868 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).@"struct".fields.len + 5869 child_block.instructions.items.len); 5870 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 5871 .ty = .void_type, 5872 .payload = sema.addExtraAssumeCapacity(Air.Block{ 5873 .body_len = @intCast(child_block.instructions.items.len), 5874 }), 5875 } }; 5876 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 5877 } else { 5878 // We can copy instructions directly to the parent block. 5879 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items); 5880 } 5881 5882 const break_inst = sema.comptime_break_inst; 5883 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; 5884 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 5885 if (extra.block_inst == body_inst) { 5886 return try sema.resolveInst(break_data.operand); 5887 } else { 5888 return error.ComptimeBreak; 5889 } 5890 }, 5891 else => |e| return e, 5892 } 5893 } 5894 } 5895 5896 /// After a body corresponding to an AIR `block` has been analyzed, this function places them into 5897 /// the block pointed at by `merges.block_inst` if necessary, or the block may be elided in favor of 5898 /// inlining the instructions directly into the parent block. Either way, it considers all merges of 5899 /// this block, and combines them appropriately using peer type resolution, returning the final 5900 /// value of the block. 5901 fn resolveAnalyzedBlock( 5902 sema: *Sema, 5903 parent_block: *Block, 5904 src: LazySrcLoc, 5905 child_block: *Block, 5906 merges: *Block.Merges, 5907 need_debug_scope: bool, 5908 ) CompileError!Air.Inst.Ref { 5909 const tracy = trace(@src()); 5910 defer tracy.end(); 5911 5912 const gpa = sema.gpa; 5913 const pt = sema.pt; 5914 const zcu = pt.zcu; 5915 5916 // Blocks must terminate with noreturn instruction. 5917 assert(child_block.instructions.items.len != 0); 5918 assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(zcu)); 5919 5920 const block_tag = sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)]; 5921 switch (block_tag) { 5922 .block => {}, 5923 .dbg_inline_block => assert(need_debug_scope), 5924 else => unreachable, 5925 } 5926 if (merges.results.items.len == 0) { 5927 switch (block_tag) { 5928 .block => { 5929 // No need for a block instruction. We can put the new instructions 5930 // directly into the parent block. 5931 if (need_debug_scope) { 5932 // The code following this block is unreachable, as the block has no 5933 // merges, so we don't necessarily need to emit this as an AIR block. 5934 // However, we need a block *somewhere* to make the scoping correct, 5935 // so forward this request to the parent block. 5936 if (parent_block.need_debug_scope) |ptr| ptr.* = true; 5937 } 5938 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items); 5939 return child_block.instructions.items[child_block.instructions.items.len - 1].toRef(); 5940 }, 5941 .dbg_inline_block => { 5942 // Create a block containing all instruction from the body. 5943 try parent_block.instructions.append(gpa, merges.block_inst); 5944 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len + 5945 child_block.instructions.items.len); 5946 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 5947 .ty = .noreturn_type, 5948 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ 5949 .func = child_block.inlining.?.func, 5950 .body_len = @intCast(child_block.instructions.items.len), 5951 }), 5952 } }; 5953 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 5954 return merges.block_inst.toRef(); 5955 }, 5956 else => unreachable, 5957 } 5958 } 5959 if (merges.results.items.len == 1) { 5960 // If the `break` is trailing, we may be able to elide the AIR block here 5961 // by appending the new instructions directly to the parent block. 5962 if (!need_debug_scope) { 5963 const last_inst_index = child_block.instructions.items.len - 1; 5964 const last_inst = child_block.instructions.items[last_inst_index]; 5965 if (sema.getBreakBlock(last_inst)) |br_block| { 5966 if (br_block == merges.block_inst) { 5967 // Great, the last instruction is the break! Put the instructions 5968 // directly into the parent block. 5969 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items[0..last_inst_index]); 5970 return merges.results.items[0]; 5971 } 5972 } 5973 } 5974 // Okay, we need a runtime block. If the value is comptime-known, the 5975 // block should just return void, and we return the merge result 5976 // directly. Otherwise, we can defer to the logic below. 5977 if (try sema.resolveValue(merges.results.items[0])) |result_val| { 5978 // Create a block containing all instruction from the body. 5979 try parent_block.instructions.append(gpa, merges.block_inst); 5980 switch (block_tag) { 5981 .block => { 5982 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 5983 child_block.instructions.items.len); 5984 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 5985 .ty = .void_type, 5986 .payload = sema.addExtraAssumeCapacity(Air.Block{ 5987 .body_len = @intCast(child_block.instructions.items.len), 5988 }), 5989 } }; 5990 }, 5991 .dbg_inline_block => { 5992 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len + 5993 child_block.instructions.items.len); 5994 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 5995 .ty = .void_type, 5996 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ 5997 .func = child_block.inlining.?.func, 5998 .body_len = @intCast(child_block.instructions.items.len), 5999 }), 6000 } }; 6001 }, 6002 else => unreachable, 6003 } 6004 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 6005 // Rewrite the break to just give value {}; the value is 6006 // comptime-known and will be returned directly. 6007 sema.air_instructions.items(.data)[@intFromEnum(merges.br_list.items[0])].br.operand = .void_value; 6008 return Air.internedToRef(result_val.toIntern()); 6009 } 6010 } 6011 // It is impossible to have the number of results be > 1 in a comptime scope. 6012 assert(!child_block.isComptime()); // Should already got a compile error in the condbr condition. 6013 6014 // Note that we'll always create an AIR block here, so `need_debug_scope` is irrelevant. 6015 6016 // Need to set the type and emit the Block instruction. This allows machine code generation 6017 // to emit a jump instruction to after the block when it encounters the break. 6018 try parent_block.instructions.append(gpa, merges.block_inst); 6019 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items }); 6020 // TODO add note "missing else causes void value" 6021 6022 const type_src = src; // TODO: better source location 6023 if (try resolved_ty.comptimeOnlySema(pt)) { 6024 const msg = msg: { 6025 const msg = try sema.errMsg(type_src, "value with comptime-only type '{f}' depends on runtime control flow", .{resolved_ty.fmt(pt)}); 6026 errdefer msg.destroy(sema.gpa); 6027 6028 const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?; 6029 try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); 6030 6031 try sema.explainWhyTypeIsComptime(msg, type_src, resolved_ty); 6032 6033 break :msg msg; 6034 }; 6035 return sema.failWithOwnedErrorMsg(child_block, msg); 6036 } 6037 for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| { 6038 try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst); 6039 } 6040 6041 try sema.checkMergeAllowed(child_block, type_src, resolved_ty); 6042 6043 const ty_inst = Air.internedToRef(resolved_ty.toIntern()); 6044 switch (block_tag) { 6045 .block => { 6046 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 6047 child_block.instructions.items.len); 6048 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 6049 .ty = ty_inst, 6050 .payload = sema.addExtraAssumeCapacity(Air.Block{ 6051 .body_len = @intCast(child_block.instructions.items.len), 6052 }), 6053 } }; 6054 }, 6055 .dbg_inline_block => { 6056 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len + 6057 child_block.instructions.items.len); 6058 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 6059 .ty = ty_inst, 6060 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ 6061 .func = child_block.inlining.?.func, 6062 .body_len = @intCast(child_block.instructions.items.len), 6063 }), 6064 } }; 6065 }, 6066 else => unreachable, 6067 } 6068 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 6069 // Now that the block has its type resolved, we need to go back into all the break 6070 // instructions, and insert type coercion on the operands. 6071 for (merges.br_list.items) |br| { 6072 const br_operand = sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand; 6073 const br_operand_src = src; 6074 const br_operand_ty = sema.typeOf(br_operand); 6075 if (br_operand_ty.eql(resolved_ty, zcu)) { 6076 // No type coercion needed. 6077 continue; 6078 } 6079 var coerce_block = parent_block.makeSubBlock(); 6080 defer coerce_block.instructions.deinit(gpa); 6081 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src); 6082 // If no instructions were produced, such as in the case of a coercion of a 6083 // constant value to a new type, we can simply point the br operand to it. 6084 if (coerce_block.instructions.items.len == 0) { 6085 sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand = coerced_operand; 6086 continue; 6087 } 6088 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1].toRef() == coerced_operand); 6089 6090 // Convert the br instruction to a block instruction that has the coercion 6091 // and then a new br inside that returns the coerced instruction. 6092 const sub_block_len: u32 = @intCast(coerce_block.instructions.items.len + 1); 6093 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 6094 sub_block_len); 6095 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 6096 const sub_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 6097 6098 sema.air_instructions.items(.tag)[@intFromEnum(br)] = .block; 6099 sema.air_instructions.items(.data)[@intFromEnum(br)] = .{ .ty_pl = .{ 6100 .ty = .noreturn_type, 6101 .payload = sema.addExtraAssumeCapacity(Air.Block{ 6102 .body_len = sub_block_len, 6103 }), 6104 } }; 6105 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); 6106 sema.air_extra.appendAssumeCapacity(@intFromEnum(sub_br_inst)); 6107 6108 sema.air_instructions.appendAssumeCapacity(.{ 6109 .tag = .br, 6110 .data = .{ .br = .{ 6111 .block_inst = merges.block_inst, 6112 .operand = coerced_operand, 6113 } }, 6114 }); 6115 } 6116 6117 if (try sema.typeHasOnePossibleValue(resolved_ty)) |block_only_value| { 6118 return Air.internedToRef(block_only_value.toIntern()); 6119 } 6120 6121 return merges.block_inst.toRef(); 6122 } 6123 6124 fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6125 const tracy = trace(@src()); 6126 defer tracy.end(); 6127 6128 const pt = sema.pt; 6129 const zcu = pt.zcu; 6130 const ip = &zcu.intern_pool; 6131 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 6132 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; 6133 6134 const src = block.nodeOffset(inst_data.src_node); 6135 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0); 6136 const options_src = block.builtinCallArgSrc(inst_data.src_node, 1); 6137 6138 const ptr = try sema.resolveInst(extra.exported); 6139 const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{ .simple = .export_target }); 6140 const ptr_ty = ptr_val.typeOf(zcu); 6141 6142 const options = try sema.resolveExportOptions(block, options_src, extra.options); 6143 6144 { 6145 if (ptr_ty.zigTypeTag(zcu) != .pointer) { 6146 return sema.fail(block, ptr_src, "expected pointer type, found '{f}'", .{ptr_ty.fmt(pt)}); 6147 } 6148 const ptr_ty_info = ptr_ty.ptrInfo(zcu); 6149 if (ptr_ty_info.flags.size == .slice) { 6150 return sema.fail(block, ptr_src, "export target cannot be slice", .{}); 6151 } 6152 if (ptr_ty_info.packed_offset.host_size != 0) { 6153 return sema.fail(block, ptr_src, "export target cannot be bit-pointer", .{}); 6154 } 6155 } 6156 6157 const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr; 6158 switch (ptr_info.base_addr) { 6159 .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}), 6160 .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}), 6161 .uav => |uav| { 6162 if (ptr_info.byte_offset != 0) { 6163 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); 6164 } 6165 if (options.linkage == .internal) return; 6166 const export_ty = Value.fromInterned(uav.val).typeOf(zcu); 6167 if (!try sema.validateExternType(export_ty, .other)) { 6168 return sema.failWithOwnedErrorMsg(block, msg: { 6169 const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); 6170 errdefer msg.destroy(sema.gpa); 6171 try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); 6172 try sema.addDeclaredHereNote(msg, export_ty); 6173 break :msg msg; 6174 }); 6175 } 6176 try sema.exports.append(zcu.gpa, .{ 6177 .opts = options, 6178 .src = src, 6179 .exported = .{ .uav = uav.val }, 6180 .status = .in_progress, 6181 }); 6182 }, 6183 .nav => |nav| { 6184 if (ptr_info.byte_offset != 0) { 6185 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); 6186 } 6187 try sema.analyzeExport(block, src, options, nav); 6188 }, 6189 } 6190 } 6191 6192 pub fn analyzeExport( 6193 sema: *Sema, 6194 block: *Block, 6195 src: LazySrcLoc, 6196 options: Zcu.Export.Options, 6197 orig_nav_index: InternPool.Nav.Index, 6198 ) !void { 6199 const gpa = sema.gpa; 6200 const pt = sema.pt; 6201 const zcu = pt.zcu; 6202 const ip = &zcu.intern_pool; 6203 6204 if (options.linkage == .internal) 6205 return; 6206 6207 try sema.ensureNavResolved(block, src, orig_nav_index, .fully); 6208 6209 const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) { 6210 .variable => |v| v.owner_nav, 6211 .@"extern" => |e| e.owner_nav, 6212 .func => |f| f.owner_nav, 6213 else => orig_nav_index, 6214 }; 6215 6216 const exported_nav = ip.getNav(exported_nav_index); 6217 const export_ty: Type = .fromInterned(exported_nav.typeOf(ip)); 6218 6219 if (!try sema.validateExternType(export_ty, .other)) { 6220 return sema.failWithOwnedErrorMsg(block, msg: { 6221 const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); 6222 errdefer msg.destroy(gpa); 6223 6224 try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); 6225 6226 try sema.addDeclaredHereNote(msg, export_ty); 6227 break :msg msg; 6228 }); 6229 } 6230 6231 // TODO: some backends might support re-exporting extern decls 6232 if (exported_nav.getExtern(ip) != null) { 6233 return sema.fail(block, src, "export target cannot be extern", .{}); 6234 } 6235 6236 try sema.maybeQueueFuncBodyAnalysis(block, src, exported_nav_index); 6237 6238 try sema.exports.append(gpa, .{ 6239 .opts = options, 6240 .src = src, 6241 .exported = .{ .nav = exported_nav_index }, 6242 .status = .in_progress, 6243 }); 6244 } 6245 6246 fn zirDisableInstrumentation(sema: *Sema) CompileError!void { 6247 const pt = sema.pt; 6248 const zcu = pt.zcu; 6249 const ip = &zcu.intern_pool; 6250 const func = switch (sema.owner.unwrap()) { 6251 .func => |func| func, 6252 .@"comptime", 6253 .nav_val, 6254 .nav_ty, 6255 .type, 6256 .memoized_state, 6257 => return, // does nothing outside a function 6258 }; 6259 ip.funcSetDisableInstrumentation(func); 6260 sema.allow_memoize = false; 6261 } 6262 6263 fn zirDisableIntrinsics(sema: *Sema) CompileError!void { 6264 const pt = sema.pt; 6265 const zcu = pt.zcu; 6266 const ip = &zcu.intern_pool; 6267 const func = switch (sema.owner.unwrap()) { 6268 .func => |func| func, 6269 .@"comptime", 6270 .nav_val, 6271 .nav_ty, 6272 .type, 6273 .memoized_state, 6274 => return, // does nothing outside a function 6275 }; 6276 ip.funcSetDisableIntrinsics(func); 6277 sema.allow_memoize = false; 6278 } 6279 6280 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 6281 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 6282 const src = block.builtinCallArgSrc(extra.node, 0); 6283 block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, .FloatMode, .{ .simple = .operand_setFloatMode }); 6284 } 6285 6286 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6287 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 6288 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 6289 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{ .simple = .operand_setRuntimeSafety }); 6290 } 6291 6292 fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { 6293 const tracy = trace(@src()); 6294 defer tracy.end(); 6295 6296 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break"; 6297 const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; 6298 const operand = try sema.resolveInst(inst_data.operand); 6299 const zir_block = extra.block_inst; 6300 6301 var block = start_block; 6302 while (true) { 6303 if (block.label) |label| { 6304 if (label.zir_block == zir_block) { 6305 const br_ref = try start_block.addBr(label.merges.block_inst, operand); 6306 const src_loc = if (extra.operand_src_node.unwrap()) |operand_src_node| 6307 start_block.nodeOffset(operand_src_node) 6308 else 6309 null; 6310 try label.merges.src_locs.append(sema.gpa, src_loc); 6311 try label.merges.results.append(sema.gpa, operand); 6312 try label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); 6313 block.runtime_index.increment(); 6314 if (block.runtime_cond == null and block.runtime_loop == null) { 6315 block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; 6316 block.runtime_loop = start_block.runtime_loop; 6317 } 6318 return; 6319 } 6320 } 6321 block = block.parent.?; 6322 } 6323 } 6324 6325 fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { 6326 const tracy = trace(@src()); 6327 defer tracy.end(); 6328 6329 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break"; 6330 const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; 6331 const operand_src = start_block.nodeOffset(extra.operand_src_node.unwrap().?); 6332 const uncoerced_operand = try sema.resolveInst(inst_data.operand); 6333 const switch_inst = extra.block_inst; 6334 6335 switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) { 6336 .switch_block, .switch_block_ref => {}, 6337 else => unreachable, // assertion failure 6338 } 6339 6340 const switch_payload_index = sema.code.instructions.items(.data)[@intFromEnum(switch_inst)].pl_node.payload_index; 6341 const switch_operand_ref = sema.code.extraData(Zir.Inst.SwitchBlock, switch_payload_index).data.operand; 6342 const switch_operand_ty = sema.typeOf(try sema.resolveInst(switch_operand_ref)); 6343 6344 const operand = try sema.coerce(start_block, switch_operand_ty, uncoerced_operand, operand_src); 6345 6346 try sema.validateRuntimeValue(start_block, operand_src, operand); 6347 6348 // We want to generate a `switch_dispatch` instruction with the switch condition, 6349 // possibly preceded by a store to the stack alloc containing the raw operand. 6350 // However, to avoid too much special-case state in Sema, this is handled by the 6351 // `switch` lowering logic. As such, we will find the `Block` corresponding to the 6352 // parent `switch_block[_ref]` instruction, create a dummy `br`, and add a merge 6353 // to signal to the switch logic to rewrite this into an appropriate dispatch. 6354 6355 var block = start_block; 6356 while (true) { 6357 if (block.label) |label| { 6358 if (label.zir_block == switch_inst) { 6359 const br_ref = try start_block.addBr(label.merges.block_inst, operand); 6360 try label.merges.extra_insts.append(sema.gpa, br_ref.toIndex().?); 6361 try label.merges.extra_src_locs.append(sema.gpa, operand_src); 6362 block.runtime_index.increment(); 6363 if (block.runtime_cond == null and block.runtime_loop == null) { 6364 block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; 6365 block.runtime_loop = start_block.runtime_loop; 6366 } 6367 return; 6368 } 6369 } 6370 block = block.parent.?; 6371 } 6372 } 6373 6374 fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6375 if (block.isComptime() or block.ownerModule().strip) return; 6376 6377 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; 6378 6379 if (block.instructions.items.len != 0) { 6380 const idx = block.instructions.items[block.instructions.items.len - 1]; 6381 if (sema.air_instructions.items(.tag)[@intFromEnum(idx)] == .dbg_stmt) { 6382 // The previous dbg_stmt didn't correspond to any actual code, so replace it. 6383 sema.air_instructions.items(.data)[@intFromEnum(idx)].dbg_stmt = .{ 6384 .line = inst_data.line, 6385 .column = inst_data.column, 6386 }; 6387 return; 6388 } 6389 } 6390 6391 _ = try block.addInst(.{ 6392 .tag = .dbg_stmt, 6393 .data = .{ .dbg_stmt = .{ 6394 .line = inst_data.line, 6395 .column = inst_data.column, 6396 } }, 6397 }); 6398 } 6399 6400 fn zirDbgEmptyStmt(_: *Sema, block: *Block, _: Zir.Inst.Index) CompileError!void { 6401 if (block.isComptime() or block.ownerModule().strip) return; 6402 _ = try block.addNoOp(.dbg_empty_stmt); 6403 } 6404 6405 fn zirDbgVar( 6406 sema: *Sema, 6407 block: *Block, 6408 inst: Zir.Inst.Index, 6409 air_tag: Air.Inst.Tag, 6410 ) CompileError!void { 6411 const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op; 6412 const operand = try sema.resolveInst(str_op.operand); 6413 const name = str_op.getStr(sema.code); 6414 try sema.addDbgVar(block, operand, air_tag, name); 6415 } 6416 6417 fn addDbgVar( 6418 sema: *Sema, 6419 block: *Block, 6420 operand: Air.Inst.Ref, 6421 air_tag: Air.Inst.Tag, 6422 name: []const u8, 6423 ) CompileError!void { 6424 if (block.isComptime() or block.ownerModule().strip) return; 6425 6426 const pt = sema.pt; 6427 const zcu = pt.zcu; 6428 const operand_ty = sema.typeOf(operand); 6429 const val_ty = switch (air_tag) { 6430 .dbg_var_ptr => operand_ty.childType(zcu), 6431 .dbg_var_val, .dbg_arg_inline => operand_ty, 6432 else => unreachable, 6433 }; 6434 if (try val_ty.comptimeOnlySema(pt)) return; 6435 if (!(try val_ty.hasRuntimeBitsSema(pt))) return; 6436 if (try sema.resolveValue(operand)) |operand_val| { 6437 if (operand_val.canMutateComptimeVarState(zcu)) return; 6438 } 6439 6440 // To ensure the lexical scoping is known to backends, this alloc must be 6441 // within a real runtime block. We set a flag which communicates information 6442 // to the closest lexically enclosing block: 6443 // * If it is a `block_inline`, communicates to logic in `analyzeBodyInner` 6444 // to create a post-hoc block. 6445 // * Otherwise, communicates to logic in `resolveBlockBody` to create a 6446 // real `block` instruction. 6447 if (block.need_debug_scope) |ptr| ptr.* = true; 6448 6449 // Add the name to the AIR. 6450 const name_nts = try sema.appendAirString(name); 6451 6452 _ = try block.addInst(.{ 6453 .tag = air_tag, 6454 .data = .{ .pl_op = .{ 6455 .payload = @intFromEnum(name_nts), 6456 .operand = operand, 6457 } }, 6458 }); 6459 } 6460 6461 pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTerminatedString { 6462 if (str.len == 0) return .none; 6463 const nts: Air.NullTerminatedString = @enumFromInt(sema.air_extra.items.len); 6464 const elements_used = str.len / 4 + 1; 6465 const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used); 6466 const buffer = mem.sliceAsBytes(elements); 6467 @memcpy(buffer[0..str.len], str); 6468 buffer[str.len] = 0; 6469 return nts; 6470 } 6471 6472 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 6473 const pt = sema.pt; 6474 const zcu = pt.zcu; 6475 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 6476 const src = block.tokenOffset(inst_data.src_tok); 6477 const decl_name = try zcu.intern_pool.getOrPutString( 6478 sema.gpa, 6479 pt.tid, 6480 inst_data.get(sema.code), 6481 .no_embedded_nulls, 6482 ); 6483 const nav_index = try sema.lookupIdentifier(block, decl_name); 6484 return sema.analyzeNavRef(block, src, nav_index); 6485 } 6486 6487 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 6488 const pt = sema.pt; 6489 const zcu = pt.zcu; 6490 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 6491 const src = block.tokenOffset(inst_data.src_tok); 6492 const decl_name = try zcu.intern_pool.getOrPutString( 6493 sema.gpa, 6494 pt.tid, 6495 inst_data.get(sema.code), 6496 .no_embedded_nulls, 6497 ); 6498 const nav = try sema.lookupIdentifier(block, decl_name); 6499 return sema.analyzeNavVal(block, src, nav); 6500 } 6501 6502 fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index { 6503 const pt = sema.pt; 6504 const zcu = pt.zcu; 6505 var namespace = block.namespace; 6506 while (true) { 6507 if (try sema.lookupInNamespace(block, namespace, name)) |lookup| { 6508 assert(lookup.accessible); 6509 return lookup.nav; 6510 } 6511 namespace = zcu.namespacePtr(namespace).parent.unwrap() orelse break; 6512 } 6513 unreachable; // AstGen detects use of undeclared identifiers. 6514 } 6515 6516 /// This looks up a member of a specific namespace. 6517 fn lookupInNamespace( 6518 sema: *Sema, 6519 block: *Block, 6520 namespace_index: InternPool.NamespaceIndex, 6521 ident_name: InternPool.NullTerminatedString, 6522 ) CompileError!?struct { 6523 nav: InternPool.Nav.Index, 6524 /// If `false`, the declaration is in a different file and is not `pub`. 6525 /// We still return the declaration for better error reporting. 6526 accessible: bool, 6527 } { 6528 const pt = sema.pt; 6529 const zcu = pt.zcu; 6530 6531 try pt.ensureNamespaceUpToDate(namespace_index); 6532 6533 const namespace = zcu.namespacePtr(namespace_index); 6534 6535 const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu }; 6536 6537 const src_file = zcu.namespacePtr(block.namespace).file_scope; 6538 6539 if (Type.fromInterned(namespace.owner_type).typeDeclInst(zcu)) |type_decl_inst| { 6540 try sema.declareDependency(.{ .namespace_name = .{ 6541 .namespace = type_decl_inst, 6542 .name = ident_name, 6543 } }); 6544 } 6545 6546 if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { 6547 return .{ 6548 .nav = nav_index, 6549 .accessible = true, 6550 }; 6551 } else if (namespace.priv_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { 6552 return .{ 6553 .nav = nav_index, 6554 .accessible = src_file == namespace.file_scope, 6555 }; 6556 } 6557 6558 return null; 6559 } 6560 6561 fn funcDeclSrcInst(sema: *Sema, func_inst: Air.Inst.Ref) !?InternPool.TrackedInst.Index { 6562 const pt = sema.pt; 6563 const zcu = pt.zcu; 6564 const ip = &zcu.intern_pool; 6565 const func_val = try sema.resolveValue(func_inst) orelse return null; 6566 if (func_val.isUndef(zcu)) return null; 6567 const nav = switch (ip.indexToKey(func_val.toIntern())) { 6568 .@"extern" => |e| e.owner_nav, 6569 .func => |f| f.owner_nav, 6570 .ptr => |ptr| switch (ptr.base_addr) { 6571 .nav => |nav| if (ptr.byte_offset == 0) nav else return null, 6572 else => return null, 6573 }, 6574 else => return null, 6575 }; 6576 return ip.getNav(nav).srcInst(ip); 6577 } 6578 6579 pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref { 6580 const pt = sema.pt; 6581 const zcu = pt.zcu; 6582 const gpa = sema.gpa; 6583 6584 if (block.isComptime() or block.is_typeof) { 6585 const index_val = try pt.intValue_u64(.usize, sema.comptime_err_ret_trace.items.len); 6586 return Air.internedToRef(index_val.toIntern()); 6587 } 6588 6589 if (!block.ownerModule().error_tracing) return .none; 6590 6591 const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); 6592 try stack_trace_ty.resolveFields(pt); 6593 const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); 6594 const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) { 6595 error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"), 6596 error.ComptimeReturn, error.ComptimeBreak => unreachable, 6597 error.OutOfMemory => |e| return e, 6598 }; 6599 6600 return try block.addInst(.{ 6601 .tag = .save_err_return_trace_index, 6602 .data = .{ .ty_pl = .{ 6603 .ty = Air.internedToRef(stack_trace_ty.toIntern()), 6604 .payload = @intCast(field_index), 6605 } }, 6606 }); 6607 } 6608 6609 /// Add instructions to block to "pop" the error return trace. 6610 /// If `operand` is provided, only pops if operand is non-error. 6611 fn popErrorReturnTrace( 6612 sema: *Sema, 6613 block: *Block, 6614 src: LazySrcLoc, 6615 operand: Air.Inst.Ref, 6616 saved_error_trace_index: Air.Inst.Ref, 6617 ) CompileError!void { 6618 const pt = sema.pt; 6619 const zcu = pt.zcu; 6620 const gpa = sema.gpa; 6621 var is_non_error: ?bool = null; 6622 var is_non_error_inst: Air.Inst.Ref = undefined; 6623 if (operand != .none) { 6624 is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand); 6625 if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val| 6626 is_non_error = cond_val.toBool(); 6627 } else is_non_error = true; // no operand means pop unconditionally 6628 6629 if (is_non_error == true) { 6630 // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or 6631 // the result is comptime-known to be a non-error. Either way, pop unconditionally. 6632 6633 const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); 6634 try stack_trace_ty.resolveFields(pt); 6635 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); 6636 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); 6637 const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); 6638 const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true); 6639 try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store); 6640 } else if (is_non_error == null) { 6641 // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need 6642 // to pop any error trace that may have been propagated from our arguments. 6643 6644 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len); 6645 const cond_block_inst = try block.addInstAsIndex(.{ 6646 .tag = .block, 6647 .data = .{ 6648 .ty_pl = .{ 6649 .ty = .void_type, 6650 .payload = undefined, // updated below 6651 }, 6652 }, 6653 }); 6654 6655 var then_block = block.makeSubBlock(); 6656 defer then_block.instructions.deinit(gpa); 6657 6658 // If non-error, then pop the error return trace by restoring the index. 6659 const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace); 6660 try stack_trace_ty.resolveFields(pt); 6661 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); 6662 const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); 6663 const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls); 6664 const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true); 6665 try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store); 6666 _ = try then_block.addBr(cond_block_inst, .void_value); 6667 6668 // Otherwise, do nothing 6669 var else_block = block.makeSubBlock(); 6670 defer else_block.instructions.deinit(gpa); 6671 _ = try else_block.addBr(cond_block_inst, .void_value); 6672 6673 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 6674 then_block.instructions.items.len + else_block.instructions.items.len + 6675 @typeInfo(Air.Block).@"struct".fields.len + 1); // +1 for the sole .cond_br instruction in the .block 6676 6677 const cond_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 6678 try sema.air_instructions.append(gpa, .{ 6679 .tag = .cond_br, 6680 .data = .{ 6681 .pl_op = .{ 6682 .operand = is_non_error_inst, 6683 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 6684 .then_body_len = @intCast(then_block.instructions.items.len), 6685 .else_body_len = @intCast(else_block.instructions.items.len), 6686 .branch_hints = .{ 6687 // Weight against error branch. 6688 .true = .likely, 6689 .false = .unlikely, 6690 // Code coverage is not valuable on either branch. 6691 .then_cov = .none, 6692 .else_cov = .none, 6693 }, 6694 }), 6695 }, 6696 }, 6697 }); 6698 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 6699 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 6700 6701 sema.air_instructions.items(.data)[@intFromEnum(cond_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); 6702 sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst)); 6703 } 6704 } 6705 6706 fn zirCall( 6707 sema: *Sema, 6708 block: *Block, 6709 inst: Zir.Inst.Index, 6710 comptime kind: enum { direct, field }, 6711 ) CompileError!Air.Inst.Ref { 6712 const tracy = trace(@src()); 6713 defer tracy.end(); 6714 6715 const pt = sema.pt; 6716 const zcu = pt.zcu; 6717 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 6718 const callee_src = block.src(.{ .node_offset_call_func = inst_data.src_node }); 6719 const call_src = block.nodeOffset(inst_data.src_node); 6720 const ExtraType = switch (kind) { 6721 .direct => Zir.Inst.Call, 6722 .field => Zir.Inst.FieldCall, 6723 }; 6724 const extra = sema.code.extraData(ExtraType, inst_data.payload_index); 6725 const args_len = extra.data.flags.args_len; 6726 6727 const modifier: std.builtin.CallModifier = @enumFromInt(extra.data.flags.packed_modifier); 6728 const ensure_result_used = extra.data.flags.ensure_result_used; 6729 const pop_error_return_trace = extra.data.flags.pop_error_return_trace; 6730 6731 const callee: ResolvedFieldCallee = switch (kind) { 6732 .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, 6733 .field => blk: { 6734 const object_ptr = try sema.resolveInst(extra.data.obj_ptr); 6735 const field_name = try zcu.intern_pool.getOrPutString( 6736 sema.gpa, 6737 pt.tid, 6738 sema.code.nullTerminatedString(extra.data.field_name_start), 6739 .no_embedded_nulls, 6740 ); 6741 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); 6742 break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); 6743 }, 6744 }; 6745 const func: Air.Inst.Ref = switch (callee) { 6746 .direct => |func_inst| func_inst, 6747 .method => |method| method.func_inst, 6748 }; 6749 6750 const callee_ty = sema.typeOf(func); 6751 const total_args = args_len + @intFromBool(callee == .method); 6752 const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method); 6753 6754 // The block index before the call, so we can potentially insert an error trace save here later. 6755 const block_index: Air.Inst.Index = @enumFromInt(block.instructions.items.len); 6756 6757 // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the 6758 // error trace potentially dirty). 6759 var input_is_error = false; 6760 6761 const args_info: CallArgsInfo = .{ .zir_call = .{ 6762 .bound_arg = switch (callee) { 6763 .direct => .none, 6764 .method => |method| method.arg0_inst, 6765 }, 6766 .bound_arg_src = callee_src, 6767 .call_inst = inst, 6768 .call_node_offset = inst_data.src_node, 6769 .num_args = args_len, 6770 .args_body = @ptrCast(sema.code.extra[extra.end..]), 6771 .any_arg_is_error = &input_is_error, 6772 } }; 6773 6774 // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction. 6775 const call_dbg_node: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); 6776 const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call); 6777 6778 if (block.ownerModule().error_tracing and 6779 !block.isComptime() and !block.is_typeof and (input_is_error or pop_error_return_trace)) 6780 { 6781 const return_ty = sema.typeOf(call_inst); 6782 if (modifier != .always_tail and return_ty.isNoReturn(zcu)) 6783 return call_inst; // call to "fn (...) noreturn", don't pop 6784 6785 // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it 6786 // *before* the recursive call. This will be a bit tricky to do and probably requires 6787 // moving this logic into analyzeCall. But that's probably a good idea anyway. 6788 if (modifier == .always_tail) 6789 return call_inst; 6790 6791 // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only 6792 // need to clean-up our own trace if we were passed to a non-error-handling expression. 6793 if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) { 6794 const stack_trace_ty = try sema.getBuiltinType(call_src, .StackTrace); 6795 try stack_trace_ty.resolveFields(pt); 6796 const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "index", .no_embedded_nulls); 6797 const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); 6798 6799 // Insert a save instruction before the arg resolution + call instructions we just generated 6800 const save_inst = try block.insertInst(block_index, .{ 6801 .tag = .save_err_return_trace_index, 6802 .data = .{ .ty_pl = .{ 6803 .ty = Air.internedToRef(stack_trace_ty.toIntern()), 6804 .payload = @intCast(field_index), 6805 } }, 6806 }); 6807 6808 // Pop the error return trace, testing the result for non-error if necessary 6809 const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst; 6810 try sema.popErrorReturnTrace(block, call_src, operand, save_inst); 6811 } 6812 6813 return call_inst; 6814 } else { 6815 return call_inst; 6816 } 6817 } 6818 6819 fn checkCallArgumentCount( 6820 sema: *Sema, 6821 block: *Block, 6822 func: Air.Inst.Ref, 6823 func_src: LazySrcLoc, 6824 callee_ty: Type, 6825 total_args: usize, 6826 member_fn: bool, 6827 ) !Type { 6828 const pt = sema.pt; 6829 const zcu = pt.zcu; 6830 const func_ty: Type = func_ty: { 6831 switch (callee_ty.zigTypeTag(zcu)) { 6832 .@"fn" => break :func_ty callee_ty, 6833 .pointer => { 6834 const ptr_info = callee_ty.ptrInfo(zcu); 6835 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") { 6836 break :func_ty .fromInterned(ptr_info.child); 6837 } 6838 }, 6839 .optional => { 6840 const opt_child = callee_ty.optionalChild(zcu); 6841 if (opt_child.zigTypeTag(zcu) == .@"fn" or (opt_child.isSinglePointer(zcu) and 6842 opt_child.childType(zcu).zigTypeTag(zcu) == .@"fn")) 6843 { 6844 const msg = msg: { 6845 const msg = try sema.errMsg(func_src, "cannot call optional type '{f}'", .{ 6846 callee_ty.fmt(pt), 6847 }); 6848 errdefer msg.destroy(sema.gpa); 6849 try sema.errNote(func_src, msg, "consider using '.?', 'orelse' or 'if'", .{}); 6850 break :msg msg; 6851 }; 6852 return sema.failWithOwnedErrorMsg(block, msg); 6853 } 6854 }, 6855 else => {}, 6856 } 6857 return sema.fail(block, func_src, "type '{f}' not a function", .{callee_ty.fmt(pt)}); 6858 }; 6859 6860 const func_ty_info = zcu.typeToFunc(func_ty).?; 6861 const fn_params_len = func_ty_info.param_types.len; 6862 const args_len = total_args - @intFromBool(member_fn); 6863 if (func_ty_info.is_var_args) { 6864 assert(callConvSupportsVarArgs(func_ty_info.cc)); 6865 if (total_args >= fn_params_len) return func_ty; 6866 } else if (fn_params_len == total_args) { 6867 return func_ty; 6868 } 6869 6870 const maybe_func_inst = try sema.funcDeclSrcInst(func); 6871 const member_str = if (member_fn) "member function " else ""; 6872 const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; 6873 const msg = msg: { 6874 const msg = try sema.errMsg( 6875 func_src, 6876 "{s}expected {s}{d} argument(s), found {d}", 6877 .{ 6878 member_str, 6879 variadic_str, 6880 fn_params_len - @intFromBool(member_fn), 6881 args_len, 6882 }, 6883 ); 6884 errdefer msg.destroy(sema.gpa); 6885 6886 if (maybe_func_inst) |func_inst| { 6887 try sema.errNote(.{ 6888 .base_node_inst = func_inst, 6889 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 6890 }, msg, "function declared here", .{}); 6891 } 6892 break :msg msg; 6893 }; 6894 return sema.failWithOwnedErrorMsg(block, msg); 6895 } 6896 6897 fn callBuiltin( 6898 sema: *Sema, 6899 block: *Block, 6900 call_src: LazySrcLoc, 6901 builtin_fn: Air.Inst.Ref, 6902 modifier: std.builtin.CallModifier, 6903 args: []const Air.Inst.Ref, 6904 operation: CallOperation, 6905 ) !void { 6906 const pt = sema.pt; 6907 const zcu = pt.zcu; 6908 const callee_ty = sema.typeOf(builtin_fn); 6909 const func_ty: Type = func_ty: { 6910 switch (callee_ty.zigTypeTag(zcu)) { 6911 .@"fn" => break :func_ty callee_ty, 6912 .pointer => { 6913 const ptr_info = callee_ty.ptrInfo(zcu); 6914 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") { 6915 break :func_ty .fromInterned(ptr_info.child); 6916 } 6917 }, 6918 else => {}, 6919 } 6920 std.debug.panic("type '{f}' is not a function calling builtin fn", .{callee_ty.fmt(pt)}); 6921 }; 6922 6923 const func_ty_info = zcu.typeToFunc(func_ty).?; 6924 const fn_params_len = func_ty_info.param_types.len; 6925 if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) { 6926 std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len }); 6927 } 6928 6929 _ = try sema.analyzeCall( 6930 block, 6931 builtin_fn, 6932 func_ty, 6933 call_src, 6934 call_src, 6935 modifier, 6936 false, 6937 .{ .resolved = .{ .src = call_src, .args = args } }, 6938 null, 6939 operation, 6940 ); 6941 } 6942 6943 const CallOperation = enum { 6944 call, 6945 @"@call", 6946 @"@panic", 6947 @"safety check", 6948 @"error return", 6949 }; 6950 6951 const CallArgsInfo = union(enum) { 6952 /// The full list of resolved (but uncoerced) arguments is known ahead of time. 6953 resolved: struct { 6954 src: LazySrcLoc, 6955 args: []const Air.Inst.Ref, 6956 }, 6957 6958 /// The list of resolved (but uncoerced) arguments is known ahead of time, but 6959 /// originated from a usage of the @call builtin at the given node offset. 6960 call_builtin: struct { 6961 call_node_offset: std.zig.Ast.Node.Offset, 6962 args: []const Air.Inst.Ref, 6963 }, 6964 6965 /// This call corresponds to a ZIR call instruction. The arguments have not yet been 6966 /// resolved. They must be resolved by `analyzeCall` so that argument resolution and 6967 /// generic instantiation may be interleaved. This is required for RLS to work on 6968 /// generic parameters. 6969 zir_call: struct { 6970 /// This may be `none`, in which case it is ignored. Otherwise, it is the 6971 /// already-resolved value of the first argument, from method call syntax. 6972 bound_arg: Air.Inst.Ref, 6973 /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`. 6974 bound_arg_src: LazySrcLoc, 6975 /// The ZIR call instruction. The parameter type is placed at this index while 6976 /// analyzing arguments. 6977 call_inst: Zir.Inst.Index, 6978 /// The node offset of `call_inst`. 6979 call_node_offset: std.zig.Ast.Node.Offset, 6980 /// The number of arguments to this call, not including `bound_arg`. 6981 num_args: u32, 6982 /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it 6983 /// is not `none`). Format is precisely the same as trailing data of ZIR `call`. 6984 args_body: []const Zir.Inst.Index, 6985 /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type. 6986 /// This is used by the caller to restore the error return trace when necessary. 6987 any_arg_is_error: *bool, 6988 }, 6989 6990 fn count(cai: CallArgsInfo) usize { 6991 return switch (cai) { 6992 inline .resolved, .call_builtin => |resolved| resolved.args.len, 6993 .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none), 6994 }; 6995 } 6996 6997 fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc { 6998 return switch (cai) { 6999 .resolved => |resolved| resolved.src, 7000 .call_builtin => |call_builtin| block.src(.{ .call_arg = .{ 7001 .call_node_offset = call_builtin.call_node_offset, 7002 .arg_index = @intCast(arg_index), 7003 } }), 7004 .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) { 7005 return zir_call.bound_arg_src; 7006 } else block.src(.{ .call_arg = .{ 7007 .call_node_offset = zir_call.call_node_offset, 7008 .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)), 7009 } }), 7010 }; 7011 } 7012 7013 /// Analyzes the arg at `arg_index` and coerces it to `param_ty`. 7014 /// `param_ty` may be `generic_poison`. A value of `null` indicates a varargs parameter. 7015 /// `func_ty_info` may be the type before instantiation, even if a generic instantiation is in progress. 7016 /// Emits a compile error if the argument is not comptime-known despite either `block.isComptime()` or 7017 /// the parameter being marked `comptime`. 7018 fn analyzeArg( 7019 cai: CallArgsInfo, 7020 sema: *Sema, 7021 block: *Block, 7022 arg_index: usize, 7023 maybe_param_ty: ?Type, 7024 func_ty_info: InternPool.Key.FuncType, 7025 func_inst: Air.Inst.Ref, 7026 maybe_func_src_inst: ?InternPool.TrackedInst.Index, 7027 ) CompileError!Air.Inst.Ref { 7028 const pt = sema.pt; 7029 const zcu = pt.zcu; 7030 const param_count = func_ty_info.param_types.len; 7031 const uncoerced_arg: Air.Inst.Ref = switch (cai) { 7032 inline .resolved, .call_builtin => |resolved| resolved.args[arg_index], 7033 .zir_call => |zir_call| arg_val: { 7034 const has_bound_arg = zir_call.bound_arg != .none; 7035 if (arg_index == 0 and has_bound_arg) { 7036 break :arg_val zir_call.bound_arg; 7037 } 7038 const real_arg_idx = arg_index - @intFromBool(has_bound_arg); 7039 7040 const arg_body = if (real_arg_idx == 0) blk: { 7041 const start = zir_call.num_args; 7042 const end = @intFromEnum(zir_call.args_body[0]); 7043 break :blk zir_call.args_body[start..end]; 7044 } else blk: { 7045 const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]); 7046 const end = @intFromEnum(zir_call.args_body[real_arg_idx]); 7047 break :blk zir_call.args_body[start..end]; 7048 }; 7049 7050 // Generate args to comptime params in comptime block 7051 const parent_comptime = block.comptime_reason; 7052 defer block.comptime_reason = parent_comptime; 7053 // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx` 7054 if (std.math.cast(u5, arg_index)) |i| { 7055 if (i < param_count and func_ty_info.paramIsComptime(i)) { 7056 block.comptime_reason = .{ 7057 .reason = .{ 7058 .src = cai.argSrc(block, arg_index), 7059 .r = .{ 7060 .comptime_param = .{ 7061 .comptime_src = if (maybe_func_src_inst) |src_inst| .{ 7062 .base_node_inst = src_inst, 7063 .offset = .{ .func_decl_param_comptime = @intCast(arg_index) }, 7064 } else unreachable, // should be non-null because the function is generic 7065 }, 7066 }, 7067 }, 7068 }; 7069 } 7070 } 7071 // Give the arg its result type 7072 const provide_param_ty: Type = maybe_param_ty orelse .generic_poison; 7073 sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(provide_param_ty.toIntern())); 7074 // Resolve the arg! 7075 const uncoerced_arg = try sema.resolveInlineBody(block, arg_body, zir_call.call_inst); 7076 7077 if (block.isComptime() and !try sema.isComptimeKnown(uncoerced_arg)) { 7078 return sema.failWithNeededComptime(block, cai.argSrc(block, arg_index), null); 7079 } 7080 7081 if (sema.typeOf(uncoerced_arg).zigTypeTag(zcu) == .noreturn) { 7082 // This terminates resolution of arguments. The caller should 7083 // propagate this. 7084 return uncoerced_arg; 7085 } 7086 7087 if (sema.typeOf(uncoerced_arg).isError(zcu)) { 7088 zir_call.any_arg_is_error.* = true; 7089 } 7090 7091 break :arg_val uncoerced_arg; 7092 }, 7093 }; 7094 const param_ty = maybe_param_ty orelse { 7095 return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index)); 7096 }; 7097 switch (param_ty.toIntern()) { 7098 .generic_poison_type => return uncoerced_arg, 7099 else => return sema.coerceExtra( 7100 block, 7101 param_ty, 7102 uncoerced_arg, 7103 cai.argSrc(block, arg_index), 7104 .{ .param_src = .{ 7105 .func_inst = func_inst, 7106 .param_i = @intCast(arg_index), 7107 } }, 7108 ) catch |err| switch (err) { 7109 error.NotCoercible => unreachable, 7110 else => |e| return e, 7111 }, 7112 } 7113 } 7114 }; 7115 7116 fn analyzeCall( 7117 sema: *Sema, 7118 block: *Block, 7119 callee: Air.Inst.Ref, 7120 func_ty: Type, 7121 func_src: LazySrcLoc, 7122 call_src: LazySrcLoc, 7123 modifier: std.builtin.CallModifier, 7124 ensure_result_used: bool, 7125 args_info: CallArgsInfo, 7126 call_dbg_node: ?Zir.Inst.Index, 7127 operation: CallOperation, 7128 ) CompileError!Air.Inst.Ref { 7129 const pt = sema.pt; 7130 const zcu = pt.zcu; 7131 const gpa = zcu.gpa; 7132 const ip = &zcu.intern_pool; 7133 const arena = sema.arena; 7134 7135 const maybe_func_inst = try sema.funcDeclSrcInst(callee); 7136 const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{ 7137 .base_node_inst = fn_decl_inst, 7138 .offset = .{ .node_offset_fn_type_ret_ty = .zero }, 7139 } else func_src; 7140 7141 const func_ty_info = zcu.typeToFunc(func_ty).?; 7142 if (!callConvIsCallable(func_ty_info.cc)) { 7143 return sema.failWithOwnedErrorMsg(block, msg: { 7144 const msg = try sema.errMsg( 7145 func_src, 7146 "unable to call function with calling convention '{s}'", 7147 .{@tagName(func_ty_info.cc)}, 7148 ); 7149 errdefer msg.destroy(gpa); 7150 if (maybe_func_inst) |func_inst| try sema.errNote(.{ 7151 .base_node_inst = func_inst, 7152 .offset = .nodeOffset(.zero), 7153 }, msg, "function declared here", .{}); 7154 break :msg msg; 7155 }); 7156 } 7157 7158 // We need this value in a few code paths. 7159 const callee_val = try sema.resolveDefinedValue(block, call_src, callee); 7160 // If the callee is a comptime-known *non-extern* function, `func_val` is populated. 7161 // If it is a comptime-known extern function, `func_is_extern` is set instead. 7162 // If it is not comptime-known, neither is set. 7163 const func_val: ?Value, const func_is_extern: bool = if (callee_val) |c| switch (ip.indexToKey(c.toIntern())) { 7164 .func => .{ c, false }, 7165 .ptr => switch (try sema.pointerDerefExtra(block, func_src, c)) { 7166 .runtime_load, .needed_well_defined, .out_of_bounds => .{ null, false }, 7167 .val => |pointee| switch (ip.indexToKey(pointee.toIntern())) { 7168 .func => .{ pointee, false }, 7169 .@"extern" => .{ null, true }, 7170 else => unreachable, 7171 }, 7172 }, 7173 .@"extern" => .{ null, true }, 7174 else => unreachable, 7175 } else .{ null, false }; 7176 7177 if (func_ty_info.is_generic and func_val == null) { 7178 return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target }); 7179 } 7180 7181 const inline_requested = func_ty_info.cc == .@"inline" or modifier == .always_inline; 7182 7183 // If the modifier is `.compile_time`, or if the return type is non-generic and comptime-only, 7184 // then we need to enter a comptime scope *now* to make sure the args are comptime-eval'd. 7185 const old_block_comptime_reason = block.comptime_reason; 7186 defer block.comptime_reason = old_block_comptime_reason; 7187 if (!block.isComptime()) { 7188 if (modifier == .compile_time) { 7189 block.comptime_reason = .{ .reason = .{ 7190 .src = call_src, 7191 .r = .{ .simple = .comptime_call_modifier }, 7192 } }; 7193 } else if (!inline_requested and try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) { 7194 block.comptime_reason = .{ 7195 .reason = .{ 7196 .src = call_src, 7197 .r = .{ 7198 .comptime_only_ret_ty = .{ 7199 .ty = .fromInterned(func_ty_info.return_type), 7200 .is_generic_inst = false, 7201 .ret_ty_src = func_ret_ty_src, 7202 }, 7203 }, 7204 }, 7205 }; 7206 } 7207 } 7208 7209 // This is whether we already know this to be an inline call. 7210 // If so, then comptime-known arguments are propagated when evaluating generic parameter/return types. 7211 // We might still learn that this call is inline *after* evaluating the generic return type. 7212 const early_known_inline = inline_requested or block.isComptime(); 7213 7214 // These values are undefined if `func_val == null`. 7215 const fn_nav: InternPool.Nav, const fn_zir: Zir, const fn_tracked_inst: InternPool.TrackedInst.Index, const fn_zir_inst: Zir.Inst.Index, const fn_zir_info: Zir.FnInfo = if (func_val) |f| b: { 7216 const info = ip.indexToKey(f.toIntern()).func; 7217 const nav = ip.getNav(info.owner_nav); 7218 const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail; 7219 const file = zcu.fileByIndex(resolved_func_inst.file); 7220 const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst); 7221 break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info }; 7222 } else .{ undefined, undefined, undefined, undefined, undefined }; 7223 7224 // This is the `inst_map` used when evaluating generic parameters and return types. 7225 var generic_inst_map: InstMap = .{}; 7226 defer generic_inst_map.deinit(gpa); 7227 if (func_ty_info.is_generic) { 7228 try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); 7229 } 7230 7231 // This exists so that `generic_block` below can include a "called from here" note back to this 7232 // call site when analyzing generic parameter/return types. 7233 var generic_inlining: Block.Inlining = if (func_ty_info.is_generic) .{ 7234 .call_block = block, 7235 .call_src = call_src, 7236 .func = func_val.?.toIntern(), 7237 .is_generic_instantiation = true, // this allows the following fields to be `undefined` 7238 .has_comptime_args = undefined, 7239 .comptime_result = undefined, 7240 .merges = undefined, 7241 } else undefined; 7242 7243 // This is the block in which we evaluate generic function components: that is, generic parameter 7244 // types and the generic return type. This must not be used if the function is not generic. 7245 // `comptime_reason` is set as needed. 7246 var generic_block: Block = if (func_ty_info.is_generic) .{ 7247 .parent = null, 7248 .sema = sema, 7249 .namespace = fn_nav.analysis.?.namespace, 7250 .instructions = .{}, 7251 .inlining = &generic_inlining, 7252 .src_base_inst = fn_nav.analysis.?.zir_index, 7253 .type_name_ctx = fn_nav.fqn, 7254 } else undefined; 7255 defer if (func_ty_info.is_generic) generic_block.instructions.deinit(gpa); 7256 7257 if (func_ty_info.is_generic) { 7258 // We certainly depend on the generic owner's signature! 7259 try sema.declareDependency(.{ .src_hash = fn_tracked_inst }); 7260 } 7261 7262 const args = try arena.alloc(Air.Inst.Ref, args_info.count()); 7263 for (args, 0..) |*arg, arg_idx| { 7264 const param_ty: ?Type = if (arg_idx < func_ty_info.param_types.len) ty: { 7265 const raw = func_ty_info.param_types.get(ip)[arg_idx]; 7266 if (raw != .generic_poison_type) break :ty .fromInterned(raw); 7267 7268 // We must discover the generic parameter type. 7269 assert(func_ty_info.is_generic); 7270 const param_inst_idx = fn_zir_info.param_body[arg_idx]; 7271 const param_inst = fn_zir.instructions.get(@intFromEnum(param_inst_idx)); 7272 switch (param_inst.tag) { 7273 .param_anytype, .param_anytype_comptime => break :ty .generic_poison, 7274 .param, .param_comptime => {}, 7275 else => unreachable, 7276 } 7277 7278 // Evaluate the generic parameter type. We need to switch out `sema.code` and `sema.inst_map`, because 7279 // the function definition may be in a different file to the call site. 7280 const old_code = sema.code; 7281 const old_inst_map = sema.inst_map; 7282 defer { 7283 generic_inst_map = sema.inst_map; 7284 sema.code = old_code; 7285 sema.inst_map = old_inst_map; 7286 } 7287 sema.code = fn_zir; 7288 sema.inst_map = generic_inst_map; 7289 7290 const extra = sema.code.extraData(Zir.Inst.Param, param_inst.data.pl_tok.payload_index); 7291 const param_src = generic_block.tokenOffset(param_inst.data.pl_tok.src_tok); 7292 const body = sema.code.bodySlice(extra.end, extra.data.type.body_len); 7293 7294 generic_block.comptime_reason = .{ .reason = .{ 7295 .r = .{ .simple = .function_parameters }, 7296 .src = param_src, 7297 } }; 7298 7299 const ty_ref = try sema.resolveInlineBody(&generic_block, body, param_inst_idx); 7300 const param_ty = try sema.analyzeAsType(&generic_block, param_src, ty_ref); 7301 7302 if (!param_ty.isValidParamType(zcu)) { 7303 const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; 7304 return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{ 7305 opaque_str, param_ty.fmt(pt), 7306 }); 7307 } 7308 7309 break :ty param_ty; 7310 } else null; // vararg 7311 7312 arg.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, callee, maybe_func_inst); 7313 const arg_ty = sema.typeOf(arg.*); 7314 if (arg_ty.zigTypeTag(zcu) == .noreturn) { 7315 return arg.*; // terminate analysis here 7316 } 7317 7318 if (func_ty_info.is_generic) { 7319 // We need to put the argument into `generic_inst_map` so that other parameters can refer to it. 7320 const param_inst_idx = fn_zir_info.param_body[arg_idx]; 7321 const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false; 7322 const param_is_comptime = declared_comptime or try arg_ty.comptimeOnlySema(pt); 7323 // We allow comptime-known arguments to propagate to generic types not only for comptime 7324 // parameters, but if the call is known to be inline. 7325 if (param_is_comptime or early_known_inline) { 7326 if (param_is_comptime and !try sema.isComptimeKnown(arg.*)) { 7327 assert(!declared_comptime); // `analyzeArg` handles this 7328 const arg_src = args_info.argSrc(block, arg_idx); 7329 const param_ty_src: LazySrcLoc = .{ 7330 .base_node_inst = maybe_func_inst.?, // the function is generic 7331 .offset = .{ .func_decl_param_ty = @intCast(arg_idx) }, 7332 }; 7333 return sema.failWithNeededComptime( 7334 block, 7335 arg_src, 7336 .{ .comptime_only_param_ty = .{ .ty = arg_ty, .param_ty_src = param_ty_src } }, 7337 ); 7338 } 7339 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*); 7340 } else { 7341 // We need a dummy instruction with this type. It doesn't actually need to be in any block, 7342 // since it will never be referenced at runtime! 7343 const dummy: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 7344 try sema.air_instructions.append(gpa, .{ .tag = .alloc, .data = .{ .ty = arg_ty } }); 7345 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, dummy.toRef()); 7346 } 7347 } 7348 } 7349 7350 // This return type is never generic poison. 7351 // However, if it has an IES, it is always associated with the callee value. 7352 // This is not correct for inline calls (where it should be an ad-hoc IES), nor for generic 7353 // calls (where it should be the IES of the instantiation). However, it's how we print this 7354 // in error messages. 7355 const resolved_ret_ty: Type = ret_ty: { 7356 if (!func_ty_info.is_generic) break :ret_ty .fromInterned(func_ty_info.return_type); 7357 7358 const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: { 7359 break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type); 7360 } else func_ty_info.return_type; 7361 7362 if (maybe_poison_bare != .generic_poison_type) break :ret_ty .fromInterned(func_ty_info.return_type); 7363 7364 // Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`. 7365 7366 assert(func_ty_info.is_generic); 7367 7368 const old_code = sema.code; 7369 const old_inst_map = sema.inst_map; 7370 defer { 7371 generic_inst_map = sema.inst_map; 7372 sema.code = old_code; 7373 sema.inst_map = old_inst_map; 7374 } 7375 sema.code = fn_zir; 7376 sema.inst_map = generic_inst_map; 7377 7378 generic_block.comptime_reason = .{ .reason = .{ 7379 .r = .{ .simple = .function_ret_ty }, 7380 .src = func_ret_ty_src, 7381 } }; 7382 7383 const bare_ty = if (fn_zir_info.ret_ty_ref != .none) bare: { 7384 assert(fn_zir_info.ret_ty_body.len == 0); 7385 break :bare try sema.resolveType(&generic_block, func_ret_ty_src, fn_zir_info.ret_ty_ref); 7386 } else bare: { 7387 assert(fn_zir_info.ret_ty_body.len != 0); 7388 const ty_ref = try sema.resolveInlineBody(&generic_block, fn_zir_info.ret_ty_body, fn_zir_inst); 7389 break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, ty_ref); 7390 }; 7391 assert(bare_ty.toIntern() != .generic_poison_type); 7392 7393 const full_ty = if (fn_zir_info.inferred_error_set) full: { 7394 try sema.validateErrorUnionPayloadType(block, bare_ty, func_ret_ty_src); 7395 const set = ip.errorUnionSet(func_ty_info.return_type); 7396 break :full try pt.errorUnionType(.fromInterned(set), bare_ty); 7397 } else bare_ty; 7398 7399 if (!full_ty.isValidReturnType(zcu)) { 7400 const opaque_str = if (full_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; 7401 return sema.fail(block, func_ret_ty_src, "{s}return type '{f}' not allowed", .{ 7402 opaque_str, full_ty.fmt(pt), 7403 }); 7404 } 7405 7406 break :ret_ty full_ty; 7407 }; 7408 7409 // If we've discovered after evaluating arguments that a generic function instantiation is 7410 // comptime-only, then we can mark the block as comptime *now*. 7411 if (!inline_requested and !block.isComptime() and try resolved_ret_ty.comptimeOnlySema(pt)) { 7412 block.comptime_reason = .{ 7413 .reason = .{ 7414 .src = call_src, 7415 .r = .{ 7416 .comptime_only_ret_ty = .{ 7417 .ty = resolved_ret_ty, 7418 .is_generic_inst = true, 7419 .ret_ty_src = func_ret_ty_src, 7420 }, 7421 }, 7422 }, 7423 }; 7424 } 7425 7426 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); 7427 7428 const is_inline_call = block.isComptime() or inline_requested; 7429 7430 if (!is_inline_call) { 7431 if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: { 7432 const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); 7433 errdefer msg.destroy(gpa); 7434 switch (operation) { 7435 .call, .@"@call", .@"@panic", .@"error return" => {}, 7436 .@"safety check" => try sema.errNote(call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}), 7437 } 7438 break :msg msg; 7439 }); 7440 if (func_ty_info.cc == .auto) { 7441 switch (sema.owner.unwrap()) { 7442 .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, 7443 .func => |owner_func| ip.funcSetHasErrorTrace(owner_func, true), 7444 } 7445 } 7446 for (args, 0..) |arg, arg_idx| { 7447 try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg); 7448 } 7449 const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: { 7450 if (!func_ty_info.is_generic) break :func .{ callee, args }; 7451 7452 // Instantiate the generic function! 7453 7454 // This may be an overestimate, but it's definitely sufficient. 7455 const max_runtime_args = args_info.count() - @popCount(func_ty_info.comptime_bits); 7456 var runtime_args: std.ArrayListUnmanaged(Air.Inst.Ref) = try .initCapacity(arena, max_runtime_args); 7457 var runtime_param_tys: std.ArrayListUnmanaged(InternPool.Index) = try .initCapacity(arena, max_runtime_args); 7458 7459 const comptime_args = try arena.alloc(InternPool.Index, args_info.count()); 7460 7461 var noalias_bits: u32 = 0; 7462 7463 for (args, comptime_args, 0..) |arg, *comptime_arg, arg_idx| { 7464 const arg_ty = sema.typeOf(arg); 7465 7466 const is_comptime = c: { 7467 if (std.math.cast(u5, arg_idx)) |i| { 7468 if (func_ty_info.paramIsComptime(i)) { 7469 break :c true; 7470 } 7471 } 7472 break :c try arg_ty.comptimeOnlySema(pt); 7473 }; 7474 const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false; 7475 7476 if (is_comptime) { 7477 // We already emitted an error if the argument isn't comptime-known. 7478 comptime_arg.* = (try sema.resolveValue(arg)).?.toIntern(); 7479 } else { 7480 comptime_arg.* = .none; 7481 if (is_noalias) { 7482 const runtime_idx = runtime_args.items.len; 7483 noalias_bits |= @as(u32, 1) << @intCast(runtime_idx); 7484 } 7485 runtime_args.appendAssumeCapacity(arg); 7486 runtime_param_tys.appendAssumeCapacity(arg_ty.toIntern()); 7487 } 7488 } 7489 7490 const bare_ret_ty = if (fn_zir_info.inferred_error_set) t: { 7491 break :t resolved_ret_ty.errorUnionPayload(zcu); 7492 } else resolved_ret_ty; 7493 7494 // We now need to actually create the function instance. 7495 const func_instance = try ip.getFuncInstance(gpa, pt.tid, .{ 7496 .param_types = runtime_param_tys.items, 7497 .noalias_bits = noalias_bits, 7498 .bare_return_type = bare_ret_ty.toIntern(), 7499 .is_noinline = func_ty_info.is_noinline, 7500 .inferred_error_set = fn_zir_info.inferred_error_set, 7501 .generic_owner = func_val.?.toIntern(), 7502 .comptime_args = comptime_args, 7503 }); 7504 if (zcu.comp.debugIncremental()) { 7505 const nav = ip.indexToKey(func_instance).func.owner_nav; 7506 const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav); 7507 if (!gop.found_existing) gop.value_ptr.* = zcu.generation; 7508 } 7509 7510 // This call is problematic as it breaks guarantees about order-independency of semantic analysis. 7511 // These guarantees are necessary for incremental compilation and parallel semantic analysis. 7512 // See: #22410 7513 zcu.funcInfo(func_instance).maxBranchQuota(ip, sema.branch_quota); 7514 7515 break :func .{ Air.internedToRef(func_instance), runtime_args.items }; 7516 }; 7517 7518 ref_func: { 7519 const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func; 7520 if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func; 7521 try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = runtime_func_val.toIntern() })); 7522 try zcu.ensureFuncBodyAnalysisQueued(runtime_func_val.toIntern()); 7523 } 7524 7525 const call_tag: Air.Inst.Tag = switch (modifier) { 7526 .auto, .no_suspend => .call, 7527 .never_tail => .call_never_tail, 7528 .never_inline => .call_never_inline, 7529 .always_tail => .call_always_tail, 7530 7531 .always_inline, 7532 .compile_time, 7533 => unreachable, 7534 }; 7535 7536 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len); 7537 const maybe_opv = try block.addInst(.{ 7538 .tag = call_tag, 7539 .data = .{ .pl_op = .{ 7540 .operand = runtime_func, 7541 .payload = sema.addExtraAssumeCapacity(Air.Call{ 7542 .args_len = @intCast(runtime_args.len), 7543 }), 7544 } }, 7545 }); 7546 sema.appendRefsAssumeCapacity(runtime_args); 7547 7548 if (ensure_result_used) { 7549 try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src); 7550 } 7551 7552 if (call_tag == .call_always_tail) { 7553 const func_or_ptr_ty = sema.typeOf(runtime_func); 7554 const runtime_func_ty = switch (func_or_ptr_ty.zigTypeTag(zcu)) { 7555 .@"fn" => func_or_ptr_ty, 7556 .pointer => func_or_ptr_ty.childType(zcu), 7557 else => unreachable, 7558 }; 7559 return sema.handleTailCall(block, call_src, runtime_func_ty, maybe_opv); 7560 } 7561 7562 if (ip.isNoReturn(resolved_ret_ty.toIntern())) { 7563 const want_check = c: { 7564 if (!block.wantSafety()) break :c false; 7565 if (func_val != null) break :c false; 7566 break :c true; 7567 }; 7568 if (want_check) { 7569 try sema.safetyPanic(block, call_src, .noreturn_returned); 7570 } else { 7571 _ = try block.addNoOp(.unreach); 7572 } 7573 return .unreachable_value; 7574 } 7575 7576 const result: Air.Inst.Ref = if (try sema.typeHasOnePossibleValue(sema.typeOf(maybe_opv))) |opv| 7577 .fromValue(opv) 7578 else 7579 maybe_opv; 7580 7581 return result; 7582 } 7583 7584 // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`. 7585 7586 if (func_ty_info.is_noinline and !block.isComptime()) { 7587 return sema.fail(block, call_src, "inline call of noinline function", .{}); 7588 } 7589 7590 const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline"; 7591 if (modifier == .never_inline) { 7592 const msg, const fail_block = msg: { 7593 const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type}); 7594 errdefer msg.destroy(gpa); 7595 const fail_block = if (block.isComptime()) b: { 7596 break :b try block.explainWhyBlockIsComptime(msg); 7597 } else block; 7598 break :msg .{ msg, fail_block }; 7599 }; 7600 return sema.failWithOwnedErrorMsg(fail_block, msg); 7601 } 7602 if (func_ty_info.is_var_args) { 7603 const msg, const fail_block = msg: { 7604 const msg = try sema.errMsg(call_src, "{s} call of variadic function", .{call_type}); 7605 errdefer msg.destroy(gpa); 7606 const fail_block = if (block.isComptime()) b: { 7607 break :b try block.explainWhyBlockIsComptime(msg); 7608 } else block; 7609 break :msg .{ msg, fail_block }; 7610 }; 7611 return sema.failWithOwnedErrorMsg(fail_block, msg); 7612 } 7613 if (func_val == null) { 7614 if (func_is_extern) { 7615 const msg, const fail_block = msg: { 7616 const msg = try sema.errMsg(call_src, "{s} call of extern function", .{call_type}); 7617 errdefer msg.destroy(gpa); 7618 const fail_block = if (block.isComptime()) b: { 7619 break :b try block.explainWhyBlockIsComptime(msg); 7620 } else block; 7621 break :msg .{ msg, fail_block }; 7622 }; 7623 return sema.failWithOwnedErrorMsg(fail_block, msg); 7624 } 7625 return sema.failWithNeededComptime( 7626 block, 7627 func_src, 7628 if (block.isComptime()) null else .{ .simple = .inline_call_target }, 7629 ); 7630 } 7631 7632 if (block.isComptime()) { 7633 for (args, 0..) |arg, arg_idx| { 7634 if (!try sema.isComptimeKnown(arg)) { 7635 const arg_src = args_info.argSrc(block, arg_idx); 7636 return sema.failWithNeededComptime(block, arg_src, null); 7637 } 7638 } 7639 } 7640 7641 // For an inline call, we depend on the source code of the whole function definition. 7642 try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index }); 7643 7644 try sema.emitBackwardBranch(block, call_src); 7645 7646 const want_memoize = m: { 7647 // TODO: comptime call memoization is currently not supported under incremental compilation 7648 // since dependencies are not marked on callers. If we want to keep this around (we should 7649 // check that it's worthwhile first!), each memoized call needs an `AnalUnit`. 7650 if (zcu.comp.incremental) break :m false; 7651 if (!block.isComptime()) break :m false; 7652 for (args) |a| { 7653 const val = (try sema.resolveValue(a)).?; 7654 if (val.canMutateComptimeVarState(zcu)) break :m false; 7655 } 7656 break :m true; 7657 }; 7658 const memoized_arg_values: []const InternPool.Index = if (want_memoize) arg_vals: { 7659 const vals = try sema.arena.alloc(InternPool.Index, args.len); 7660 for (vals, args) |*v, a| v.* = (try sema.resolveValue(a)).?.toIntern(); 7661 break :arg_vals vals; 7662 } else undefined; 7663 if (want_memoize) memoize: { 7664 const memoized_call_index = ip.getIfExists(.{ 7665 .memoized_call = .{ 7666 .func = func_val.?.toIntern(), 7667 .arg_values = memoized_arg_values, 7668 .result = undefined, // ignored by hash+eql 7669 .branch_count = undefined, // ignored by hash+eql 7670 }, 7671 }) orelse break :memoize; 7672 const memoized_call = ip.indexToKey(memoized_call_index).memoized_call; 7673 if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) { 7674 // Let the call play out se we get the correct source location for the 7675 // "evaluation exceeded X backwards branches" error. 7676 break :memoize; 7677 } 7678 sema.branch_count += memoized_call.branch_count; 7679 const result = Air.internedToRef(memoized_call.result); 7680 if (ensure_result_used) { 7681 try sema.ensureResultUsed(block, sema.typeOf(result), call_src); 7682 } 7683 return result; 7684 } 7685 7686 var new_ies: InferredErrorSet = .{ .func = .none }; 7687 7688 const old_inst_map = sema.inst_map; 7689 const old_code = sema.code; 7690 const old_func_index = sema.func_index; 7691 const old_fn_ret_ty = sema.fn_ret_ty; 7692 const old_fn_ret_ty_ies = sema.fn_ret_ty_ies; 7693 const old_error_return_trace_index_on_fn_entry = sema.error_return_trace_index_on_fn_entry; 7694 defer { 7695 sema.inst_map.deinit(gpa); 7696 sema.inst_map = old_inst_map; 7697 sema.code = old_code; 7698 sema.func_index = old_func_index; 7699 sema.fn_ret_ty = old_fn_ret_ty; 7700 sema.fn_ret_ty_ies = old_fn_ret_ty_ies; 7701 sema.error_return_trace_index_on_fn_entry = old_error_return_trace_index_on_fn_entry; 7702 } 7703 sema.inst_map = .{}; 7704 sema.code = fn_zir; 7705 sema.func_index = func_val.?.toIntern(); 7706 sema.fn_ret_ty = if (fn_zir_info.inferred_error_set) try pt.errorUnionType( 7707 .fromInterned(.adhoc_inferred_error_set_type), 7708 resolved_ret_ty.errorUnionPayload(zcu), 7709 ) else resolved_ret_ty; 7710 sema.fn_ret_ty_ies = if (fn_zir_info.inferred_error_set) &new_ies else null; 7711 7712 try sema.inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); 7713 for (args, 0..) |arg, arg_idx| { 7714 sema.inst_map.putAssumeCapacityNoClobber(fn_zir_info.param_body[arg_idx], arg); 7715 } 7716 7717 const need_debug_scope = !block.isComptime() and !block.is_typeof and !block.ownerModule().strip; 7718 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 7719 try sema.air_instructions.append(gpa, .{ 7720 .tag = if (need_debug_scope) .dbg_inline_block else .block, 7721 .data = undefined, 7722 }); 7723 7724 var inlining: Block.Inlining = .{ 7725 .call_block = block, 7726 .call_src = call_src, 7727 .func = func_val.?.toIntern(), 7728 .is_generic_instantiation = false, 7729 .has_comptime_args = for (args) |a| { 7730 if (try sema.isComptimeKnown(a)) break true; 7731 } else false, 7732 .comptime_result = undefined, 7733 .merges = .{ 7734 .block_inst = block_inst, 7735 .results = .empty, 7736 .br_list = .empty, 7737 .src_locs = .empty, 7738 }, 7739 }; 7740 var child_block: Block = .{ 7741 .parent = null, 7742 .sema = sema, 7743 .namespace = fn_nav.analysis.?.namespace, 7744 .instructions = .{}, 7745 .inlining = &inlining, 7746 .is_typeof = block.is_typeof, 7747 .comptime_reason = if (block.isComptime()) .inlining_parent else null, 7748 .error_return_trace_index = block.error_return_trace_index, 7749 .runtime_cond = block.runtime_cond, 7750 .runtime_loop = block.runtime_loop, 7751 .runtime_index = block.runtime_index, 7752 .src_base_inst = fn_nav.analysis.?.zir_index, 7753 .type_name_ctx = fn_nav.fqn, 7754 }; 7755 7756 defer child_block.instructions.deinit(gpa); 7757 defer inlining.merges.deinit(gpa); 7758 7759 if (!inlining.has_comptime_args) { 7760 var block_it = block; 7761 while (block_it.inlining) |parent_inlining| { 7762 if (!parent_inlining.is_generic_instantiation and 7763 !parent_inlining.has_comptime_args and 7764 parent_inlining.func == func_val.?.toIntern()) 7765 { 7766 return sema.fail(block, call_src, "inline call is recursive", .{}); 7767 } 7768 block_it = parent_inlining.call_block; 7769 } 7770 } 7771 7772 if (!block.isComptime() and !block.is_typeof) { 7773 const zir_tags = sema.code.instructions.items(.tag); 7774 const zir_datas = sema.code.instructions.items(.data); 7775 for (fn_zir_info.param_body) |inst| switch (zir_tags[@intFromEnum(inst)]) { 7776 .param, .param_comptime => { 7777 const extra = sema.code.extraData(Zir.Inst.Param, zir_datas[@intFromEnum(inst)].pl_tok.payload_index); 7778 const param_name = sema.code.nullTerminatedString(extra.data.name); 7779 const air_inst = sema.inst_map.get(inst).?; 7780 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name); 7781 }, 7782 .param_anytype, .param_anytype_comptime => { 7783 const param_name = zir_datas[@intFromEnum(inst)].str_tok.get(sema.code); 7784 const air_inst = sema.inst_map.get(inst).?; 7785 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name); 7786 }, 7787 else => {}, 7788 }; 7789 } 7790 7791 child_block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block); 7792 // Save the error trace as our first action in the function 7793 // to match the behavior of runtime function calls. 7794 const error_return_trace_index_on_parent_fn_entry = sema.error_return_trace_index_on_fn_entry; 7795 sema.error_return_trace_index_on_fn_entry = child_block.error_return_trace_index; 7796 defer sema.error_return_trace_index_on_fn_entry = error_return_trace_index_on_parent_fn_entry; 7797 7798 // We temporarily set `allow_memoize` to `true` to track this comptime call. 7799 // It is restored after the call finishes analysis, so that a caller may 7800 // know whether an in-progress call (containing this call) may be memoized. 7801 const old_allow_memoize = sema.allow_memoize; 7802 defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize; 7803 sema.allow_memoize = true; 7804 7805 // Store the current eval branch count so we can find out how many eval branches 7806 // the comptime call caused. 7807 const old_branch_count = sema.branch_count; 7808 7809 const result_raw: Air.Inst.Ref = result: { 7810 sema.analyzeFnBody(&child_block, fn_zir_info.body) catch |err| switch (err) { 7811 error.ComptimeReturn => break :result inlining.comptime_result, 7812 else => |e| return e, 7813 }; 7814 break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope); 7815 }; 7816 7817 const maybe_opv: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: { 7818 const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern()); 7819 break :r Air.internedToRef(val_resolved); 7820 } else r: { 7821 const resolved_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result_raw).toIntern()); 7822 if (resolved_ty == .none) break :r result_raw; 7823 // TODO: mutate in place the previous instruction if possible 7824 // rather than adding a bitcast instruction. 7825 break :r try block.addBitCast(.fromInterned(resolved_ty), result_raw); 7826 }; 7827 7828 if (block.isComptime()) { 7829 const result_val = (try sema.resolveValue(maybe_opv)).?; 7830 if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) { 7831 _ = try pt.intern(.{ .memoized_call = .{ 7832 .func = func_val.?.toIntern(), 7833 .arg_values = memoized_arg_values, 7834 .result = result_val.toIntern(), 7835 .branch_count = sema.branch_count - old_branch_count, 7836 } }); 7837 } 7838 } 7839 7840 if (ensure_result_used) { 7841 try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src); 7842 } 7843 7844 return maybe_opv; 7845 } 7846 7847 fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref { 7848 const pt = sema.pt; 7849 const zcu = pt.zcu; 7850 const target = zcu.getTarget(); 7851 const backend = zcu.comp.getZigBackend(); 7852 if (!target_util.supportsTailCall(target, backend)) { 7853 return sema.fail(block, call_src, "unable to perform tail call: compiler backend '{s}' does not support tail calls on target architecture '{s}' with the selected CPU feature flags", .{ 7854 @tagName(backend), @tagName(target.cpu.arch), 7855 }); 7856 } 7857 const owner_func_ty: Type = .fromInterned(zcu.funcInfo(sema.owner.unwrap().func).ty); 7858 if (owner_func_ty.toIntern() != func_ty.toIntern()) { 7859 return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{f}' does not match type of calling function '{f}'", .{ 7860 func_ty.fmt(pt), owner_func_ty.fmt(pt), 7861 }); 7862 } 7863 _ = try block.addUnOp(.ret, result); 7864 return .unreachable_value; 7865 } 7866 7867 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7868 const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type; 7869 const ty = try sema.pt.intType(int_type.signedness, int_type.bit_count); 7870 return Air.internedToRef(ty.toIntern()); 7871 } 7872 7873 fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7874 const tracy = trace(@src()); 7875 defer tracy.end(); 7876 7877 const pt = sema.pt; 7878 const zcu = pt.zcu; 7879 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 7880 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 7881 const child_type = try sema.resolveType(block, operand_src, inst_data.operand); 7882 if (child_type.zigTypeTag(zcu) == .@"opaque") { 7883 return sema.fail(block, operand_src, "opaque type '{f}' cannot be optional", .{child_type.fmt(pt)}); 7884 } else if (child_type.zigTypeTag(zcu) == .null) { 7885 return sema.fail(block, operand_src, "type '{f}' cannot be optional", .{child_type.fmt(pt)}); 7886 } 7887 const opt_type = try pt.optionalType(child_type.toIntern()); 7888 7889 return Air.internedToRef(opt_type.toIntern()); 7890 } 7891 7892 fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7893 const pt = sema.pt; 7894 const zcu = pt.zcu; 7895 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 7896 const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type; 7897 const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu); 7898 try indexable_ty.resolveFields(pt); 7899 assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction 7900 if (indexable_ty.zigTypeTag(zcu) == .@"struct") { 7901 const elem_type = indexable_ty.fieldType(@intFromEnum(bin.rhs), zcu); 7902 return Air.internedToRef(elem_type.toIntern()); 7903 } else { 7904 const elem_type = indexable_ty.elemType2(zcu); 7905 return Air.internedToRef(elem_type.toIntern()); 7906 } 7907 } 7908 7909 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7910 const pt = sema.pt; 7911 const zcu = pt.zcu; 7912 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 7913 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type; 7914 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); 7915 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction 7916 const elem_ty = ptr_ty.childType(zcu); 7917 if (elem_ty.toIntern() == .anyopaque_type) { 7918 // The pointer's actual child type is effectively unknown, so it makes 7919 // sense to represent it with a generic poison. 7920 return .generic_poison_type; 7921 } 7922 return Air.internedToRef(ptr_ty.childType(zcu).toIntern()); 7923 } 7924 7925 fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7926 const pt = sema.pt; 7927 const zcu = pt.zcu; 7928 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 7929 const src = block.nodeOffset(un_node.src_node); 7930 const ptr_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type; 7931 try sema.checkMemOperand(block, src, ptr_ty); 7932 const elem_ty = switch (ptr_ty.ptrSize(zcu)) { 7933 .slice, .many, .c => ptr_ty.childType(zcu), 7934 .one => ptr_ty.childType(zcu).childType(zcu), 7935 }; 7936 return Air.internedToRef(elem_ty.toIntern()); 7937 } 7938 7939 fn zirVecArrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7940 const pt = sema.pt; 7941 const zcu = pt.zcu; 7942 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 7943 const vec_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type; 7944 switch (vec_ty.zigTypeTag(zcu)) { 7945 .array, .vector => {}, 7946 else => return sema.fail(block, block.nodeOffset(un_node.src_node), "expected array or vector type, found '{f}'", .{vec_ty.fmt(pt)}), 7947 } 7948 return Air.internedToRef(vec_ty.childType(zcu).toIntern()); 7949 } 7950 7951 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7952 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 7953 const len_src = block.builtinCallArgSrc(inst_data.src_node, 0); 7954 const elem_type_src = block.builtinCallArgSrc(inst_data.src_node, 1); 7955 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 7956 const len: u32 = @intCast(try sema.resolveInt(block, len_src, extra.lhs, .u32, .{ .simple = .vector_length })); 7957 const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs); 7958 try sema.checkVectorElemType(block, elem_type_src, elem_type); 7959 const vector_type = try sema.pt.vectorType(.{ 7960 .len = len, 7961 .child = elem_type.toIntern(), 7962 }); 7963 return Air.internedToRef(vector_type.toIntern()); 7964 } 7965 7966 fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7967 const tracy = trace(@src()); 7968 defer tracy.end(); 7969 7970 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 7971 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 7972 const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node }); 7973 const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node }); 7974 const len = try sema.resolveInt(block, len_src, extra.lhs, .usize, .{ .simple = .array_length }); 7975 const elem_type = try sema.resolveType(block, elem_src, extra.rhs); 7976 try sema.validateArrayElemType(block, elem_type, elem_src); 7977 const array_ty = try sema.pt.arrayType(.{ 7978 .len = len, 7979 .child = elem_type.toIntern(), 7980 }); 7981 7982 return Air.internedToRef(array_ty.toIntern()); 7983 } 7984 7985 fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7986 const tracy = trace(@src()); 7987 defer tracy.end(); 7988 7989 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 7990 const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; 7991 const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node }); 7992 const sentinel_src = block.src(.{ .node_offset_array_type_sentinel = inst_data.src_node }); 7993 const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node }); 7994 const len = try sema.resolveInt(block, len_src, extra.len, .usize, .{ .simple = .array_length }); 7995 const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); 7996 try sema.validateArrayElemType(block, elem_type, elem_src); 7997 const uncasted_sentinel = try sema.resolveInst(extra.sentinel); 7998 const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); 7999 const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ .simple = .array_sentinel }); 8000 const array_ty = try sema.pt.arrayType(.{ 8001 .len = len, 8002 .sentinel = sentinel_val.toIntern(), 8003 .child = elem_type.toIntern(), 8004 }); 8005 try sema.checkSentinelType(block, sentinel_src, elem_type); 8006 8007 return Air.internedToRef(array_ty.toIntern()); 8008 } 8009 8010 fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void { 8011 const pt = sema.pt; 8012 const zcu = pt.zcu; 8013 if (elem_type.zigTypeTag(zcu) == .@"opaque") { 8014 return sema.fail(block, elem_src, "array of opaque type '{f}' not allowed", .{elem_type.fmt(pt)}); 8015 } else if (elem_type.zigTypeTag(zcu) == .noreturn) { 8016 return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{}); 8017 } 8018 } 8019 8020 fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8021 const tracy = trace(@src()); 8022 defer tracy.end(); 8023 8024 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8025 if (true) { 8026 return sema.failWithUseOfAsync(block, block.nodeOffset(inst_data.src_node)); 8027 } 8028 const zcu = sema.zcu; 8029 const operand_src = block.src(.{ .node_offset_anyframe_type = inst_data.src_node }); 8030 const return_type = try sema.resolveType(block, operand_src, inst_data.operand); 8031 const anyframe_type = try zcu.anyframeType(return_type); 8032 8033 return Air.internedToRef(anyframe_type.toIntern()); 8034 } 8035 8036 fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8037 const tracy = trace(@src()); 8038 defer tracy.end(); 8039 8040 const pt = sema.pt; 8041 const zcu = pt.zcu; 8042 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8043 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8044 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 8045 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 8046 const error_set = try sema.resolveType(block, lhs_src, extra.lhs); 8047 const payload = try sema.resolveType(block, rhs_src, extra.rhs); 8048 8049 if (error_set.zigTypeTag(zcu) != .error_set) { 8050 return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{ 8051 error_set.fmt(pt), 8052 }); 8053 } 8054 try sema.validateErrorUnionPayloadType(block, payload, rhs_src); 8055 const err_union_ty = try pt.errorUnionType(error_set, payload); 8056 return Air.internedToRef(err_union_ty.toIntern()); 8057 } 8058 8059 fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void { 8060 const pt = sema.pt; 8061 const zcu = pt.zcu; 8062 if (payload_ty.zigTypeTag(zcu) == .@"opaque") { 8063 return sema.fail(block, payload_src, "error union with payload of opaque type '{f}' not allowed", .{ 8064 payload_ty.fmt(pt), 8065 }); 8066 } else if (payload_ty.zigTypeTag(zcu) == .error_set) { 8067 return sema.fail(block, payload_src, "error union with payload of error set type '{f}' not allowed", .{ 8068 payload_ty.fmt(pt), 8069 }); 8070 } 8071 } 8072 8073 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8074 _ = block; 8075 const pt = sema.pt; 8076 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 8077 const name = try pt.zcu.intern_pool.getOrPutString( 8078 sema.gpa, 8079 pt.tid, 8080 inst_data.get(sema.code), 8081 .no_embedded_nulls, 8082 ); 8083 _ = try pt.getErrorValue(name); 8084 // Create an error set type with only this error value, and return the value. 8085 const error_set_type = try pt.singleErrorSetType(name); 8086 return Air.internedToRef((try pt.intern(.{ .err = .{ 8087 .ty = error_set_type.toIntern(), 8088 .name = name, 8089 } }))); 8090 } 8091 8092 fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 8093 const tracy = trace(@src()); 8094 defer tracy.end(); 8095 8096 const pt = sema.pt; 8097 const zcu = pt.zcu; 8098 const ip = &zcu.intern_pool; 8099 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 8100 const src = block.nodeOffset(extra.node); 8101 const operand_src = block.builtinCallArgSrc(extra.node, 0); 8102 const uncasted_operand = try sema.resolveInst(extra.operand); 8103 const operand = try sema.coerce(block, .anyerror, uncasted_operand, operand_src); 8104 const err_int_ty = try pt.errorIntType(); 8105 8106 if (try sema.resolveValue(operand)) |val| { 8107 if (val.isUndef(zcu)) { 8108 return pt.undefRef(err_int_ty); 8109 } 8110 const err_name = ip.indexToKey(val.toIntern()).err.name; 8111 return Air.internedToRef((try pt.intValue( 8112 err_int_ty, 8113 try pt.getErrorValue(err_name), 8114 )).toIntern()); 8115 } 8116 8117 const op_ty = sema.typeOf(uncasted_operand); 8118 switch (try sema.resolveInferredErrorSetTy(block, src, op_ty.toIntern())) { 8119 .anyerror_type => {}, 8120 else => |err_set_ty_index| { 8121 const names = ip.indexToKey(err_set_ty_index).error_set_type.names; 8122 switch (names.len) { 8123 0 => return Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()), 8124 1 => return pt.intRef(err_int_ty, ip.getErrorValueIfExists(names.get(ip)[0]).?), 8125 else => {}, 8126 } 8127 }, 8128 } 8129 8130 try sema.requireRuntimeBlock(block, src, operand_src); 8131 return block.addBitCast(err_int_ty, operand); 8132 } 8133 8134 fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 8135 const tracy = trace(@src()); 8136 defer tracy.end(); 8137 8138 const pt = sema.pt; 8139 const zcu = pt.zcu; 8140 const ip = &zcu.intern_pool; 8141 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 8142 const src = block.nodeOffset(extra.node); 8143 const operand_src = block.builtinCallArgSrc(extra.node, 0); 8144 const uncasted_operand = try sema.resolveInst(extra.operand); 8145 const err_int_ty = try pt.errorIntType(); 8146 const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src); 8147 8148 if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { 8149 const int = try sema.usizeCast(block, operand_src, try value.toUnsignedIntSema(pt)); 8150 if (int > len: { 8151 const mutate = &ip.global_error_set.mutate; 8152 mutate.map.mutex.lock(); 8153 defer mutate.map.mutex.unlock(); 8154 break :len mutate.names.len; 8155 } or int == 0) 8156 return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int}); 8157 return Air.internedToRef((try pt.intern(.{ .err = .{ 8158 .ty = .anyerror_type, 8159 .name = ip.global_error_set.shared.names.acquire().view().items(.@"0")[int - 1], 8160 } }))); 8161 } 8162 try sema.requireRuntimeBlock(block, src, operand_src); 8163 if (block.wantSafety()) { 8164 const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); 8165 const zero_val = Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()); 8166 const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val); 8167 const ok = try block.addBinOp(.bool_and, is_lt_len, is_non_zero); 8168 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 8169 } 8170 return block.addInst(.{ 8171 .tag = .bitcast, 8172 .data = .{ .ty_op = .{ 8173 .ty = .anyerror_type, 8174 .operand = operand, 8175 } }, 8176 }); 8177 } 8178 8179 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8180 const tracy = trace(@src()); 8181 defer tracy.end(); 8182 8183 const pt = sema.pt; 8184 const zcu = pt.zcu; 8185 const ip = &zcu.intern_pool; 8186 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8187 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8188 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 8189 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 8190 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 8191 const lhs = try sema.resolveInst(extra.lhs); 8192 const rhs = try sema.resolveInst(extra.rhs); 8193 if (sema.typeOf(lhs).zigTypeTag(zcu) == .bool and sema.typeOf(rhs).zigTypeTag(zcu) == .bool) { 8194 const msg = msg: { 8195 const msg = try sema.errMsg(lhs_src, "expected error set type, found 'bool'", .{}); 8196 errdefer msg.destroy(sema.gpa); 8197 try sema.errNote(src, msg, "'||' merges error sets; 'or' performs boolean OR", .{}); 8198 break :msg msg; 8199 }; 8200 return sema.failWithOwnedErrorMsg(block, msg); 8201 } 8202 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); 8203 const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); 8204 if (lhs_ty.zigTypeTag(zcu) != .error_set) 8205 return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{lhs_ty.fmt(pt)}); 8206 if (rhs_ty.zigTypeTag(zcu) != .error_set) 8207 return sema.fail(block, rhs_src, "expected error set type, found '{f}'", .{rhs_ty.fmt(pt)}); 8208 8209 // Anything merged with anyerror is anyerror. 8210 if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) { 8211 return .anyerror_type; 8212 } 8213 8214 if (ip.isInferredErrorSetType(lhs_ty.toIntern())) { 8215 switch (try sema.resolveInferredErrorSet(block, src, lhs_ty.toIntern())) { 8216 // isAnyError might have changed from a false negative to a true 8217 // positive after resolution. 8218 .anyerror_type => return .anyerror_type, 8219 else => {}, 8220 } 8221 } 8222 if (ip.isInferredErrorSetType(rhs_ty.toIntern())) { 8223 switch (try sema.resolveInferredErrorSet(block, src, rhs_ty.toIntern())) { 8224 // isAnyError might have changed from a false negative to a true 8225 // positive after resolution. 8226 .anyerror_type => return .anyerror_type, 8227 else => {}, 8228 } 8229 } 8230 8231 const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty); 8232 return Air.internedToRef(err_set_ty.toIntern()); 8233 } 8234 8235 fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8236 _ = block; 8237 const tracy = trace(@src()); 8238 defer tracy.end(); 8239 8240 const pt = sema.pt; 8241 const zcu = pt.zcu; 8242 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 8243 const name = inst_data.get(sema.code); 8244 return Air.internedToRef((try pt.intern(.{ 8245 .enum_literal = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls), 8246 }))); 8247 } 8248 8249 fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref { 8250 const tracy = trace(@src()); 8251 defer tracy.end(); 8252 8253 const pt = sema.pt; 8254 const zcu = pt.zcu; 8255 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8256 const src = block.nodeOffset(inst_data.src_node); 8257 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 8258 const name = try zcu.intern_pool.getOrPutString( 8259 sema.gpa, 8260 pt.tid, 8261 sema.code.nullTerminatedString(extra.field_name_start), 8262 .no_embedded_nulls, 8263 ); 8264 8265 const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison; 8266 8267 const uncoerced_result = res: { 8268 if (orig_ty.toIntern() == .generic_poison_type) { 8269 // Treat this as a normal enum literal. 8270 break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name })); 8271 } 8272 8273 var ty = orig_ty; 8274 while (true) switch (ty.zigTypeTag(zcu)) { 8275 .error_union => ty = ty.errorUnionPayload(zcu), 8276 .optional => ty = ty.optionalChild(zcu), 8277 .pointer => ty = if (ty.isSinglePointer(zcu)) ty.childType(zcu) else break, 8278 .enum_literal, .error_set => { 8279 // Treat this as a normal enum literal. 8280 break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name })); 8281 }, 8282 else => break, 8283 }; 8284 8285 break :res try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src); 8286 }; 8287 8288 // Decl literals cannot lookup runtime `var`s. 8289 if (!try sema.isComptimeKnown(uncoerced_result)) { 8290 return sema.fail(block, src, "decl literal must be comptime-known", .{}); 8291 } 8292 8293 if (do_coerce) { 8294 return sema.coerce(block, orig_ty, uncoerced_result, src); 8295 } else { 8296 return uncoerced_result; 8297 } 8298 } 8299 8300 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8301 const pt = sema.pt; 8302 const zcu = pt.zcu; 8303 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8304 const src = block.nodeOffset(inst_data.src_node); 8305 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 8306 const operand = try sema.resolveInst(inst_data.operand); 8307 const operand_ty = sema.typeOf(operand); 8308 8309 const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(zcu)) { 8310 .@"enum" => operand, 8311 .@"union" => blk: { 8312 try operand_ty.resolveFields(pt); 8313 const tag_ty = operand_ty.unionTagType(zcu) orelse { 8314 return sema.fail( 8315 block, 8316 operand_src, 8317 "untagged union '{f}' cannot be converted to integer", 8318 .{operand_ty.fmt(pt)}, 8319 ); 8320 }; 8321 8322 break :blk try sema.unionToTag(block, tag_ty, operand, operand_src); 8323 }, 8324 else => { 8325 return sema.fail(block, operand_src, "expected enum or tagged union, found '{f}'", .{ 8326 operand_ty.fmt(pt), 8327 }); 8328 }, 8329 }; 8330 const enum_tag_ty = sema.typeOf(enum_tag); 8331 const int_tag_ty = enum_tag_ty.intTagType(zcu); 8332 8333 // TODO: use correct solution 8334 // https://github.com/ziglang/zig/issues/15909 8335 if (enum_tag_ty.enumFieldCount(zcu) == 0 and !enum_tag_ty.isNonexhaustiveEnum(zcu)) { 8336 return sema.fail(block, operand_src, "cannot use @intFromEnum on empty enum '{f}'", .{ 8337 enum_tag_ty.fmt(pt), 8338 }); 8339 } 8340 8341 if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| { 8342 return Air.internedToRef((try pt.getCoerced(opv, int_tag_ty)).toIntern()); 8343 } 8344 8345 if (try sema.resolveValue(enum_tag)) |enum_tag_val| { 8346 if (enum_tag_val.isUndef(zcu)) { 8347 return pt.undefRef(int_tag_ty); 8348 } 8349 8350 const val = try enum_tag_val.intFromEnum(enum_tag_ty, pt); 8351 return Air.internedToRef(val.toIntern()); 8352 } 8353 8354 try sema.requireRuntimeBlock(block, src, operand_src); 8355 return block.addBitCast(int_tag_ty, enum_tag); 8356 } 8357 8358 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8359 const pt = sema.pt; 8360 const zcu = pt.zcu; 8361 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8362 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8363 const src = block.nodeOffset(inst_data.src_node); 8364 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 8365 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt"); 8366 const operand = try sema.resolveInst(extra.rhs); 8367 const operand_ty = sema.typeOf(operand); 8368 8369 if (dest_ty.zigTypeTag(zcu) != .@"enum") { 8370 return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)}); 8371 } 8372 _ = try sema.checkIntType(block, operand_src, operand_ty); 8373 8374 if (try sema.resolveValue(operand)) |int_val| { 8375 if (dest_ty.isNonexhaustiveEnum(zcu)) { 8376 const int_tag_ty = dest_ty.intTagType(zcu); 8377 if (try sema.intFitsInType(int_val, int_tag_ty, null)) { 8378 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); 8379 } 8380 return sema.fail(block, src, "int value '{f}' out of range of non-exhaustive enum '{f}'", .{ 8381 int_val.fmtValueSema(pt, sema), dest_ty.fmt(pt), 8382 }); 8383 } 8384 if (int_val.isUndef(zcu)) { 8385 return sema.failWithUseOfUndef(block, operand_src); 8386 } 8387 if (!(try sema.enumHasInt(dest_ty, int_val))) { 8388 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{ 8389 dest_ty.fmt(pt), int_val.fmtValueSema(pt, sema), 8390 }); 8391 } 8392 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); 8393 } 8394 8395 if (dest_ty.intTagType(zcu).zigTypeTag(zcu) == .comptime_int) { 8396 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum }); 8397 } 8398 8399 if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| { 8400 if (block.wantSafety()) { 8401 // The operand is runtime-known but the result is comptime-known. In 8402 // this case we still need a safety check. 8403 const expect_int_val = switch (zcu.intern_pool.indexToKey(opv.toIntern())) { 8404 .enum_tag => |enum_tag| enum_tag.int, 8405 else => unreachable, 8406 }; 8407 const expect_int_coerced = try pt.getCoerced(.fromInterned(expect_int_val), operand_ty); 8408 const ok = try block.addBinOp(.cmp_eq, operand, Air.internedToRef(expect_int_coerced.toIntern())); 8409 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); 8410 } 8411 return Air.internedToRef(opv.toIntern()); 8412 } 8413 8414 try sema.requireRuntimeBlock(block, src, operand_src); 8415 if (block.wantSafety()) { 8416 try sema.preparePanicId(src, .invalid_enum_value); 8417 return block.addTyOp(.intcast_safe, dest_ty, operand); 8418 } 8419 return block.addTyOp(.intcast, dest_ty, operand); 8420 } 8421 8422 /// Pointer in, pointer out. 8423 fn zirOptionalPayloadPtr( 8424 sema: *Sema, 8425 block: *Block, 8426 inst: Zir.Inst.Index, 8427 safety_check: bool, 8428 ) CompileError!Air.Inst.Ref { 8429 const tracy = trace(@src()); 8430 defer tracy.end(); 8431 8432 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8433 const optional_ptr = try sema.resolveInst(inst_data.operand); 8434 const src = block.nodeOffset(inst_data.src_node); 8435 8436 return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); 8437 } 8438 8439 fn analyzeOptionalPayloadPtr( 8440 sema: *Sema, 8441 block: *Block, 8442 src: LazySrcLoc, 8443 optional_ptr: Air.Inst.Ref, 8444 safety_check: bool, 8445 initializing: bool, 8446 ) CompileError!Air.Inst.Ref { 8447 const pt = sema.pt; 8448 const zcu = pt.zcu; 8449 const optional_ptr_ty = sema.typeOf(optional_ptr); 8450 assert(optional_ptr_ty.zigTypeTag(zcu) == .pointer); 8451 8452 const opt_type = optional_ptr_ty.childType(zcu); 8453 if (opt_type.zigTypeTag(zcu) != .optional) { 8454 return sema.failWithExpectedOptionalType(block, src, opt_type); 8455 } 8456 8457 const child_type = opt_type.optionalChild(zcu); 8458 const child_pointer = try pt.ptrTypeSema(.{ 8459 .child = child_type.toIntern(), 8460 .flags = .{ 8461 .is_const = optional_ptr_ty.isConstPtr(zcu), 8462 .address_space = optional_ptr_ty.ptrAddressSpace(zcu), 8463 }, 8464 }); 8465 8466 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| { 8467 if (initializing) { 8468 if (sema.isComptimeMutablePtr(ptr_val)) { 8469 // Set the optional to non-null at comptime. 8470 // If the payload is OPV, we must use that value instead of undef. 8471 const payload_val = try sema.typeHasOnePossibleValue(child_type) orelse try pt.undefValue(child_type); 8472 const opt_val = try pt.intern(.{ .opt = .{ 8473 .ty = opt_type.toIntern(), 8474 .val = payload_val.toIntern(), 8475 } }); 8476 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(opt_val), opt_type); 8477 } else { 8478 // Emit runtime instructions to set the optional non-null bit. 8479 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); 8480 try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr); 8481 } 8482 return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern()); 8483 } 8484 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| { 8485 if (val.isNull(zcu)) { 8486 return sema.fail(block, src, "unable to unwrap null", .{}); 8487 } 8488 return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern()); 8489 } 8490 } 8491 8492 try sema.requireRuntimeBlock(block, src, null); 8493 if (safety_check and block.wantSafety()) { 8494 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); 8495 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 8496 } 8497 8498 if (initializing) { 8499 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); 8500 try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr); 8501 return opt_payload_ptr; 8502 } else { 8503 return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr); 8504 } 8505 } 8506 8507 /// Value in, value out. 8508 fn zirOptionalPayload( 8509 sema: *Sema, 8510 block: *Block, 8511 inst: Zir.Inst.Index, 8512 safety_check: bool, 8513 ) CompileError!Air.Inst.Ref { 8514 const tracy = trace(@src()); 8515 defer tracy.end(); 8516 8517 const pt = sema.pt; 8518 const zcu = pt.zcu; 8519 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8520 const src = block.nodeOffset(inst_data.src_node); 8521 const operand = try sema.resolveInst(inst_data.operand); 8522 const operand_ty = sema.typeOf(operand); 8523 const result_ty = switch (operand_ty.zigTypeTag(zcu)) { 8524 .optional => operand_ty.optionalChild(zcu), 8525 .pointer => t: { 8526 if (operand_ty.ptrSize(zcu) != .c) { 8527 return sema.failWithExpectedOptionalType(block, src, operand_ty); 8528 } 8529 // TODO https://github.com/ziglang/zig/issues/6597 8530 if (true) break :t operand_ty; 8531 const ptr_info = operand_ty.ptrInfo(zcu); 8532 break :t try pt.ptrTypeSema(.{ 8533 .child = ptr_info.child, 8534 .flags = .{ 8535 .alignment = ptr_info.flags.alignment, 8536 .is_const = ptr_info.flags.is_const, 8537 .is_volatile = ptr_info.flags.is_volatile, 8538 .is_allowzero = ptr_info.flags.is_allowzero, 8539 .address_space = ptr_info.flags.address_space, 8540 }, 8541 }); 8542 }, 8543 else => return sema.failWithExpectedOptionalType(block, src, operand_ty), 8544 }; 8545 8546 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 8547 if (val.optionalValue(zcu)) |payload| return Air.internedToRef(payload.toIntern()); 8548 if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{}); 8549 if (safety_check and block.wantSafety()) { 8550 try sema.safetyPanic(block, src, .unwrap_null); 8551 } else { 8552 _ = try block.addNoOp(.unreach); 8553 } 8554 return .unreachable_value; 8555 } 8556 8557 try sema.requireRuntimeBlock(block, src, null); 8558 if (safety_check and block.wantSafety()) { 8559 const is_non_null = try block.addUnOp(.is_non_null, operand); 8560 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 8561 } 8562 return block.addTyOp(.optional_payload, result_ty, operand); 8563 } 8564 8565 /// Value in, value out 8566 fn zirErrUnionPayload( 8567 sema: *Sema, 8568 block: *Block, 8569 inst: Zir.Inst.Index, 8570 ) CompileError!Air.Inst.Ref { 8571 const tracy = trace(@src()); 8572 defer tracy.end(); 8573 8574 const pt = sema.pt; 8575 const zcu = pt.zcu; 8576 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8577 const src = block.nodeOffset(inst_data.src_node); 8578 const operand = try sema.resolveInst(inst_data.operand); 8579 const operand_src = src; 8580 const err_union_ty = sema.typeOf(operand); 8581 if (err_union_ty.zigTypeTag(zcu) != .error_union) { 8582 return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{ 8583 err_union_ty.fmt(pt), 8584 }); 8585 } 8586 return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); 8587 } 8588 8589 fn analyzeErrUnionPayload( 8590 sema: *Sema, 8591 block: *Block, 8592 src: LazySrcLoc, 8593 err_union_ty: Type, 8594 operand: Air.Inst.Ref, 8595 operand_src: LazySrcLoc, 8596 safety_check: bool, 8597 ) CompileError!Air.Inst.Ref { 8598 const pt = sema.pt; 8599 const zcu = pt.zcu; 8600 const payload_ty = err_union_ty.errorUnionPayload(zcu); 8601 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 8602 if (val.getErrorName(zcu).unwrap()) |name| { 8603 return sema.failWithComptimeErrorRetTrace(block, src, name); 8604 } 8605 return Air.internedToRef(zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.payload); 8606 } 8607 8608 try sema.requireRuntimeBlock(block, src, null); 8609 8610 // If the error set has no fields then no safety check is needed. 8611 if (safety_check and block.wantSafety() and 8612 !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) 8613 { 8614 try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); 8615 } 8616 8617 if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_only_value| { 8618 return Air.internedToRef(payload_only_value.toIntern()); 8619 } 8620 8621 return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); 8622 } 8623 8624 /// Pointer in, pointer out. 8625 fn zirErrUnionPayloadPtr( 8626 sema: *Sema, 8627 block: *Block, 8628 inst: Zir.Inst.Index, 8629 ) CompileError!Air.Inst.Ref { 8630 const tracy = trace(@src()); 8631 defer tracy.end(); 8632 8633 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8634 const operand = try sema.resolveInst(inst_data.operand); 8635 const src = block.nodeOffset(inst_data.src_node); 8636 8637 return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); 8638 } 8639 8640 fn analyzeErrUnionPayloadPtr( 8641 sema: *Sema, 8642 block: *Block, 8643 src: LazySrcLoc, 8644 operand: Air.Inst.Ref, 8645 safety_check: bool, 8646 initializing: bool, 8647 ) CompileError!Air.Inst.Ref { 8648 const pt = sema.pt; 8649 const zcu = pt.zcu; 8650 const operand_ty = sema.typeOf(operand); 8651 assert(operand_ty.zigTypeTag(zcu) == .pointer); 8652 8653 if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) { 8654 return sema.fail(block, src, "expected error union type, found '{f}'", .{ 8655 operand_ty.childType(zcu).fmt(pt), 8656 }); 8657 } 8658 8659 const err_union_ty = operand_ty.childType(zcu); 8660 const payload_ty = err_union_ty.errorUnionPayload(zcu); 8661 const operand_pointer_ty = try pt.ptrTypeSema(.{ 8662 .child = payload_ty.toIntern(), 8663 .flags = .{ 8664 .is_const = operand_ty.isConstPtr(zcu), 8665 .address_space = operand_ty.ptrAddressSpace(zcu), 8666 }, 8667 }); 8668 8669 if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| { 8670 if (initializing) { 8671 if (sema.isComptimeMutablePtr(ptr_val)) { 8672 // Set the error union to non-error at comptime. 8673 // If the payload is OPV, we must use that value instead of undef. 8674 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); 8675 const eu_val = try pt.intern(.{ .error_union = .{ 8676 .ty = err_union_ty.toIntern(), 8677 .val = .{ .payload = payload_val.toIntern() }, 8678 } }); 8679 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(eu_val), err_union_ty); 8680 } else { 8681 // Emit runtime instructions to set the error union error code. 8682 try sema.requireRuntimeBlock(block, src, null); 8683 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); 8684 try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr); 8685 } 8686 return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern()); 8687 } 8688 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| { 8689 if (val.getErrorName(zcu).unwrap()) |name| { 8690 return sema.failWithComptimeErrorRetTrace(block, src, name); 8691 } 8692 return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern()); 8693 } 8694 } 8695 8696 try sema.requireRuntimeBlock(block, src, null); 8697 8698 // If the error set has no fields then no safety check is needed. 8699 if (safety_check and block.wantSafety() and 8700 !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) 8701 { 8702 try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); 8703 } 8704 8705 if (initializing) { 8706 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); 8707 try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr); 8708 return eu_payload_ptr; 8709 } else { 8710 return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand); 8711 } 8712 } 8713 8714 /// Value in, value out 8715 fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8716 const tracy = trace(@src()); 8717 defer tracy.end(); 8718 8719 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8720 const src = block.nodeOffset(inst_data.src_node); 8721 const operand = try sema.resolveInst(inst_data.operand); 8722 return sema.analyzeErrUnionCode(block, src, operand); 8723 } 8724 8725 /// If `operand` is comptime-known, asserts that it is an error value rather than a payload value. 8726 fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { 8727 const pt = sema.pt; 8728 const zcu = pt.zcu; 8729 const operand_ty = sema.typeOf(operand); 8730 if (operand_ty.zigTypeTag(zcu) != .error_union) { 8731 return sema.fail(block, src, "expected error union type, found '{f}'", .{ 8732 operand_ty.fmt(pt), 8733 }); 8734 } 8735 8736 const result_ty = operand_ty.errorUnionSet(zcu); 8737 8738 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 8739 return Air.internedToRef((try pt.intern(.{ .err = .{ 8740 .ty = result_ty.toIntern(), 8741 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, 8742 } }))); 8743 } 8744 8745 try sema.requireRuntimeBlock(block, src, null); 8746 return block.addTyOp(.unwrap_errunion_err, result_ty, operand); 8747 } 8748 8749 /// Pointer in, value out 8750 fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8751 const tracy = trace(@src()); 8752 defer tracy.end(); 8753 8754 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8755 const src = block.nodeOffset(inst_data.src_node); 8756 const operand = try sema.resolveInst(inst_data.operand); 8757 return sema.analyzeErrUnionCodePtr(block, src, operand); 8758 } 8759 8760 fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { 8761 const pt = sema.pt; 8762 const zcu = pt.zcu; 8763 const operand_ty = sema.typeOf(operand); 8764 assert(operand_ty.zigTypeTag(zcu) == .pointer); 8765 8766 if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) { 8767 return sema.fail(block, src, "expected error union type, found '{f}'", .{ 8768 operand_ty.childType(zcu).fmt(pt), 8769 }); 8770 } 8771 8772 const result_ty = operand_ty.childType(zcu).errorUnionSet(zcu); 8773 8774 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { 8775 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { 8776 assert(val.getErrorName(zcu) != .none); 8777 return Air.internedToRef((try pt.intern(.{ .err = .{ 8778 .ty = result_ty.toIntern(), 8779 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, 8780 } }))); 8781 } 8782 } 8783 8784 try sema.requireRuntimeBlock(block, src, null); 8785 return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); 8786 } 8787 8788 fn zirFunc( 8789 sema: *Sema, 8790 block: *Block, 8791 inst: Zir.Inst.Index, 8792 inferred_error_set: bool, 8793 ) CompileError!Air.Inst.Ref { 8794 const pt = sema.pt; 8795 const zcu = pt.zcu; 8796 const ip = &zcu.intern_pool; 8797 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8798 const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); 8799 const target = zcu.getTarget(); 8800 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node }); 8801 const src = block.nodeOffset(inst_data.src_node); 8802 8803 var extra_index = extra.end; 8804 8805 const ret_ty: Type = if (extra.data.ret_ty.is_generic) 8806 .generic_poison 8807 else switch (extra.data.ret_ty.body_len) { 8808 0 => .void, 8809 1 => blk: { 8810 const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 8811 extra_index += 1; 8812 break :blk try sema.resolveType(block, ret_ty_src, ret_ty_ref); 8813 }, 8814 else => blk: { 8815 const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_ty.body_len); 8816 extra_index += ret_ty_body.len; 8817 8818 const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, .type, .{ .simple = .function_ret_ty }); 8819 break :blk ret_ty_val.toType(); 8820 }, 8821 }; 8822 8823 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 8824 const has_body = extra.data.body_len != 0; 8825 if (has_body) { 8826 extra_index += extra.data.body_len; 8827 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 8828 } 8829 8830 // If this instruction has a body, then it's a function declaration, and we decide 8831 // the callconv based on whether it is exported. Otherwise, the callconv defaults 8832 // to `.auto`. 8833 const cc: std.builtin.CallingConvention = if (has_body) cc: { 8834 const func_decl_nav = sema.owner.unwrap().nav_val; 8835 const fn_is_exported = exported: { 8836 const decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(ip) orelse return error.AnalysisFail; 8837 const zir_decl = sema.code.getDeclaration(decl_inst); 8838 break :exported zir_decl.linkage == .@"export"; 8839 }; 8840 if (fn_is_exported) { 8841 break :cc target.cCallingConvention() orelse { 8842 // This target has no default C calling convention. We sometimes trigger a similar 8843 // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency, 8844 // let's eval that now and just get the transitive error. (It's guaranteed to error 8845 // because it does the exact `cCallingConvention` call we just did.) 8846 const cc_type = try sema.getBuiltinType(src, .CallingConvention); 8847 _ = try sema.namespaceLookupVal( 8848 block, 8849 LazySrcLoc.unneeded, 8850 cc_type.getNamespaceIndex(zcu), 8851 try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls), 8852 ); 8853 // The above should have errored. 8854 @panic("std.builtin is corrupt"); 8855 }; 8856 } else { 8857 break :cc .auto; 8858 } 8859 } else .auto; 8860 8861 return sema.funcCommon( 8862 block, 8863 inst_data.src_node, 8864 inst, 8865 cc, 8866 ret_ty, 8867 false, 8868 inferred_error_set, 8869 has_body, 8870 src_locs, 8871 0, 8872 false, 8873 ); 8874 } 8875 8876 fn resolveGenericBody( 8877 sema: *Sema, 8878 block: *Block, 8879 src: LazySrcLoc, 8880 body: []const Zir.Inst.Index, 8881 func_inst: Zir.Inst.Index, 8882 dest_ty: Type, 8883 reason: ComptimeReason, 8884 ) !Value { 8885 assert(body.len != 0); 8886 8887 // Make sure any nested param instructions don't clobber our work. 8888 const prev_params = block.params; 8889 block.params = .{}; 8890 defer { 8891 block.params = prev_params; 8892 } 8893 8894 const uncasted = try sema.resolveInlineBody(block, body, func_inst); 8895 const result = try sema.coerce(block, dest_ty, uncasted, src); 8896 return sema.resolveConstDefinedValue(block, src, result, reason); 8897 } 8898 8899 pub fn handleExternLibName( 8900 sema: *Sema, 8901 block: *Block, 8902 src_loc: LazySrcLoc, 8903 lib_name: []const u8, 8904 ) CompileError!void { 8905 blk: { 8906 const pt = sema.pt; 8907 const zcu = pt.zcu; 8908 const comp = zcu.comp; 8909 const target = zcu.getTarget(); 8910 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); 8911 if (std.zig.target.isLibCLibName(target, lib_name)) { 8912 if (!comp.config.link_libc) { 8913 return sema.fail( 8914 block, 8915 src_loc, 8916 "dependency on libc must be explicitly specified in the build command", 8917 .{}, 8918 ); 8919 } 8920 break :blk; 8921 } 8922 if (std.zig.target.isLibCxxLibName(target, lib_name)) { 8923 if (!comp.config.link_libcpp) return sema.fail( 8924 block, 8925 src_loc, 8926 "dependency on libc++ must be explicitly specified in the build command", 8927 .{}, 8928 ); 8929 break :blk; 8930 } 8931 if (mem.eql(u8, lib_name, "unwind")) { 8932 if (!comp.config.link_libunwind) return sema.fail( 8933 block, 8934 src_loc, 8935 "dependency on libunwind must be explicitly specified in the build command", 8936 .{}, 8937 ); 8938 break :blk; 8939 } 8940 if (!target.cpu.arch.isWasm() and !block.ownerModule().pic) { 8941 return sema.fail( 8942 block, 8943 src_loc, 8944 "dependency on dynamic library '{s}' requires enabling Position Independent Code; fixed by '-l{s}' or '-fPIC'", 8945 .{ lib_name, lib_name }, 8946 ); 8947 } 8948 } 8949 } 8950 8951 /// These are calling conventions that are confirmed to work with variadic functions. 8952 /// Any calling conventions not included here are either not yet verified to work with variadic 8953 /// functions or there are no more other calling conventions that support variadic functions. 8954 const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention.Tag{ 8955 .x86_64_sysv, 8956 .x86_64_win, 8957 .x86_sysv, 8958 .x86_win, 8959 .aarch64_aapcs, 8960 .aarch64_aapcs_darwin, 8961 .aarch64_aapcs_win, 8962 .aarch64_vfabi, 8963 .aarch64_vfabi_sve, 8964 .arm_aapcs, 8965 .arm_aapcs_vfp, 8966 .mips64_n64, 8967 .mips64_n32, 8968 .mips_o32, 8969 .riscv64_lp64, 8970 .riscv64_lp64_v, 8971 .riscv32_ilp32, 8972 .riscv32_ilp32_v, 8973 .sparc64_sysv, 8974 .sparc_sysv, 8975 .powerpc64_elf, 8976 .powerpc64_elf_altivec, 8977 .powerpc64_elf_v2, 8978 .powerpc_sysv, 8979 .powerpc_sysv_altivec, 8980 .powerpc_aix, 8981 .powerpc_aix_altivec, 8982 .wasm_mvp, 8983 .arc_sysv, 8984 .avr_gnu, 8985 .bpf_std, 8986 .csky_sysv, 8987 .hexagon_sysv, 8988 .hexagon_sysv_hvx, 8989 .lanai_sysv, 8990 .loongarch64_lp64, 8991 .loongarch32_ilp32, 8992 .m68k_sysv, 8993 .m68k_gnu, 8994 .m68k_rtd, 8995 .msp430_eabi, 8996 .s390x_sysv, 8997 .s390x_sysv_vx, 8998 .ve_sysv, 8999 .xcore_xs1, 9000 .xcore_xs2, 9001 .xtensa_call0, 9002 .xtensa_windowed, 9003 }; 9004 fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool { 9005 return for (calling_conventions_supporting_var_args) |supported_cc| { 9006 if (cc == supported_cc) return true; 9007 } else false; 9008 } 9009 fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void { 9010 const CallingConventionsSupportingVarArgsList = struct { 9011 arch: std.Target.Cpu.Arch, 9012 pub fn format(ctx: @This(), w: *std.io.Writer) std.io.Writer.Error!void { 9013 var first = true; 9014 for (calling_conventions_supporting_var_args) |cc_inner| { 9015 for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| { 9016 if (supported_arch == ctx.arch) break; 9017 } else continue; // callconv not supported by this arch 9018 if (!first) { 9019 try w.writeAll(", "); 9020 } 9021 first = false; 9022 try w.print("'{s}'", .{@tagName(cc_inner)}); 9023 } 9024 } 9025 }; 9026 9027 if (!callConvSupportsVarArgs(cc)) { 9028 return sema.failWithOwnedErrorMsg(block, msg: { 9029 const msg = try sema.errMsg(src, "variadic function does not support '{s}' calling convention", .{@tagName(cc)}); 9030 errdefer msg.destroy(sema.gpa); 9031 const target = sema.pt.zcu.getTarget(); 9032 try sema.errNote(src, msg, "supported calling conventions: {f}", .{CallingConventionsSupportingVarArgsList{ .arch = target.cpu.arch }}); 9033 break :msg msg; 9034 }); 9035 } 9036 } 9037 9038 fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool { 9039 return switch (cc) { 9040 .naked, 9041 9042 .arm_interrupt, 9043 .avr_interrupt, 9044 .avr_signal, 9045 .csky_interrupt, 9046 .m68k_interrupt, 9047 .mips_interrupt, 9048 .mips64_interrupt, 9049 .riscv32_interrupt, 9050 .riscv64_interrupt, 9051 .x86_interrupt, 9052 .x86_64_interrupt, 9053 9054 .amdgcn_kernel, 9055 .nvptx_kernel, 9056 .spirv_kernel, 9057 .spirv_fragment, 9058 .spirv_vertex, 9059 => false, 9060 9061 else => true, 9062 }; 9063 } 9064 9065 fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void { 9066 const pt = sema.pt; 9067 const zcu = pt.zcu; 9068 const target = zcu.getTarget(); 9069 9070 if (!peer_ty.isPtrAtRuntime(zcu)) { 9071 return; 9072 } 9073 9074 const as = peer_ty.ptrAddressSpace(zcu); 9075 if (!target_util.arePointersLogical(target, as)) { 9076 return; 9077 } 9078 9079 return sema.failWithOwnedErrorMsg(block, msg: { 9080 const msg = try sema.errMsg(src, "value with non-mergable pointer type '{f}' depends on runtime control flow", .{peer_ty.fmt(pt)}); 9081 errdefer msg.destroy(sema.gpa); 9082 9083 const runtime_src = block.runtime_cond orelse block.runtime_loop.?; 9084 try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); 9085 9086 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm); 9087 try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target {s}-{s} by compiler backend {s}", .{ 9088 @tagName(as), 9089 @tagName(target.cpu.arch.family()), 9090 @tagName(target.os.tag), 9091 @tagName(backend), 9092 }); 9093 9094 break :msg msg; 9095 }); 9096 } 9097 9098 const Section = union(enum) { 9099 generic, 9100 default, 9101 explicit: InternPool.NullTerminatedString, 9102 }; 9103 9104 fn funcCommon( 9105 sema: *Sema, 9106 block: *Block, 9107 src_node_offset: std.zig.Ast.Node.Offset, 9108 func_inst: Zir.Inst.Index, 9109 cc: std.builtin.CallingConvention, 9110 /// this might be Type.generic_poison 9111 bare_return_type: Type, 9112 var_args: bool, 9113 inferred_error_set: bool, 9114 has_body: bool, 9115 src_locs: Zir.Inst.Func.SrcLocs, 9116 noalias_bits: u32, 9117 is_noinline: bool, 9118 ) CompileError!Air.Inst.Ref { 9119 const pt = sema.pt; 9120 const zcu = pt.zcu; 9121 const gpa = sema.gpa; 9122 const target = zcu.getTarget(); 9123 const ip = &zcu.intern_pool; 9124 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset }); 9125 const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset }); 9126 const func_src = block.nodeOffset(src_node_offset); 9127 9128 const ret_ty_requires_comptime = try bare_return_type.comptimeOnlySema(pt); 9129 var is_generic = bare_return_type.isGenericPoison() or ret_ty_requires_comptime; 9130 9131 var comptime_bits: u32 = 0; 9132 for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| { 9133 const param_ty: Type = .fromInterned(param_ty_ip); 9134 const is_noalias = blk: { 9135 const index = std.math.cast(u5, i) orelse break :blk false; 9136 break :blk @as(u1, @truncate(noalias_bits >> index)) != 0; 9137 }; 9138 const param_src = block.src(.{ .fn_proto_param = .{ 9139 .fn_proto_node_offset = src_node_offset, 9140 .param_index = @intCast(i), 9141 } }); 9142 const param_ty_comptime = try param_ty.comptimeOnlySema(pt); 9143 const param_ty_generic = param_ty.isGenericPoison(); 9144 if (param_is_comptime or param_ty_comptime or param_ty_generic) { 9145 is_generic = true; 9146 } 9147 if (param_is_comptime) { 9148 comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error 9149 } 9150 if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(cc)) { 9151 return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); 9152 } 9153 if (param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc)) { 9154 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); 9155 } 9156 if (!param_ty.isValidParamType(zcu)) { 9157 const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; 9158 return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{ 9159 opaque_str, param_ty.fmt(pt), 9160 }); 9161 } 9162 if (!param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc) and !try sema.validateExternType(param_ty, .param_ty)) { 9163 const msg = msg: { 9164 const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{s}'", .{ 9165 param_ty.fmt(pt), @tagName(cc), 9166 }); 9167 errdefer msg.destroy(sema.gpa); 9168 9169 try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty); 9170 9171 try sema.addDeclaredHereNote(msg, param_ty); 9172 break :msg msg; 9173 }; 9174 return sema.failWithOwnedErrorMsg(block, msg); 9175 } 9176 if (param_ty_comptime and !param_is_comptime and has_body and !block.isComptime()) { 9177 const msg = msg: { 9178 const msg = try sema.errMsg(param_src, "parameter of type '{f}' must be declared comptime", .{ 9179 param_ty.fmt(pt), 9180 }); 9181 errdefer msg.destroy(sema.gpa); 9182 9183 try sema.explainWhyTypeIsComptime(msg, param_src, param_ty); 9184 9185 try sema.addDeclaredHereNote(msg, param_ty); 9186 break :msg msg; 9187 }; 9188 return sema.failWithOwnedErrorMsg(block, msg); 9189 } 9190 if (!param_ty_generic and is_noalias and 9191 !(param_ty.zigTypeTag(zcu) == .pointer or param_ty.isPtrLikeOptional(zcu))) 9192 { 9193 return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); 9194 } 9195 switch (cc) { 9196 .x86_64_interrupt, .x86_interrupt => { 9197 const err_code_size = target.ptrBitWidth(); 9198 switch (i) { 9199 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{s}' calling convention must be a pointer type", .{@tagName(cc)}), 9200 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{s}' calling convention must be a {d}-bit integer", .{ @tagName(cc), err_code_size }), 9201 else => return sema.fail(block, param_src, "'{s}' calling convention supports up to 2 parameters, found {d}", .{ @tagName(cc), i + 1 }), 9202 } 9203 }, 9204 .arm_interrupt, 9205 .mips64_interrupt, 9206 .mips_interrupt, 9207 .riscv64_interrupt, 9208 .riscv32_interrupt, 9209 .avr_interrupt, 9210 .csky_interrupt, 9211 .m68k_interrupt, 9212 .avr_signal, 9213 => return sema.fail(block, param_src, "parameters are not allowed with '{s}' calling convention", .{@tagName(cc)}), 9214 else => {}, 9215 } 9216 } 9217 9218 if (var_args) { 9219 if (is_generic) { 9220 return sema.fail(block, func_src, "generic function cannot be variadic", .{}); 9221 } 9222 const va_args_src = block.src(.{ 9223 .fn_proto_param = .{ 9224 .fn_proto_node_offset = src_node_offset, 9225 .param_index = @intCast(block.params.len), // va_arg must be the last parameter 9226 }, 9227 }); 9228 try sema.checkCallConvSupportsVarArgs(block, va_args_src, cc); 9229 } 9230 9231 const ret_poison = bare_return_type.isGenericPoison(); 9232 9233 const param_types = block.params.items(.ty); 9234 9235 if (inferred_error_set) { 9236 assert(has_body); 9237 if (!ret_poison) 9238 try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); 9239 const func_index = try ip.getFuncDeclIes(gpa, pt.tid, .{ 9240 .owner_nav = sema.owner.unwrap().nav_val, 9241 9242 .param_types = param_types, 9243 .noalias_bits = noalias_bits, 9244 .comptime_bits = comptime_bits, 9245 .bare_return_type = bare_return_type.toIntern(), 9246 .cc = cc, 9247 .is_var_args = var_args, 9248 .is_generic = is_generic, 9249 .is_noinline = is_noinline, 9250 9251 .zir_body_inst = try block.trackZir(func_inst), 9252 .lbrace_line = src_locs.lbrace_line, 9253 .rbrace_line = src_locs.rbrace_line, 9254 .lbrace_column = @as(u16, @truncate(src_locs.columns)), 9255 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), 9256 }); 9257 return finishFunc( 9258 sema, 9259 block, 9260 func_index, 9261 .none, 9262 ret_poison, 9263 bare_return_type, 9264 ret_ty_src, 9265 cc, 9266 ret_ty_requires_comptime, 9267 func_inst, 9268 cc_src, 9269 is_noinline, 9270 ); 9271 } 9272 9273 const func_ty = try ip.getFuncType(gpa, pt.tid, .{ 9274 .param_types = param_types, 9275 .noalias_bits = noalias_bits, 9276 .comptime_bits = comptime_bits, 9277 .return_type = bare_return_type.toIntern(), 9278 .cc = cc, 9279 .is_var_args = var_args, 9280 .is_generic = is_generic, 9281 .is_noinline = is_noinline, 9282 }); 9283 9284 if (has_body) { 9285 const func_index = try ip.getFuncDecl(gpa, pt.tid, .{ 9286 .owner_nav = sema.owner.unwrap().nav_val, 9287 .ty = func_ty, 9288 .cc = cc, 9289 .is_noinline = is_noinline, 9290 .zir_body_inst = try block.trackZir(func_inst), 9291 .lbrace_line = src_locs.lbrace_line, 9292 .rbrace_line = src_locs.rbrace_line, 9293 .lbrace_column = @as(u16, @truncate(src_locs.columns)), 9294 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), 9295 }); 9296 return finishFunc( 9297 sema, 9298 block, 9299 func_index, 9300 func_ty, 9301 ret_poison, 9302 bare_return_type, 9303 ret_ty_src, 9304 cc, 9305 ret_ty_requires_comptime, 9306 func_inst, 9307 cc_src, 9308 is_noinline, 9309 ); 9310 } 9311 9312 return finishFunc( 9313 sema, 9314 block, 9315 .none, 9316 func_ty, 9317 ret_poison, 9318 bare_return_type, 9319 ret_ty_src, 9320 cc, 9321 ret_ty_requires_comptime, 9322 func_inst, 9323 cc_src, 9324 is_noinline, 9325 ); 9326 } 9327 9328 fn finishFunc( 9329 sema: *Sema, 9330 block: *Block, 9331 opt_func_index: InternPool.Index, 9332 func_ty: InternPool.Index, 9333 ret_poison: bool, 9334 bare_return_type: Type, 9335 ret_ty_src: LazySrcLoc, 9336 cc_resolved: std.builtin.CallingConvention, 9337 ret_ty_requires_comptime: bool, 9338 func_inst: Zir.Inst.Index, 9339 cc_src: LazySrcLoc, 9340 is_noinline: bool, 9341 ) CompileError!Air.Inst.Ref { 9342 const pt = sema.pt; 9343 const zcu = pt.zcu; 9344 const ip = &zcu.intern_pool; 9345 const gpa = sema.gpa; 9346 9347 const return_type: Type = if (opt_func_index == .none or ret_poison) 9348 bare_return_type 9349 else 9350 .fromInterned(ip.funcTypeReturnType(ip.typeOf(opt_func_index))); 9351 9352 if (!return_type.isValidReturnType(zcu)) { 9353 const opaque_str = if (return_type.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; 9354 return sema.fail(block, ret_ty_src, "{s}return type '{f}' not allowed", .{ 9355 opaque_str, return_type.fmt(pt), 9356 }); 9357 } 9358 if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(cc_resolved) and 9359 !try sema.validateExternType(return_type, .ret_ty)) 9360 { 9361 const msg = msg: { 9362 const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{s}'", .{ 9363 return_type.fmt(pt), @tagName(cc_resolved), 9364 }); 9365 errdefer msg.destroy(gpa); 9366 9367 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, return_type, .ret_ty); 9368 9369 try sema.addDeclaredHereNote(msg, return_type); 9370 break :msg msg; 9371 }; 9372 return sema.failWithOwnedErrorMsg(block, msg); 9373 } 9374 9375 // If the return type is comptime-only but not dependent on parameters then 9376 // all parameter types also need to be comptime. 9377 if (opt_func_index != .none and ret_ty_requires_comptime and !block.isComptime()) comptime_check: { 9378 for (block.params.items(.is_comptime)) |is_comptime| { 9379 if (!is_comptime) break; 9380 } else break :comptime_check; 9381 9382 const msg = try sema.errMsg( 9383 ret_ty_src, 9384 "function with comptime-only return type '{f}' requires all parameters to be comptime", 9385 .{return_type.fmt(pt)}, 9386 ); 9387 errdefer msg.destroy(sema.gpa); 9388 try sema.explainWhyTypeIsComptime(msg, ret_ty_src, return_type); 9389 9390 const tags = sema.code.instructions.items(.tag); 9391 const data = sema.code.instructions.items(.data); 9392 const param_body = sema.code.getParamBody(func_inst); 9393 for ( 9394 block.params.items(.is_comptime), 9395 block.params.items(.name), 9396 param_body[0..block.params.len], 9397 ) |is_comptime, name_nts, param_index| { 9398 if (!is_comptime) { 9399 const param_src = block.tokenOffset(switch (tags[@intFromEnum(param_index)]) { 9400 .param => data[@intFromEnum(param_index)].pl_tok.src_tok, 9401 .param_anytype => data[@intFromEnum(param_index)].str_tok.src_tok, 9402 else => unreachable, 9403 }); 9404 const name = sema.code.nullTerminatedString(name_nts); 9405 if (name.len != 0) { 9406 try sema.errNote(param_src, msg, "param '{s}' is required to be comptime", .{name}); 9407 } else { 9408 try sema.errNote(param_src, msg, "param is required to be comptime", .{}); 9409 } 9410 } 9411 } 9412 return sema.failWithOwnedErrorMsg(block, msg); 9413 } 9414 9415 validate_incoming_stack_align: { 9416 const a: u64 = switch (cc_resolved) { 9417 inline else => |payload| if (@TypeOf(payload) != void and @hasField(@TypeOf(payload), "incoming_stack_alignment")) 9418 payload.incoming_stack_alignment orelse break :validate_incoming_stack_align 9419 else 9420 break :validate_incoming_stack_align, 9421 }; 9422 if (!std.math.isPowerOfTwo(a)) { 9423 return sema.fail(block, cc_src, "calling convention incoming stack alignment '{d}' is not a power of two", .{a}); 9424 } 9425 } 9426 9427 switch (cc_resolved) { 9428 .x86_64_interrupt, 9429 .x86_interrupt, 9430 .arm_interrupt, 9431 .mips64_interrupt, 9432 .mips_interrupt, 9433 .riscv64_interrupt, 9434 .riscv32_interrupt, 9435 .avr_interrupt, 9436 .csky_interrupt, 9437 .m68k_interrupt, 9438 .avr_signal, 9439 => if (return_type.zigTypeTag(zcu) != .void and return_type.zigTypeTag(zcu) != .noreturn) { 9440 return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(cc_resolved)}); 9441 }, 9442 .@"inline" => if (is_noinline) { 9443 return sema.fail(block, cc_src, "'noinline' function cannot have calling convention 'inline'", .{}); 9444 }, 9445 else => {}, 9446 } 9447 9448 switch (zcu.callconvSupported(cc_resolved)) { 9449 .ok => {}, 9450 .bad_arch => |allowed_archs| { 9451 const ArchListFormatter = struct { 9452 archs: []const std.Target.Cpu.Arch, 9453 pub fn format(formatter: @This(), w: *std.io.Writer) std.io.Writer.Error!void { 9454 for (formatter.archs, 0..) |arch, i| { 9455 if (i != 0) 9456 try w.writeAll(", "); 9457 try w.print("'{s}'", .{@tagName(arch)}); 9458 } 9459 } 9460 }; 9461 return sema.fail(block, cc_src, "calling convention '{s}' only available on architectures {f}", .{ 9462 @tagName(cc_resolved), 9463 ArchListFormatter{ .archs = allowed_archs }, 9464 }); 9465 }, 9466 .bad_backend => |bad_backend| return sema.fail(block, cc_src, "calling convention '{s}' not supported by compiler backend '{s}'", .{ 9467 @tagName(cc_resolved), 9468 @tagName(bad_backend), 9469 }), 9470 } 9471 9472 return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty); 9473 } 9474 9475 fn zirParam( 9476 sema: *Sema, 9477 block: *Block, 9478 inst: Zir.Inst.Index, 9479 comptime_syntax: bool, 9480 ) CompileError!void { 9481 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; 9482 const src = block.tokenOffset(inst_data.src_tok); 9483 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); 9484 const param_name: Zir.NullTerminatedString = extra.data.name; 9485 const body = sema.code.bodySlice(extra.end, extra.data.type.body_len); 9486 9487 const param_ty: Type = if (extra.data.type.is_generic) .generic_poison else ty: { 9488 // Make sure any nested param instructions don't clobber our work. 9489 const prev_params = block.params; 9490 block.params = .{}; 9491 defer { 9492 block.params = prev_params; 9493 } 9494 9495 const param_ty_inst = try sema.resolveInlineBody(block, body, inst); 9496 break :ty try sema.analyzeAsType(block, src, param_ty_inst); 9497 }; 9498 9499 try block.params.append(sema.arena, .{ 9500 .ty = param_ty.toIntern(), 9501 .is_comptime = comptime_syntax, 9502 .name = param_name, 9503 }); 9504 } 9505 9506 fn zirParamAnytype( 9507 sema: *Sema, 9508 block: *Block, 9509 inst: Zir.Inst.Index, 9510 comptime_syntax: bool, 9511 ) CompileError!void { 9512 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 9513 const param_name: Zir.NullTerminatedString = inst_data.start; 9514 9515 try block.params.append(sema.arena, .{ 9516 .ty = .generic_poison_type, 9517 .is_comptime = comptime_syntax, 9518 .name = param_name, 9519 }); 9520 } 9521 9522 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9523 const tracy = trace(@src()); 9524 defer tracy.end(); 9525 9526 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9527 const src = block.nodeOffset(inst_data.src_node); 9528 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; 9529 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false); 9530 } 9531 9532 fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9533 const tracy = trace(@src()); 9534 defer tracy.end(); 9535 9536 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9537 const src = block.nodeOffset(inst_data.src_node); 9538 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; 9539 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true); 9540 } 9541 9542 fn analyzeAs( 9543 sema: *Sema, 9544 block: *Block, 9545 src: LazySrcLoc, 9546 zir_dest_type: Zir.Inst.Ref, 9547 zir_operand: Zir.Inst.Ref, 9548 no_cast_to_comptime_int: bool, 9549 ) CompileError!Air.Inst.Ref { 9550 const pt = sema.pt; 9551 const zcu = pt.zcu; 9552 const operand = try sema.resolveInst(zir_operand); 9553 const dest_ty = try sema.resolveTypeOrPoison(block, src, zir_dest_type) orelse return operand; 9554 switch (dest_ty.zigTypeTag(zcu)) { 9555 .@"opaque" => return sema.fail(block, src, "cannot cast to opaque type '{f}'", .{dest_ty.fmt(pt)}), 9556 .noreturn => return sema.fail(block, src, "cannot cast to noreturn", .{}), 9557 else => {}, 9558 } 9559 9560 const is_ret = if (zir_dest_type.toIndex()) |ptr_index| 9561 sema.code.instructions.items(.tag)[@intFromEnum(ptr_index)] == .ret_type 9562 else 9563 false; 9564 return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) { 9565 error.NotCoercible => unreachable, 9566 else => |e| return e, 9567 }; 9568 } 9569 9570 fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9571 const tracy = trace(@src()); 9572 defer tracy.end(); 9573 9574 const pt = sema.pt; 9575 const zcu = pt.zcu; 9576 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 9577 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0); 9578 const operand = try sema.resolveInst(inst_data.operand); 9579 const operand_ty = sema.typeOf(operand); 9580 const ptr_ty = operand_ty.scalarType(zcu); 9581 const is_vector = operand_ty.zigTypeTag(zcu) == .vector; 9582 if (!ptr_ty.isPtrAtRuntime(zcu)) { 9583 return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}); 9584 } 9585 const pointee_ty = ptr_ty.childType(zcu); 9586 if (try ptr_ty.comptimeOnlySema(pt)) { 9587 const msg = msg: { 9588 const msg = try sema.errMsg(ptr_src, "comptime-only type '{f}' has no pointer address", .{pointee_ty.fmt(pt)}); 9589 errdefer msg.destroy(sema.gpa); 9590 try sema.explainWhyTypeIsComptime(msg, ptr_src, pointee_ty); 9591 break :msg msg; 9592 }; 9593 return sema.failWithOwnedErrorMsg(block, msg); 9594 } 9595 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; 9596 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .usize_type, .len = len }) else .usize; 9597 9598 if (try sema.resolveValue(operand)) |operand_val| ct: { 9599 if (!is_vector) { 9600 if (operand_val.isUndef(zcu)) { 9601 return .undef_usize; 9602 } 9603 const addr = try operand_val.getUnsignedIntSema(pt) orelse { 9604 // Wasn't an integer pointer. This is a runtime operation. 9605 break :ct; 9606 }; 9607 return Air.internedToRef((try pt.intValue( 9608 .usize, 9609 addr, 9610 )).toIntern()); 9611 } 9612 const new_elems = try sema.arena.alloc(InternPool.Index, len); 9613 for (new_elems, 0..) |*new_elem, i| { 9614 const ptr_val = try operand_val.elemValue(pt, i); 9615 if (ptr_val.isUndef(zcu)) { 9616 new_elem.* = .undef_usize; 9617 continue; 9618 } 9619 const addr = try ptr_val.getUnsignedIntSema(pt) orelse { 9620 // A vector element wasn't an integer pointer. This is a runtime operation. 9621 break :ct; 9622 }; 9623 new_elem.* = (try pt.intValue( 9624 .usize, 9625 addr, 9626 )).toIntern(); 9627 } 9628 return Air.internedToRef(try pt.intern(.{ .aggregate = .{ 9629 .ty = dest_ty.toIntern(), 9630 .storage = .{ .elems = new_elems }, 9631 } })); 9632 } 9633 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src); 9634 try sema.validateRuntimeValue(block, ptr_src, operand); 9635 try sema.checkLogicalPtrOperation(block, ptr_src, ptr_ty); 9636 return block.addBitCast(dest_ty, operand); 9637 } 9638 9639 fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9640 const tracy = trace(@src()); 9641 defer tracy.end(); 9642 9643 const pt = sema.pt; 9644 const zcu = pt.zcu; 9645 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9646 const src = block.nodeOffset(inst_data.src_node); 9647 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); 9648 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9649 const field_name = try zcu.intern_pool.getOrPutString( 9650 sema.gpa, 9651 pt.tid, 9652 sema.code.nullTerminatedString(extra.field_name_start), 9653 .no_embedded_nulls, 9654 ); 9655 const object = try sema.resolveInst(extra.lhs); 9656 return sema.fieldVal(block, src, object, field_name, field_name_src); 9657 } 9658 9659 fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9660 const tracy = trace(@src()); 9661 defer tracy.end(); 9662 9663 const pt = sema.pt; 9664 const zcu = pt.zcu; 9665 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9666 const src = block.nodeOffset(inst_data.src_node); 9667 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); 9668 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9669 const field_name = try zcu.intern_pool.getOrPutString( 9670 sema.gpa, 9671 pt.tid, 9672 sema.code.nullTerminatedString(extra.field_name_start), 9673 .no_embedded_nulls, 9674 ); 9675 const object_ptr = try sema.resolveInst(extra.lhs); 9676 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); 9677 } 9678 9679 fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9680 const tracy = trace(@src()); 9681 defer tracy.end(); 9682 9683 const pt = sema.pt; 9684 const zcu = pt.zcu; 9685 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9686 const src = block.nodeOffset(inst_data.src_node); 9687 const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node }); 9688 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9689 const field_name = try zcu.intern_pool.getOrPutString( 9690 sema.gpa, 9691 pt.tid, 9692 sema.code.nullTerminatedString(extra.field_name_start), 9693 .no_embedded_nulls, 9694 ); 9695 const object_ptr = try sema.resolveInst(extra.lhs); 9696 const struct_ty = sema.typeOf(object_ptr).childType(zcu); 9697 switch (struct_ty.zigTypeTag(zcu)) { 9698 .@"struct", .@"union" => { 9699 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true); 9700 }, 9701 else => { 9702 return sema.failWithStructInitNotSupported(block, src, struct_ty); 9703 }, 9704 } 9705 } 9706 9707 fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9708 const tracy = trace(@src()); 9709 defer tracy.end(); 9710 9711 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9712 const src = block.nodeOffset(inst_data.src_node); 9713 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); 9714 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; 9715 const object = try sema.resolveInst(extra.lhs); 9716 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); 9717 return sema.fieldVal(block, src, object, field_name, field_name_src); 9718 } 9719 9720 fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9721 const tracy = trace(@src()); 9722 defer tracy.end(); 9723 9724 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9725 const src = block.nodeOffset(inst_data.src_node); 9726 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); 9727 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; 9728 const object_ptr = try sema.resolveInst(extra.lhs); 9729 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); 9730 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); 9731 } 9732 9733 fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9734 const tracy = trace(@src()); 9735 defer tracy.end(); 9736 9737 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9738 const src = block.nodeOffset(inst_data.src_node); 9739 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 9740 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9741 9742 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast"); 9743 const operand = try sema.resolveInst(extra.rhs); 9744 9745 return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src); 9746 } 9747 9748 fn intCast( 9749 sema: *Sema, 9750 block: *Block, 9751 src: LazySrcLoc, 9752 dest_ty: Type, 9753 dest_ty_src: LazySrcLoc, 9754 operand: Air.Inst.Ref, 9755 operand_src: LazySrcLoc, 9756 ) CompileError!Air.Inst.Ref { 9757 const pt = sema.pt; 9758 const zcu = pt.zcu; 9759 const operand_ty = sema.typeOf(operand); 9760 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src); 9761 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 9762 9763 if (try sema.isComptimeKnown(operand)) { 9764 return sema.coerce(block, dest_ty, operand, operand_src); 9765 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { 9766 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); 9767 } 9768 9769 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); 9770 const is_vector = dest_ty.zigTypeTag(zcu) == .vector; 9771 9772 if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| { 9773 // requirement: intCast(u0, input) iff input == 0 9774 if (block.wantSafety()) { 9775 try sema.requireRuntimeBlock(block, src, operand_src); 9776 const wanted_info = dest_scalar_ty.intInfo(zcu); 9777 const wanted_bits = wanted_info.bits; 9778 9779 if (wanted_bits == 0) { 9780 const ok = if (is_vector) ok: { 9781 const zeros = try sema.splat(operand_ty, try pt.intValue(operand_scalar_ty, 0)); 9782 const zero_inst = Air.internedToRef(zeros.toIntern()); 9783 const is_in_range = try block.addCmpVector(operand, zero_inst, .eq); 9784 const all_in_range = try block.addReduce(is_in_range, .And); 9785 break :ok all_in_range; 9786 } else ok: { 9787 const zero_inst = Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern()); 9788 const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst); 9789 break :ok is_in_range; 9790 }; 9791 try sema.addSafetyCheck(block, src, ok, .integer_out_of_bounds); 9792 } 9793 } 9794 9795 return Air.internedToRef(opv.toIntern()); 9796 } 9797 9798 try sema.requireRuntimeBlock(block, src, operand_src); 9799 if (block.wantSafety()) { 9800 try sema.preparePanicId(src, .integer_out_of_bounds); 9801 return block.addTyOp(.intcast_safe, dest_ty, operand); 9802 } 9803 return block.addTyOp(.intcast, dest_ty, operand); 9804 } 9805 9806 fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9807 const tracy = trace(@src()); 9808 defer tracy.end(); 9809 9810 const pt = sema.pt; 9811 const zcu = pt.zcu; 9812 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9813 const src = block.nodeOffset(inst_data.src_node); 9814 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 9815 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9816 9817 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast"); 9818 const operand = try sema.resolveInst(extra.rhs); 9819 const operand_ty = sema.typeOf(operand); 9820 switch (dest_ty.zigTypeTag(zcu)) { 9821 .@"anyframe", 9822 .comptime_float, 9823 .comptime_int, 9824 .enum_literal, 9825 .error_set, 9826 .error_union, 9827 .@"fn", 9828 .frame, 9829 .noreturn, 9830 .null, 9831 .@"opaque", 9832 .optional, 9833 .type, 9834 .undefined, 9835 .void, 9836 => return sema.fail(block, src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}), 9837 9838 .@"enum" => { 9839 const msg = msg: { 9840 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}); 9841 errdefer msg.destroy(sema.gpa); 9842 switch (operand_ty.zigTypeTag(zcu)) { 9843 .int, .comptime_int => try sema.errNote(src, msg, "use @enumFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}), 9844 else => {}, 9845 } 9846 9847 break :msg msg; 9848 }; 9849 return sema.failWithOwnedErrorMsg(block, msg); 9850 }, 9851 9852 .pointer => { 9853 const msg = msg: { 9854 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}); 9855 errdefer msg.destroy(sema.gpa); 9856 switch (operand_ty.zigTypeTag(zcu)) { 9857 .int, .comptime_int => try sema.errNote(src, msg, "use @ptrFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}), 9858 .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}), 9859 else => {}, 9860 } 9861 9862 break :msg msg; 9863 }; 9864 return sema.failWithOwnedErrorMsg(block, msg); 9865 }, 9866 .@"struct", .@"union" => if (dest_ty.containerLayout(zcu) == .auto) { 9867 const container = switch (dest_ty.zigTypeTag(zcu)) { 9868 .@"struct" => "struct", 9869 .@"union" => "union", 9870 else => unreachable, 9871 }; 9872 return sema.fail(block, src, "cannot @bitCast to '{f}'; {s} does not have a guaranteed in-memory layout", .{ 9873 dest_ty.fmt(pt), container, 9874 }); 9875 }, 9876 9877 .array, 9878 .bool, 9879 .float, 9880 .int, 9881 .vector, 9882 => {}, 9883 } 9884 switch (operand_ty.zigTypeTag(zcu)) { 9885 .@"anyframe", 9886 .comptime_float, 9887 .comptime_int, 9888 .enum_literal, 9889 .error_set, 9890 .error_union, 9891 .@"fn", 9892 .frame, 9893 .noreturn, 9894 .null, 9895 .@"opaque", 9896 .optional, 9897 .type, 9898 .undefined, 9899 .void, 9900 => return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}), 9901 9902 .@"enum" => { 9903 const msg = msg: { 9904 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}); 9905 errdefer msg.destroy(sema.gpa); 9906 switch (dest_ty.zigTypeTag(zcu)) { 9907 .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromEnum to cast to '{f}'", .{dest_ty.fmt(pt)}), 9908 else => {}, 9909 } 9910 9911 break :msg msg; 9912 }; 9913 return sema.failWithOwnedErrorMsg(block, msg); 9914 }, 9915 .pointer => { 9916 const msg = msg: { 9917 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}); 9918 errdefer msg.destroy(sema.gpa); 9919 switch (dest_ty.zigTypeTag(zcu)) { 9920 .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromPtr to cast to '{f}'", .{dest_ty.fmt(pt)}), 9921 .pointer => try sema.errNote(operand_src, msg, "use @ptrCast to cast to '{f}'", .{dest_ty.fmt(pt)}), 9922 else => {}, 9923 } 9924 9925 break :msg msg; 9926 }; 9927 return sema.failWithOwnedErrorMsg(block, msg); 9928 }, 9929 .@"struct", .@"union" => if (operand_ty.containerLayout(zcu) == .auto) { 9930 const container = switch (operand_ty.zigTypeTag(zcu)) { 9931 .@"struct" => "struct", 9932 .@"union" => "union", 9933 else => unreachable, 9934 }; 9935 return sema.fail(block, operand_src, "cannot @bitCast from '{f}'; {s} does not have a guaranteed in-memory layout", .{ 9936 operand_ty.fmt(pt), container, 9937 }); 9938 }, 9939 9940 .array, 9941 .bool, 9942 .float, 9943 .int, 9944 .vector, 9945 => {}, 9946 } 9947 return sema.bitCast(block, dest_ty, operand, block.nodeOffset(inst_data.src_node), operand_src); 9948 } 9949 9950 fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9951 const tracy = trace(@src()); 9952 defer tracy.end(); 9953 9954 const pt = sema.pt; 9955 const zcu = pt.zcu; 9956 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9957 const src = block.nodeOffset(inst_data.src_node); 9958 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 9959 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9960 9961 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast"); 9962 const dest_scalar_ty = dest_ty.scalarType(zcu); 9963 9964 const operand = try sema.resolveInst(extra.rhs); 9965 const operand_ty = sema.typeOf(operand); 9966 const operand_scalar_ty = operand_ty.scalarType(zcu); 9967 9968 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 9969 const is_vector = dest_ty.zigTypeTag(zcu) == .vector; 9970 9971 const target = zcu.getTarget(); 9972 const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(zcu)) { 9973 .comptime_float => true, 9974 .float => false, 9975 else => return sema.fail( 9976 block, 9977 src, 9978 "expected float or vector type, found '{f}'", 9979 .{dest_ty.fmt(pt)}, 9980 ), 9981 }; 9982 9983 switch (operand_scalar_ty.zigTypeTag(zcu)) { 9984 .comptime_float, .float, .comptime_int => {}, 9985 else => return sema.fail( 9986 block, 9987 operand_src, 9988 "expected float or vector type, found '{f}'", 9989 .{operand_ty.fmt(pt)}, 9990 ), 9991 } 9992 9993 if (try sema.resolveValue(operand)) |operand_val| { 9994 if (!is_vector) { 9995 return Air.internedToRef((try operand_val.floatCast(dest_ty, pt)).toIntern()); 9996 } 9997 const vec_len = operand_ty.vectorLen(zcu); 9998 const new_elems = try sema.arena.alloc(InternPool.Index, vec_len); 9999 for (new_elems, 0..) |*new_elem, i| { 10000 const old_elem = try operand_val.elemValue(pt, i); 10001 new_elem.* = (try old_elem.floatCast(dest_scalar_ty, pt)).toIntern(); 10002 } 10003 return Air.internedToRef(try pt.intern(.{ .aggregate = .{ 10004 .ty = dest_ty.toIntern(), 10005 .storage = .{ .elems = new_elems }, 10006 } })); 10007 } 10008 if (dest_is_comptime_float) { 10009 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{}); 10010 } 10011 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src); 10012 10013 const src_bits = operand_scalar_ty.floatBits(target); 10014 const dst_bits = dest_scalar_ty.floatBits(target); 10015 if (dst_bits >= src_bits) { 10016 return sema.coerce(block, dest_ty, operand, operand_src); 10017 } 10018 return block.addTyOp(.fptrunc, dest_ty, operand); 10019 } 10020 10021 fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10022 const tracy = trace(@src()); 10023 defer tracy.end(); 10024 10025 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10026 const src = block.nodeOffset(inst_data.src_node); 10027 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10028 const array = try sema.resolveInst(extra.lhs); 10029 const elem_index = try sema.resolveInst(extra.rhs); 10030 return sema.elemVal(block, src, array, elem_index, src, false); 10031 } 10032 10033 fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10034 const tracy = trace(@src()); 10035 defer tracy.end(); 10036 10037 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10038 const src = block.nodeOffset(inst_data.src_node); 10039 const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node }); 10040 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10041 const array = try sema.resolveInst(extra.lhs); 10042 const uncoerced_elem_index = try sema.resolveInst(extra.rhs); 10043 const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src); 10044 return sema.elemVal(block, src, array, elem_index, elem_index_src, true); 10045 } 10046 10047 fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10048 const tracy = trace(@src()); 10049 defer tracy.end(); 10050 10051 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm; 10052 const array = try sema.resolveInst(inst_data.operand); 10053 const elem_index = try sema.pt.intRef(.usize, inst_data.idx); 10054 return sema.elemVal(block, LazySrcLoc.unneeded, array, elem_index, LazySrcLoc.unneeded, false); 10055 } 10056 10057 fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10058 const tracy = trace(@src()); 10059 defer tracy.end(); 10060 10061 const pt = sema.pt; 10062 const zcu = pt.zcu; 10063 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10064 const src = block.nodeOffset(inst_data.src_node); 10065 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10066 const array_ptr = try sema.resolveInst(extra.lhs); 10067 const elem_index = try sema.resolveInst(extra.rhs); 10068 const indexable_ty = sema.typeOf(array_ptr); 10069 if (indexable_ty.zigTypeTag(zcu) != .pointer) { 10070 const capture_src = block.src(.{ .for_capture_from_input = inst_data.src_node }); 10071 const msg = msg: { 10072 const msg = try sema.errMsg(capture_src, "pointer capture of non pointer type '{f}'", .{ 10073 indexable_ty.fmt(pt), 10074 }); 10075 errdefer msg.destroy(sema.gpa); 10076 if (indexable_ty.isIndexable(zcu)) { 10077 try sema.errNote(src, msg, "consider using '&' here", .{}); 10078 } 10079 break :msg msg; 10080 }; 10081 return sema.failWithOwnedErrorMsg(block, msg); 10082 } 10083 return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false); 10084 } 10085 10086 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10087 const tracy = trace(@src()); 10088 defer tracy.end(); 10089 10090 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10091 const src = block.nodeOffset(inst_data.src_node); 10092 const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node }); 10093 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10094 const array_ptr = try sema.resolveInst(extra.lhs); 10095 const uncoerced_elem_index = try sema.resolveInst(extra.rhs); 10096 const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src); 10097 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true); 10098 } 10099 10100 fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10101 const tracy = trace(@src()); 10102 defer tracy.end(); 10103 10104 const pt = sema.pt; 10105 const zcu = pt.zcu; 10106 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10107 const src = block.nodeOffset(inst_data.src_node); 10108 const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; 10109 const array_ptr = try sema.resolveInst(extra.ptr); 10110 const elem_index = try pt.intRef(.usize, extra.index); 10111 const array_ty = sema.typeOf(array_ptr).childType(zcu); 10112 switch (array_ty.zigTypeTag(zcu)) { 10113 .array, .vector => {}, 10114 else => if (!array_ty.isTuple(zcu)) { 10115 return sema.failWithArrayInitNotSupported(block, src, array_ty); 10116 }, 10117 } 10118 return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true); 10119 } 10120 10121 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10122 const tracy = trace(@src()); 10123 defer tracy.end(); 10124 10125 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10126 const src = block.nodeOffset(inst_data.src_node); 10127 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; 10128 const array_ptr = try sema.resolveInst(extra.lhs); 10129 const start = try sema.resolveInst(extra.start); 10130 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10131 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); 10132 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); 10133 10134 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false); 10135 } 10136 10137 fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10138 const tracy = trace(@src()); 10139 defer tracy.end(); 10140 10141 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10142 const src = block.nodeOffset(inst_data.src_node); 10143 const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data; 10144 const array_ptr = try sema.resolveInst(extra.lhs); 10145 const start = try sema.resolveInst(extra.start); 10146 const end = try sema.resolveInst(extra.end); 10147 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10148 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); 10149 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); 10150 10151 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false); 10152 } 10153 10154 fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10155 const tracy = trace(@src()); 10156 defer tracy.end(); 10157 10158 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10159 const src = block.nodeOffset(inst_data.src_node); 10160 const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); 10161 const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; 10162 const array_ptr = try sema.resolveInst(extra.lhs); 10163 const start = try sema.resolveInst(extra.start); 10164 const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end); 10165 const sentinel = try sema.resolveInst(extra.sentinel); 10166 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10167 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); 10168 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); 10169 10170 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false); 10171 } 10172 10173 fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10174 const tracy = trace(@src()); 10175 defer tracy.end(); 10176 10177 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10178 const src = block.nodeOffset(inst_data.src_node); 10179 const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; 10180 const array_ptr = try sema.resolveInst(extra.lhs); 10181 const start = try sema.resolveInst(extra.start); 10182 const len = try sema.resolveInst(extra.len); 10183 const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel); 10184 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10185 const start_src = block.src(.{ .node_offset_slice_start = extra.start_src_node_offset }); 10186 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); 10187 const sentinel_src: LazySrcLoc = if (sentinel == .none) 10188 LazySrcLoc.unneeded 10189 else 10190 block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); 10191 10192 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); 10193 } 10194 10195 fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10196 const tracy = trace(@src()); 10197 defer tracy.end(); 10198 10199 const pt = sema.pt; 10200 const zcu = pt.zcu; 10201 10202 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 10203 10204 const src = block.nodeOffset(inst_data.src_node); 10205 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); 10206 const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); 10207 10208 // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will 10209 // have a double pointer if it was already a pointer. 10210 10211 const lhs_ptr_ty = sema.typeOf(try sema.resolveInst(inst_data.operand)); 10212 const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) { 10213 .pointer => lhs_ptr_ty.childType(zcu), 10214 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{lhs_ptr_ty.fmt(pt)}), 10215 }; 10216 10217 const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) { 10218 .array => lhs_ty.childType(zcu), 10219 .pointer => switch (lhs_ty.ptrSize(zcu)) { 10220 .many, .c, .slice => lhs_ty.childType(zcu), 10221 .one => s: { 10222 const lhs_elem_ty = lhs_ty.childType(zcu); 10223 break :s switch (lhs_elem_ty.zigTypeTag(zcu)) { 10224 .array => lhs_elem_ty.childType(zcu), // array element type 10225 else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}), 10226 }; 10227 }, 10228 }, 10229 else => return sema.fail(block, src, "slice of non-array type '{f}'", .{lhs_ty.fmt(pt)}), 10230 }; 10231 10232 return Air.internedToRef(sentinel_ty.toIntern()); 10233 } 10234 10235 /// Holds common data used when analyzing or resolving switch prong bodies, 10236 /// including setting up captures. 10237 const SwitchProngAnalysis = struct { 10238 sema: *Sema, 10239 /// The block containing the `switch_block` itself. 10240 parent_block: *Block, 10241 operand: Operand, 10242 /// If this switch is on an error set, this is the type to assign to the 10243 /// `else` prong. If `null`, the prong should be unreachable. 10244 else_error_ty: ?Type, 10245 /// The index of the `switch_block` instruction itself. 10246 switch_block_inst: Zir.Inst.Index, 10247 /// The dummy index into which inline tag captures should be placed. May be 10248 /// undefined if no prong has a tag capture. 10249 tag_capture_inst: Zir.Inst.Index, 10250 10251 const Operand = union(enum) { 10252 /// This switch will be dispatched only once, with the given operand. 10253 simple: struct { 10254 /// The raw switch operand value. Always defined. 10255 by_val: Air.Inst.Ref, 10256 /// The switch operand *pointer*. Defined only if there is a prong 10257 /// with a by-ref capture. 10258 by_ref: Air.Inst.Ref, 10259 /// The switch condition value. For unions, `operand` is the union 10260 /// and `cond` is its enum tag value. 10261 cond: Air.Inst.Ref, 10262 }, 10263 /// This switch may be dispatched multiple times with `continue` syntax. 10264 /// As such, the operand is stored in an alloc if needed. 10265 loop: struct { 10266 /// The `alloc` containing the `switch` operand for the active dispatch. 10267 /// Each prong must load from this `alloc` to get captures. 10268 /// If there are no captures, this may be undefined. 10269 operand_alloc: Air.Inst.Ref, 10270 /// Whether `operand_alloc` contains a by-val operand or a by-ref 10271 /// operand. 10272 operand_is_ref: bool, 10273 /// The switch condition value for the *initial* dispatch. For 10274 /// unions, this is the enum tag value. 10275 init_cond: Air.Inst.Ref, 10276 }, 10277 }; 10278 10279 /// Resolve a switch prong which is determined at comptime to have no peers. 10280 /// Uses `resolveBlockBody`. Sets up captures as needed. 10281 fn resolveProngComptime( 10282 spa: SwitchProngAnalysis, 10283 child_block: *Block, 10284 prong_type: enum { normal, special }, 10285 prong_body: []const Zir.Inst.Index, 10286 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10287 /// Must use the `switch_capture` field in `offset`. 10288 capture_src: LazySrcLoc, 10289 /// The set of all values which can reach this prong. May be undefined 10290 /// if the prong is special or contains ranges. 10291 case_vals: []const Air.Inst.Ref, 10292 /// The inline capture of this prong. If this is not an inline prong, 10293 /// this is `.none`. 10294 inline_case_capture: Air.Inst.Ref, 10295 /// Whether this prong has an inline tag capture. If `true`, then 10296 /// `inline_case_capture` cannot be `.none`. 10297 has_tag_capture: bool, 10298 merges: *Block.Merges, 10299 ) CompileError!Air.Inst.Ref { 10300 const sema = spa.sema; 10301 const src = spa.parent_block.nodeOffset( 10302 sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src_node, 10303 ); 10304 10305 // We can propagate `.cold` hints from this branch since it's comptime-known 10306 // to be taken from the parent branch. 10307 const parent_hint = sema.branch_hint; 10308 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; 10309 10310 if (has_tag_capture) { 10311 const tag_ref = try spa.analyzeTagCapture(child_block, capture_src, inline_case_capture); 10312 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); 10313 } 10314 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); 10315 10316 switch (capture) { 10317 .none => { 10318 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); 10319 }, 10320 10321 .by_val, .by_ref => { 10322 const capture_ref = try spa.analyzeCapture( 10323 child_block, 10324 capture == .by_ref, 10325 prong_type == .special, 10326 capture_src, 10327 case_vals, 10328 inline_case_capture, 10329 ); 10330 10331 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) { 10332 // This prong should be unreachable! 10333 return .unreachable_value; 10334 } 10335 10336 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); 10337 defer assert(sema.inst_map.remove(spa.switch_block_inst)); 10338 10339 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); 10340 }, 10341 } 10342 } 10343 10344 /// Analyze a switch prong which may have peers at runtime. 10345 /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed. 10346 /// Returns the `BranchHint` for the prong. 10347 fn analyzeProngRuntime( 10348 spa: SwitchProngAnalysis, 10349 case_block: *Block, 10350 prong_type: enum { normal, special }, 10351 prong_body: []const Zir.Inst.Index, 10352 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10353 /// Must use the `switch_capture` field in `offset`. 10354 capture_src: LazySrcLoc, 10355 /// The set of all values which can reach this prong. May be undefined 10356 /// if the prong is special or contains ranges. 10357 case_vals: []const Air.Inst.Ref, 10358 /// The inline capture of this prong. If this is not an inline prong, 10359 /// this is `.none`. 10360 inline_case_capture: Air.Inst.Ref, 10361 /// Whether this prong has an inline tag capture. If `true`, then 10362 /// `inline_case_capture` cannot be `.none`. 10363 has_tag_capture: bool, 10364 ) CompileError!std.builtin.BranchHint { 10365 const sema = spa.sema; 10366 10367 if (has_tag_capture) { 10368 const tag_ref = try spa.analyzeTagCapture(case_block, capture_src, inline_case_capture); 10369 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); 10370 } 10371 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); 10372 10373 switch (capture) { 10374 .none => { 10375 return sema.analyzeBodyRuntimeBreak(case_block, prong_body); 10376 }, 10377 10378 .by_val, .by_ref => { 10379 const capture_ref = try spa.analyzeCapture( 10380 case_block, 10381 capture == .by_ref, 10382 prong_type == .special, 10383 capture_src, 10384 case_vals, 10385 inline_case_capture, 10386 ); 10387 10388 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) { 10389 // No need to analyze any further, the prong is unreachable 10390 return .none; 10391 } 10392 10393 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); 10394 defer assert(sema.inst_map.remove(spa.switch_block_inst)); 10395 10396 return sema.analyzeBodyRuntimeBreak(case_block, prong_body); 10397 }, 10398 } 10399 } 10400 10401 fn analyzeTagCapture( 10402 spa: SwitchProngAnalysis, 10403 block: *Block, 10404 capture_src: LazySrcLoc, 10405 inline_case_capture: Air.Inst.Ref, 10406 ) CompileError!Air.Inst.Ref { 10407 const sema = spa.sema; 10408 const pt = sema.pt; 10409 const zcu = pt.zcu; 10410 const operand_ty = switch (spa.operand) { 10411 .simple => |s| sema.typeOf(s.by_val), 10412 .loop => |l| ty: { 10413 const alloc_ty = sema.typeOf(l.operand_alloc); 10414 const alloc_child = alloc_ty.childType(zcu); 10415 if (l.operand_is_ref) break :ty alloc_child.childType(zcu); 10416 break :ty alloc_child; 10417 }, 10418 }; 10419 if (operand_ty.zigTypeTag(zcu) != .@"union") { 10420 const tag_capture_src: LazySrcLoc = .{ 10421 .base_node_inst = capture_src.base_node_inst, 10422 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, 10423 }; 10424 return sema.fail(block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ 10425 operand_ty.fmt(pt), 10426 }); 10427 } 10428 assert(inline_case_capture != .none); 10429 return inline_case_capture; 10430 } 10431 10432 fn analyzeCapture( 10433 spa: SwitchProngAnalysis, 10434 block: *Block, 10435 capture_byref: bool, 10436 is_special_prong: bool, 10437 capture_src: LazySrcLoc, 10438 case_vals: []const Air.Inst.Ref, 10439 inline_case_capture: Air.Inst.Ref, 10440 ) CompileError!Air.Inst.Ref { 10441 const sema = spa.sema; 10442 const pt = sema.pt; 10443 const zcu = pt.zcu; 10444 const ip = &zcu.intern_pool; 10445 10446 const zir_datas = sema.code.instructions.items(.data); 10447 const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node; 10448 10449 const operand_src = block.src(.{ .node_offset_switch_operand = switch_node_offset }); 10450 10451 const operand_val, const operand_ptr = switch (spa.operand) { 10452 .simple => |s| .{ s.by_val, s.by_ref }, 10453 .loop => |l| op: { 10454 const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src); 10455 if (l.operand_is_ref) { 10456 const by_val = try sema.analyzeLoad(block, operand_src, loaded, operand_src); 10457 break :op .{ by_val, loaded }; 10458 } else { 10459 break :op .{ loaded, undefined }; 10460 } 10461 }, 10462 }; 10463 10464 const operand_ty = sema.typeOf(operand_val); 10465 const operand_ptr_ty = if (capture_byref) sema.typeOf(operand_ptr) else undefined; 10466 10467 if (inline_case_capture != .none) { 10468 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inline_case_capture, undefined) catch unreachable; 10469 if (operand_ty.zigTypeTag(zcu) == .@"union") { 10470 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?); 10471 const union_obj = zcu.typeToUnion(operand_ty).?; 10472 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 10473 if (capture_byref) { 10474 const ptr_field_ty = try pt.ptrTypeSema(.{ 10475 .child = field_ty.toIntern(), 10476 .flags = .{ 10477 .is_const = !operand_ptr_ty.ptrIsMutable(zcu), 10478 .is_volatile = operand_ptr_ty.isVolatilePtr(zcu), 10479 .address_space = operand_ptr_ty.ptrAddressSpace(zcu), 10480 }, 10481 }); 10482 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |union_ptr| { 10483 return Air.internedToRef((try union_ptr.ptrField(field_index, pt)).toIntern()); 10484 } 10485 return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty); 10486 } else { 10487 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |union_val| { 10488 const tag_and_val = ip.indexToKey(union_val.toIntern()).un; 10489 return Air.internedToRef(tag_and_val.val); 10490 } 10491 return block.addStructFieldVal(operand_val, field_index, field_ty); 10492 } 10493 } else if (capture_byref) { 10494 return sema.uavRef(item_val.toIntern()); 10495 } else { 10496 return inline_case_capture; 10497 } 10498 } 10499 10500 if (is_special_prong) { 10501 if (capture_byref) { 10502 return operand_ptr; 10503 } 10504 10505 switch (operand_ty.zigTypeTag(zcu)) { 10506 .error_set => if (spa.else_error_ty) |ty| { 10507 return sema.bitCast(block, ty, operand_val, operand_src, null); 10508 } else { 10509 try sema.analyzeUnreachable(block, operand_src, false); 10510 return .unreachable_value; 10511 }, 10512 else => return operand_val, 10513 } 10514 } 10515 10516 switch (operand_ty.zigTypeTag(zcu)) { 10517 .@"union" => { 10518 const union_obj = zcu.typeToUnion(operand_ty).?; 10519 const first_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable; 10520 10521 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; 10522 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); 10523 10524 const field_indices = try sema.arena.alloc(u32, case_vals.len); 10525 for (case_vals, field_indices) |item, *field_idx| { 10526 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 10527 field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; 10528 } 10529 10530 // Fast path: if all the operands are the same type already, we don't need to hit 10531 // PTR! This will also allow us to emit simpler code. 10532 const same_types = for (field_indices[1..]) |field_idx| { 10533 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10534 if (!field_ty.eql(first_field_ty, zcu)) break false; 10535 } else true; 10536 10537 const capture_ty = if (same_types) first_field_ty else capture_ty: { 10538 // We need values to run PTR on, so make a bunch of undef constants. 10539 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); 10540 for (dummy_captures, field_indices) |*dummy, field_idx| { 10541 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10542 dummy.* = try pt.undefRef(field_ty); 10543 } 10544 10545 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); 10546 for (case_srcs, 0..) |*case_src, i| { 10547 case_src.* = .{ 10548 .base_node_inst = capture_src.base_node_inst, 10549 .offset = .{ .switch_case_item = .{ 10550 .switch_node_offset = switch_node_offset, 10551 .case_idx = capture_src.offset.switch_capture.case_idx, 10552 .item_idx = .{ .kind = .single, .index = @intCast(i) }, 10553 } }, 10554 }; 10555 } 10556 10557 break :capture_ty sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { 10558 error.AnalysisFail => { 10559 const msg = sema.err orelse return error.AnalysisFail; 10560 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); 10561 return error.AnalysisFail; 10562 }, 10563 else => |e| return e, 10564 }; 10565 }; 10566 10567 // By-reference captures have some further restrictions which make them easier to emit 10568 if (capture_byref) { 10569 const operand_ptr_info = operand_ptr_ty.ptrInfo(zcu); 10570 const capture_ptr_ty = resolve: { 10571 // By-ref captures of hetereogeneous types are only allowed if all field 10572 // pointer types are peer resolvable to each other. 10573 // We need values to run PTR on, so make a bunch of undef constants. 10574 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); 10575 for (field_indices, dummy_captures) |field_idx, *dummy| { 10576 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10577 const field_ptr_ty = try pt.ptrTypeSema(.{ 10578 .child = field_ty.toIntern(), 10579 .flags = .{ 10580 .is_const = operand_ptr_info.flags.is_const, 10581 .is_volatile = operand_ptr_info.flags.is_volatile, 10582 .address_space = operand_ptr_info.flags.address_space, 10583 .alignment = union_obj.fieldAlign(ip, field_idx), 10584 }, 10585 }); 10586 dummy.* = try pt.undefRef(field_ptr_ty); 10587 } 10588 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); 10589 for (case_srcs, 0..) |*case_src, i| { 10590 case_src.* = .{ 10591 .base_node_inst = capture_src.base_node_inst, 10592 .offset = .{ .switch_case_item = .{ 10593 .switch_node_offset = switch_node_offset, 10594 .case_idx = capture_src.offset.switch_capture.case_idx, 10595 .item_idx = .{ .kind = .single, .index = @intCast(i) }, 10596 } }, 10597 }; 10598 } 10599 10600 break :resolve sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { 10601 error.AnalysisFail => { 10602 const msg = sema.err orelse return error.AnalysisFail; 10603 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); 10604 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); 10605 return error.AnalysisFail; 10606 }, 10607 else => |e| return e, 10608 }; 10609 }; 10610 10611 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| { 10612 if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); 10613 const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); 10614 return Air.internedToRef((try pt.getCoerced(field_ptr_val, capture_ptr_ty)).toIntern()); 10615 } 10616 10617 try sema.requireRuntimeBlock(block, operand_src, null); 10618 return block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty); 10619 } 10620 10621 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |operand_val_val| { 10622 if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty); 10623 const union_val = ip.indexToKey(operand_val_val.toIntern()).un; 10624 if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); 10625 const uncoerced = Air.internedToRef(union_val.val); 10626 return sema.coerce(block, capture_ty, uncoerced, operand_src); 10627 } 10628 10629 try sema.requireRuntimeBlock(block, operand_src, null); 10630 10631 if (same_types) { 10632 return block.addStructFieldVal(operand_val, first_field_index, capture_ty); 10633 } 10634 10635 // We may have to emit a switch block which coerces the operand to the capture type. 10636 // If we can, try to avoid that using in-memory coercions. 10637 const first_non_imc = in_mem: { 10638 for (field_indices, 0..) |field_idx, i| { 10639 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10640 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) { 10641 break :in_mem i; 10642 } 10643 } 10644 // All fields are in-memory coercible to the resolved type! 10645 // Just take the first field and bitcast the result. 10646 const uncoerced = try block.addStructFieldVal(operand_val, first_field_index, first_field_ty); 10647 return block.addBitCast(capture_ty, uncoerced); 10648 }; 10649 10650 // By-val capture with heterogeneous types which are not all in-memory coercible to 10651 // the resolved capture type. We finally have to fall back to the ugly method. 10652 10653 // However, let's first track which operands are in-memory coercible. There may well 10654 // be several, and we can squash all of these cases into the same switch prong using 10655 // a simple bitcast. We'll make this the 'else' prong. 10656 10657 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len); 10658 in_mem_coercible.unset(first_non_imc); 10659 { 10660 const next = first_non_imc + 1; 10661 for (field_indices[next..], next..) |field_idx, i| { 10662 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10663 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) { 10664 in_mem_coercible.unset(i); 10665 } 10666 } 10667 } 10668 10669 const capture_block_inst = try block.addInstAsIndex(.{ 10670 .tag = .block, 10671 .data = .{ 10672 .ty_pl = .{ 10673 .ty = Air.internedToRef(capture_ty.toIntern()), 10674 .payload = undefined, // updated below 10675 }, 10676 }, 10677 }); 10678 10679 const prong_count = field_indices.len - in_mem_coercible.count(); 10680 10681 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints 10682 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra); 10683 defer cases_extra.deinit(); 10684 10685 { 10686 // All branch hints are `.none`, so just add zero elems. 10687 comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0); 10688 const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable; 10689 try cases_extra.appendNTimes(0, need_elems); 10690 } 10691 10692 { 10693 // Non-bitcast cases 10694 var it = in_mem_coercible.iterator(.{ .kind = .unset }); 10695 while (it.next()) |idx| { 10696 var coerce_block = block.makeSubBlock(); 10697 defer coerce_block.instructions.deinit(sema.gpa); 10698 10699 const case_src: LazySrcLoc = .{ 10700 .base_node_inst = capture_src.base_node_inst, 10701 .offset = .{ .switch_case_item = .{ 10702 .switch_node_offset = switch_node_offset, 10703 .case_idx = capture_src.offset.switch_capture.case_idx, 10704 .item_idx = .{ .kind = .single, .index = @intCast(idx) }, 10705 } }, 10706 }; 10707 10708 const field_idx = field_indices[idx]; 10709 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); 10710 const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty); 10711 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); 10712 _ = try coerce_block.addBr(capture_block_inst, coerced); 10713 10714 try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 10715 1 + // `item`, no ranges 10716 coerce_block.instructions.items.len); 10717 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 10718 .items_len = 1, 10719 .ranges_len = 0, 10720 .body_len = @intCast(coerce_block.instructions.items.len), 10721 })); 10722 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item 10723 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body 10724 } 10725 } 10726 const else_body_len = len: { 10727 // 'else' prong uses a bitcast 10728 var coerce_block = block.makeSubBlock(); 10729 defer coerce_block.instructions.deinit(sema.gpa); 10730 10731 const first_imc_item_idx = in_mem_coercible.findFirstSet().?; 10732 const first_imc_field_idx = field_indices[first_imc_item_idx]; 10733 const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); 10734 const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty); 10735 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); 10736 _ = try coerce_block.addBr(capture_block_inst, coerced); 10737 10738 try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); 10739 break :len coerce_block.instructions.items.len; 10740 }; 10741 10742 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + 10743 cases_extra.items.len + 10744 @typeInfo(Air.Block).@"struct".fields.len + 10745 1); 10746 10747 const switch_br_inst: u32 = @intCast(sema.air_instructions.len); 10748 try sema.air_instructions.append(sema.gpa, .{ 10749 .tag = .switch_br, 10750 .data = .{ 10751 .pl_op = .{ 10752 .operand = undefined, // set by switch below 10753 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ 10754 .cases_len = @intCast(prong_count), 10755 .else_body_len = @intCast(else_body_len), 10756 }), 10757 }, 10758 }, 10759 }); 10760 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); 10761 10762 // Set up block body 10763 switch (spa.operand) { 10764 .simple => |s| { 10765 const air_datas = sema.air_instructions.items(.data); 10766 air_datas[switch_br_inst].pl_op.operand = s.cond; 10767 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ 10768 .body_len = 1, 10769 }); 10770 sema.air_extra.appendAssumeCapacity(switch_br_inst); 10771 }, 10772 .loop => { 10773 // The block must first extract the tag from the loaded union. 10774 const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 10775 try sema.air_instructions.append(sema.gpa, .{ 10776 .tag = .get_union_tag, 10777 .data = .{ .ty_op = .{ 10778 .ty = Air.internedToRef(union_obj.enum_tag_ty), 10779 .operand = operand_val, 10780 } }, 10781 }); 10782 const air_datas = sema.air_instructions.items(.data); 10783 air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); 10784 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ 10785 .body_len = 2, 10786 }); 10787 sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst)); 10788 sema.air_extra.appendAssumeCapacity(switch_br_inst); 10789 }, 10790 } 10791 10792 return capture_block_inst.toRef(); 10793 }, 10794 .error_set => { 10795 if (capture_byref) { 10796 return sema.fail( 10797 block, 10798 capture_src, 10799 "error set cannot be captured by reference", 10800 .{}, 10801 ); 10802 } 10803 10804 if (case_vals.len == 1) { 10805 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable; 10806 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?); 10807 return sema.bitCast(block, item_ty, operand_val, operand_src, null); 10808 } 10809 10810 var names: InferredErrorSet.NameMap = .{}; 10811 try names.ensureUnusedCapacity(sema.arena, case_vals.len); 10812 for (case_vals) |err| { 10813 const err_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, err, undefined) catch unreachable; 10814 names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {}); 10815 } 10816 const error_ty = try pt.errorSetFromUnsortedNames(names.keys()); 10817 return sema.bitCast(block, error_ty, operand_val, operand_src, null); 10818 }, 10819 else => { 10820 // In this case the capture value is just the passed-through value 10821 // of the switch condition. 10822 if (capture_byref) { 10823 return operand_ptr; 10824 } else { 10825 return operand_val; 10826 } 10827 }, 10828 } 10829 } 10830 }; 10831 10832 fn switchCond( 10833 sema: *Sema, 10834 block: *Block, 10835 src: LazySrcLoc, 10836 operand: Air.Inst.Ref, 10837 ) CompileError!Air.Inst.Ref { 10838 const pt = sema.pt; 10839 const zcu = pt.zcu; 10840 const operand_ty = sema.typeOf(operand); 10841 switch (operand_ty.zigTypeTag(zcu)) { 10842 .type, 10843 .void, 10844 .bool, 10845 .int, 10846 .float, 10847 .comptime_float, 10848 .comptime_int, 10849 .enum_literal, 10850 .pointer, 10851 .@"fn", 10852 .error_set, 10853 .@"enum", 10854 => { 10855 if (operand_ty.isSlice(zcu)) { 10856 return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)}); 10857 } 10858 if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| { 10859 return Air.internedToRef(opv.toIntern()); 10860 } 10861 return operand; 10862 }, 10863 10864 .@"union" => { 10865 try operand_ty.resolveFields(pt); 10866 const enum_ty = operand_ty.unionTagType(zcu) orelse { 10867 const msg = msg: { 10868 const msg = try sema.errMsg(src, "switch on union with no attached enum", .{}); 10869 errdefer msg.destroy(sema.gpa); 10870 if (operand_ty.srcLocOrNull(zcu)) |union_src| { 10871 try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{}); 10872 } 10873 break :msg msg; 10874 }; 10875 return sema.failWithOwnedErrorMsg(block, msg); 10876 }; 10877 return sema.unionToTag(block, enum_ty, operand, src); 10878 }, 10879 10880 .error_union, 10881 .noreturn, 10882 .array, 10883 .@"struct", 10884 .undefined, 10885 .null, 10886 .optional, 10887 .@"opaque", 10888 .vector, 10889 .frame, 10890 .@"anyframe", 10891 => return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)}), 10892 } 10893 } 10894 10895 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, LazySrcLoc); 10896 10897 fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10898 const tracy = trace(@src()); 10899 defer tracy.end(); 10900 10901 const pt = sema.pt; 10902 const zcu = pt.zcu; 10903 const gpa = sema.gpa; 10904 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10905 const switch_src = block.nodeOffset(inst_data.src_node); 10906 const switch_src_node_offset = inst_data.src_node; 10907 const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset }); 10908 const else_prong_src = block.src(.{ .node_offset_switch_special_prong = switch_src_node_offset }); 10909 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index); 10910 const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset }); 10911 const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset }); 10912 10913 const raw_operand_val = try sema.resolveInst(extra.data.operand); 10914 10915 // AstGen guarantees that the instruction immediately preceding 10916 // switch_block_err_union is a dbg_stmt 10917 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); 10918 10919 var header_extra_index: usize = extra.end; 10920 10921 const scalar_cases_len = extra.data.bits.scalar_cases_len; 10922 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: { 10923 const multi_cases_len = sema.code.extra[header_extra_index]; 10924 header_extra_index += 1; 10925 break :blk multi_cases_len; 10926 } else 0; 10927 10928 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: { 10929 const err_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]); 10930 header_extra_index += 1; 10931 // SwitchProngAnalysis wants inst_map to have space for the tag capture. 10932 // Note that the normal capture is referred to via the switch block 10933 // index, which there is already necessarily space for. 10934 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{err_capture_inst}); 10935 break :blk err_capture_inst; 10936 } else undefined; 10937 10938 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); 10939 defer case_vals.deinit(gpa); 10940 10941 const NonError = struct { 10942 body: []const Zir.Inst.Index, 10943 end: usize, 10944 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10945 }; 10946 10947 const non_error_case: NonError = non_error: { 10948 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]); 10949 const extra_body_start = header_extra_index + 1; 10950 break :non_error .{ 10951 .body = sema.code.bodySlice(extra_body_start, info.body_len), 10952 .end = extra_body_start + info.body_len, 10953 .capture = info.capture, 10954 }; 10955 }; 10956 10957 const Else = struct { 10958 body: []const Zir.Inst.Index, 10959 end: usize, 10960 is_inline: bool, 10961 has_capture: bool, 10962 }; 10963 10964 const else_case: Else = if (!extra.data.bits.has_else) .{ 10965 .body = &.{}, 10966 .end = non_error_case.end, 10967 .is_inline = false, 10968 .has_capture = false, 10969 } else special: { 10970 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[non_error_case.end]); 10971 const extra_body_start = non_error_case.end + 1; 10972 assert(info.capture != .by_ref); 10973 assert(!info.has_tag_capture); 10974 break :special .{ 10975 .body = sema.code.bodySlice(extra_body_start, info.body_len), 10976 .end = extra_body_start + info.body_len, 10977 .is_inline = info.is_inline, 10978 .has_capture = info.capture != .none, 10979 }; 10980 }; 10981 10982 var seen_errors = SwitchErrorSet.init(gpa); 10983 defer seen_errors.deinit(); 10984 10985 const operand_ty = sema.typeOf(raw_operand_val); 10986 const operand_err_set = if (extra.data.bits.payload_is_ref) 10987 operand_ty.childType(zcu) 10988 else 10989 operand_ty; 10990 10991 if (operand_err_set.zigTypeTag(zcu) != .error_union) { 10992 return sema.fail(block, switch_src, "expected error union type, found '{f}'", .{ 10993 operand_ty.fmt(pt), 10994 }); 10995 } 10996 10997 const operand_err_set_ty = operand_err_set.errorUnionSet(zcu); 10998 10999 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 11000 try sema.air_instructions.append(gpa, .{ 11001 .tag = .block, 11002 .data = undefined, 11003 }); 11004 var label: Block.Label = .{ 11005 .zir_block = inst, 11006 .merges = .{ 11007 .src_locs = .{}, 11008 .results = .{}, 11009 .br_list = .{}, 11010 .block_inst = block_inst, 11011 }, 11012 }; 11013 11014 var child_block: Block = .{ 11015 .parent = block, 11016 .sema = sema, 11017 .namespace = block.namespace, 11018 .instructions = .{}, 11019 .label = &label, 11020 .inlining = block.inlining, 11021 .comptime_reason = block.comptime_reason, 11022 .is_typeof = block.is_typeof, 11023 .c_import_buf = block.c_import_buf, 11024 .runtime_cond = block.runtime_cond, 11025 .runtime_loop = block.runtime_loop, 11026 .runtime_index = block.runtime_index, 11027 .error_return_trace_index = block.error_return_trace_index, 11028 .want_safety = block.want_safety, 11029 .src_base_inst = block.src_base_inst, 11030 .type_name_ctx = block.type_name_ctx, 11031 }; 11032 const merges = &child_block.label.?.merges; 11033 defer child_block.instructions.deinit(gpa); 11034 defer merges.deinit(gpa); 11035 11036 const resolved_err_set = try sema.resolveInferredErrorSetTy(block, main_src, operand_err_set_ty.toIntern()); 11037 if (Type.fromInterned(resolved_err_set).errorSetIsEmpty(zcu)) { 11038 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges); 11039 } 11040 11041 const else_error_ty: ?Type = try validateErrSetSwitch( 11042 sema, 11043 block, 11044 &seen_errors, 11045 &case_vals, 11046 operand_err_set_ty, 11047 inst_data, 11048 scalar_cases_len, 11049 multi_cases_len, 11050 .{ .body = else_case.body, .end = else_case.end, .src = else_prong_src }, 11051 extra.data.bits.has_else, 11052 ); 11053 11054 var spa: SwitchProngAnalysis = .{ 11055 .sema = sema, 11056 .parent_block = block, 11057 .operand = .{ 11058 .simple = .{ 11059 .by_val = undefined, // must be set to the unwrapped error code before use 11060 .by_ref = undefined, 11061 .cond = raw_operand_val, 11062 }, 11063 }, 11064 .else_error_ty = else_error_ty, 11065 .switch_block_inst = inst, 11066 .tag_capture_inst = undefined, 11067 }; 11068 11069 if (try sema.resolveDefinedValue(&child_block, main_src, raw_operand_val)) |ov| { 11070 const operand_val = if (extra.data.bits.payload_is_ref) 11071 (try sema.pointerDeref(&child_block, main_src, ov, operand_ty)).? 11072 else 11073 ov; 11074 11075 if (operand_val.errorUnionIsPayload(zcu)) { 11076 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges); 11077 } else { 11078 const err_val = Value.fromInterned(try pt.intern(.{ 11079 .err = .{ 11080 .ty = operand_err_set_ty.toIntern(), 11081 .name = operand_val.getErrorName(zcu).unwrap().?, 11082 }, 11083 })); 11084 spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref) 11085 try sema.analyzeErrUnionCodePtr(block, switch_operand_src, raw_operand_val) 11086 else 11087 try sema.analyzeErrUnionCode(block, switch_operand_src, raw_operand_val); 11088 11089 if (extra.data.bits.any_uses_err_capture) { 11090 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val); 11091 } 11092 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst)); 11093 11094 return resolveSwitchComptime( 11095 sema, 11096 spa, 11097 &child_block, 11098 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val), 11099 err_val, 11100 operand_err_set_ty, 11101 switch_src_node_offset, 11102 .{ 11103 .body = else_case.body, 11104 .end = else_case.end, 11105 .capture = if (else_case.has_capture) .by_val else .none, 11106 .is_inline = else_case.is_inline, 11107 .has_tag_capture = false, 11108 }, 11109 case_vals, 11110 scalar_cases_len, 11111 multi_cases_len, 11112 true, 11113 false, 11114 ); 11115 } 11116 } 11117 11118 if (scalar_cases_len + multi_cases_len == 0) { 11119 if (else_error_ty) |ty| if (ty.errorSetIsEmpty(zcu)) { 11120 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges); 11121 }; 11122 } 11123 11124 if (child_block.isComptime()) { 11125 _ = try sema.resolveConstDefinedValue(&child_block, main_operand_src, raw_operand_val, null); 11126 unreachable; 11127 } 11128 11129 const cond = if (extra.data.bits.payload_is_ref) blk: { 11130 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val).elemType2(zcu)); 11131 const loaded = try sema.analyzeLoad(block, main_src, raw_operand_val, main_src); 11132 break :blk try sema.analyzeIsNonErr(block, main_src, loaded); 11133 } else blk: { 11134 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val)); 11135 break :blk try sema.analyzeIsNonErr(block, main_src, raw_operand_val); 11136 }; 11137 11138 var sub_block = child_block.makeSubBlock(); 11139 sub_block.runtime_loop = null; 11140 sub_block.runtime_cond = main_operand_src; 11141 sub_block.runtime_index.increment(); 11142 sub_block.need_debug_scope = null; // this body is emitted regardless 11143 defer sub_block.instructions.deinit(gpa); 11144 11145 const non_error_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body); 11146 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); 11147 defer gpa.free(true_instructions); 11148 11149 spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref) 11150 try sema.analyzeErrUnionCodePtr(&sub_block, switch_operand_src, raw_operand_val) 11151 else 11152 try sema.analyzeErrUnionCode(&sub_block, switch_operand_src, raw_operand_val); 11153 11154 if (extra.data.bits.any_uses_err_capture) { 11155 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val); 11156 } 11157 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst)); 11158 _ = try sema.analyzeSwitchRuntimeBlock( 11159 spa, 11160 &sub_block, 11161 switch_src, 11162 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val), 11163 operand_err_set_ty, 11164 switch_operand_src, 11165 case_vals, 11166 .{ 11167 .body = else_case.body, 11168 .end = else_case.end, 11169 .capture = if (else_case.has_capture) .by_val else .none, 11170 .is_inline = else_case.is_inline, 11171 .has_tag_capture = false, 11172 }, 11173 scalar_cases_len, 11174 multi_cases_len, 11175 false, 11176 undefined, 11177 true, 11178 switch_src_node_offset, 11179 else_prong_src, 11180 undefined, 11181 seen_errors, 11182 undefined, 11183 undefined, 11184 undefined, 11185 cond_dbg_node_index, 11186 true, 11187 ); 11188 11189 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 11190 true_instructions.len + sub_block.instructions.items.len); 11191 11192 _ = try child_block.addInst(.{ 11193 .tag = .cond_br, 11194 .data = .{ 11195 .pl_op = .{ 11196 .operand = cond, 11197 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 11198 .then_body_len = @intCast(true_instructions.len), 11199 .else_body_len = @intCast(sub_block.instructions.items.len), 11200 .branch_hints = .{ 11201 .true = non_error_hint, 11202 .false = .none, 11203 // Code coverage is desired for error handling. 11204 .then_cov = .poi, 11205 .else_cov = .poi, 11206 }, 11207 }), 11208 }, 11209 }, 11210 }); 11211 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); 11212 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 11213 11214 return sema.resolveAnalyzedBlock(block, main_src, &child_block, merges, false); 11215 } 11216 11217 fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { 11218 const tracy = trace(@src()); 11219 defer tracy.end(); 11220 11221 const pt = sema.pt; 11222 const zcu = pt.zcu; 11223 const gpa = sema.gpa; 11224 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 11225 const src = block.nodeOffset(inst_data.src_node); 11226 const src_node_offset = inst_data.src_node; 11227 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); 11228 const special_prong_src = block.src(.{ .node_offset_switch_special_prong = src_node_offset }); 11229 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); 11230 11231 const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: { 11232 const maybe_ptr = try sema.resolveInst(extra.data.operand); 11233 const val, const ref = if (operand_is_ref) 11234 .{ try sema.analyzeLoad(block, src, maybe_ptr, operand_src), maybe_ptr } 11235 else 11236 .{ maybe_ptr, undefined }; 11237 11238 const init_cond = try sema.switchCond(block, operand_src, val); 11239 11240 const operand_ty = sema.typeOf(val); 11241 11242 if (extra.data.bits.has_continue and !block.isComptime()) { 11243 // Even if the operand is comptime-known, this `switch` is runtime. 11244 if (try operand_ty.comptimeOnlySema(pt)) { 11245 return sema.failWithOwnedErrorMsg(block, msg: { 11246 const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)}); 11247 errdefer msg.destroy(gpa); 11248 try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{}); 11249 break :msg msg; 11250 }); 11251 } 11252 try sema.validateRuntimeValue(block, operand_src, maybe_ptr); 11253 const operand_alloc = if (extra.data.bits.any_non_inline_capture) a: { 11254 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(maybe_ptr)); 11255 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty); 11256 _ = try block.addBinOp(.store, operand_alloc, maybe_ptr); 11257 break :a operand_alloc; 11258 } else undefined; 11259 break :op .{ 11260 .{ .loop = .{ 11261 .operand_alloc = operand_alloc, 11262 .operand_is_ref = operand_is_ref, 11263 .init_cond = init_cond, 11264 } }, 11265 operand_ty, 11266 }; 11267 } 11268 11269 // We always use `simple` in the comptime case, because as far as the dispatching logic 11270 // is concerned, it really is dispatching a single prong. `resolveSwitchComptime` will 11271 // be resposible for recursively resolving different prongs as needed. 11272 break :op .{ 11273 .{ .simple = .{ 11274 .by_val = val, 11275 .by_ref = ref, 11276 .cond = init_cond, 11277 } }, 11278 operand_ty, 11279 }; 11280 }; 11281 11282 const union_originally = raw_operand_ty.zigTypeTag(zcu) == .@"union"; 11283 const err_set = raw_operand_ty.zigTypeTag(zcu) == .error_set; 11284 const cond_ty = switch (raw_operand_ty.zigTypeTag(zcu)) { 11285 .@"union" => raw_operand_ty.unionTagType(zcu).?, // validated by `switchCond` above 11286 else => raw_operand_ty, 11287 }; 11288 11289 // AstGen guarantees that the instruction immediately preceding 11290 // switch_block(_ref) is a dbg_stmt 11291 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); 11292 11293 var header_extra_index: usize = extra.end; 11294 11295 const scalar_cases_len = extra.data.bits.scalar_cases_len; 11296 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: { 11297 const multi_cases_len = sema.code.extra[header_extra_index]; 11298 header_extra_index += 1; 11299 break :blk multi_cases_len; 11300 } else 0; 11301 11302 const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: { 11303 const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]); 11304 header_extra_index += 1; 11305 // SwitchProngAnalysis wants inst_map to have space for the tag capture. 11306 // Note that the normal capture is referred to via the switch block 11307 // index, which there is already necessarily space for. 11308 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst}); 11309 break :blk tag_capture_inst; 11310 } else undefined; 11311 11312 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); 11313 defer case_vals.deinit(gpa); 11314 11315 const special_prong = extra.data.bits.specialProng(); 11316 const special: SpecialProng = switch (special_prong) { 11317 .none => .{ 11318 .body = &.{}, 11319 .end = header_extra_index, 11320 .capture = .none, 11321 .is_inline = false, 11322 .has_tag_capture = false, 11323 }, 11324 .under, .@"else" => blk: { 11325 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]); 11326 const extra_body_start = header_extra_index + 1; 11327 break :blk .{ 11328 .body = sema.code.bodySlice(extra_body_start, info.body_len), 11329 .end = extra_body_start + info.body_len, 11330 .capture = info.capture, 11331 .is_inline = info.is_inline, 11332 .has_tag_capture = info.has_tag_capture, 11333 }; 11334 }, 11335 }; 11336 11337 // Duplicate checking variables later also used for `inline else`. 11338 var seen_enum_fields: []?LazySrcLoc = &.{}; 11339 var seen_errors = SwitchErrorSet.init(gpa); 11340 var range_set = RangeSet.init(gpa, zcu); 11341 var true_count: u8 = 0; 11342 var false_count: u8 = 0; 11343 11344 defer { 11345 range_set.deinit(); 11346 gpa.free(seen_enum_fields); 11347 seen_errors.deinit(); 11348 } 11349 11350 var empty_enum = false; 11351 11352 var else_error_ty: ?Type = null; 11353 11354 // Validate usage of '_' prongs. 11355 if (special_prong == .under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) { 11356 const msg = msg: { 11357 const msg = try sema.errMsg( 11358 src, 11359 "'_' prong only allowed when switching on non-exhaustive enums", 11360 .{}, 11361 ); 11362 errdefer msg.destroy(gpa); 11363 try sema.errNote( 11364 special_prong_src, 11365 msg, 11366 "'_' prong here", 11367 .{}, 11368 ); 11369 try sema.errNote( 11370 src, 11371 msg, 11372 "consider using 'else'", 11373 .{}, 11374 ); 11375 break :msg msg; 11376 }; 11377 return sema.failWithOwnedErrorMsg(block, msg); 11378 } 11379 11380 // Validate for duplicate items, missing else prong, and invalid range. 11381 switch (cond_ty.zigTypeTag(zcu)) { 11382 .@"union" => unreachable, // handled in `switchCond` 11383 .@"enum" => { 11384 seen_enum_fields = try gpa.alloc(?LazySrcLoc, cond_ty.enumFieldCount(zcu)); 11385 empty_enum = seen_enum_fields.len == 0 and !cond_ty.isNonexhaustiveEnum(zcu); 11386 @memset(seen_enum_fields, null); 11387 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. 11388 11389 var extra_index: usize = special.end; 11390 { 11391 var scalar_i: u32 = 0; 11392 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11393 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11394 extra_index += 1; 11395 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11396 extra_index += 1 + info.body_len; 11397 11398 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( 11399 block, 11400 seen_enum_fields, 11401 &range_set, 11402 item_ref, 11403 cond_ty, 11404 block.src(.{ .switch_case_item = .{ 11405 .switch_node_offset = src_node_offset, 11406 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 11407 .item_idx = .{ .kind = .single, .index = 0 }, 11408 } }), 11409 )); 11410 } 11411 } 11412 { 11413 var multi_i: u32 = 0; 11414 while (multi_i < multi_cases_len) : (multi_i += 1) { 11415 const items_len = sema.code.extra[extra_index]; 11416 extra_index += 1; 11417 const ranges_len = sema.code.extra[extra_index]; 11418 extra_index += 1; 11419 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11420 extra_index += 1; 11421 const items = sema.code.refSlice(extra_index, items_len); 11422 extra_index += items_len + info.body_len; 11423 11424 try case_vals.ensureUnusedCapacity(gpa, items.len); 11425 for (items, 0..) |item_ref, item_i| { 11426 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( 11427 block, 11428 seen_enum_fields, 11429 &range_set, 11430 item_ref, 11431 cond_ty, 11432 block.src(.{ .switch_case_item = .{ 11433 .switch_node_offset = src_node_offset, 11434 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11435 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11436 } }), 11437 )); 11438 } 11439 11440 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset); 11441 } 11442 } 11443 const all_tags_handled = for (seen_enum_fields) |seen_src| { 11444 if (seen_src == null) break false; 11445 } else true; 11446 11447 if (special_prong == .@"else") { 11448 if (all_tags_handled and !cond_ty.isNonexhaustiveEnum(zcu)) return sema.fail( 11449 block, 11450 special_prong_src, 11451 "unreachable else prong; all cases already handled", 11452 .{}, 11453 ); 11454 } else if (!all_tags_handled) { 11455 const msg = msg: { 11456 const msg = try sema.errMsg( 11457 src, 11458 "switch must handle all possibilities", 11459 .{}, 11460 ); 11461 errdefer msg.destroy(sema.gpa); 11462 for (seen_enum_fields, 0..) |seen_src, i| { 11463 if (seen_src != null) continue; 11464 11465 const field_name = cond_ty.enumFieldName(i, zcu); 11466 try sema.addFieldErrNote( 11467 cond_ty, 11468 i, 11469 msg, 11470 "unhandled enumeration value: '{f}'", 11471 .{field_name.fmt(&zcu.intern_pool)}, 11472 ); 11473 } 11474 try sema.errNote( 11475 cond_ty.srcLoc(zcu), 11476 msg, 11477 "enum '{f}' declared here", 11478 .{cond_ty.fmt(pt)}, 11479 ); 11480 break :msg msg; 11481 }; 11482 return sema.failWithOwnedErrorMsg(block, msg); 11483 } else if (special_prong == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) { 11484 return sema.fail( 11485 block, 11486 src, 11487 "switch on non-exhaustive enum must include 'else' or '_' prong", 11488 .{}, 11489 ); 11490 } 11491 }, 11492 .error_set => else_error_ty = try validateErrSetSwitch( 11493 sema, 11494 block, 11495 &seen_errors, 11496 &case_vals, 11497 cond_ty, 11498 inst_data, 11499 scalar_cases_len, 11500 multi_cases_len, 11501 .{ .body = special.body, .end = special.end, .src = special_prong_src }, 11502 special_prong == .@"else", 11503 ), 11504 .int, .comptime_int => { 11505 var extra_index: usize = special.end; 11506 { 11507 var scalar_i: u32 = 0; 11508 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11509 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11510 extra_index += 1; 11511 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11512 extra_index += 1 + info.body_len; 11513 11514 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( 11515 block, 11516 &range_set, 11517 item_ref, 11518 cond_ty, 11519 block.src(.{ .switch_case_item = .{ 11520 .switch_node_offset = src_node_offset, 11521 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 11522 .item_idx = .{ .kind = .single, .index = 0 }, 11523 } }), 11524 )); 11525 } 11526 } 11527 { 11528 var multi_i: u32 = 0; 11529 while (multi_i < multi_cases_len) : (multi_i += 1) { 11530 const items_len = sema.code.extra[extra_index]; 11531 extra_index += 1; 11532 const ranges_len = sema.code.extra[extra_index]; 11533 extra_index += 1; 11534 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11535 extra_index += 1; 11536 const items = sema.code.refSlice(extra_index, items_len); 11537 extra_index += items_len; 11538 11539 try case_vals.ensureUnusedCapacity(gpa, items.len); 11540 for (items, 0..) |item_ref, item_i| { 11541 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( 11542 block, 11543 &range_set, 11544 item_ref, 11545 cond_ty, 11546 block.src(.{ .switch_case_item = .{ 11547 .switch_node_offset = src_node_offset, 11548 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11549 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11550 } }), 11551 )); 11552 } 11553 11554 try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len); 11555 var range_i: u32 = 0; 11556 while (range_i < ranges_len) : (range_i += 1) { 11557 const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11558 extra_index += 1; 11559 const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11560 extra_index += 1; 11561 11562 const vals = try sema.validateSwitchRange( 11563 block, 11564 &range_set, 11565 item_first, 11566 item_last, 11567 cond_ty, 11568 block.src(.{ .switch_case_item = .{ 11569 .switch_node_offset = src_node_offset, 11570 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11571 .item_idx = .{ .kind = .range, .index = @intCast(range_i) }, 11572 } }), 11573 ); 11574 case_vals.appendAssumeCapacity(vals[0]); 11575 case_vals.appendAssumeCapacity(vals[1]); 11576 } 11577 11578 extra_index += info.body_len; 11579 } 11580 } 11581 11582 check_range: { 11583 if (cond_ty.zigTypeTag(zcu) == .int) { 11584 const min_int = try cond_ty.minInt(pt, cond_ty); 11585 const max_int = try cond_ty.maxInt(pt, cond_ty); 11586 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) { 11587 if (special_prong == .@"else") { 11588 return sema.fail( 11589 block, 11590 special_prong_src, 11591 "unreachable else prong; all cases already handled", 11592 .{}, 11593 ); 11594 } 11595 break :check_range; 11596 } 11597 } 11598 if (special_prong != .@"else") { 11599 return sema.fail( 11600 block, 11601 src, 11602 "switch must handle all possibilities", 11603 .{}, 11604 ); 11605 } 11606 } 11607 }, 11608 .bool => { 11609 var extra_index: usize = special.end; 11610 { 11611 var scalar_i: u32 = 0; 11612 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11613 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11614 extra_index += 1; 11615 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11616 extra_index += 1 + info.body_len; 11617 11618 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( 11619 block, 11620 &true_count, 11621 &false_count, 11622 item_ref, 11623 block.src(.{ .switch_case_item = .{ 11624 .switch_node_offset = src_node_offset, 11625 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 11626 .item_idx = .{ .kind = .single, .index = 0 }, 11627 } }), 11628 )); 11629 } 11630 } 11631 { 11632 var multi_i: u32 = 0; 11633 while (multi_i < multi_cases_len) : (multi_i += 1) { 11634 const items_len = sema.code.extra[extra_index]; 11635 extra_index += 1; 11636 const ranges_len = sema.code.extra[extra_index]; 11637 extra_index += 1; 11638 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11639 extra_index += 1; 11640 const items = sema.code.refSlice(extra_index, items_len); 11641 extra_index += items_len + info.body_len; 11642 11643 try case_vals.ensureUnusedCapacity(gpa, items.len); 11644 for (items, 0..) |item_ref, item_i| { 11645 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( 11646 block, 11647 &true_count, 11648 &false_count, 11649 item_ref, 11650 block.src(.{ .switch_case_item = .{ 11651 .switch_node_offset = src_node_offset, 11652 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11653 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11654 } }), 11655 )); 11656 } 11657 11658 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset); 11659 } 11660 } 11661 switch (special_prong) { 11662 .@"else" => { 11663 if (true_count + false_count == 2) { 11664 return sema.fail( 11665 block, 11666 special_prong_src, 11667 "unreachable else prong; all cases already handled", 11668 .{}, 11669 ); 11670 } 11671 }, 11672 .under, .none => { 11673 if (true_count + false_count < 2) { 11674 return sema.fail( 11675 block, 11676 src, 11677 "switch must handle all possibilities", 11678 .{}, 11679 ); 11680 } 11681 }, 11682 } 11683 }, 11684 .enum_literal, .void, .@"fn", .pointer, .type => { 11685 if (special_prong != .@"else") { 11686 return sema.fail( 11687 block, 11688 src, 11689 "else prong required when switching on type '{f}'", 11690 .{cond_ty.fmt(pt)}, 11691 ); 11692 } 11693 11694 var seen_values = ValueSrcMap{}; 11695 defer seen_values.deinit(gpa); 11696 11697 var extra_index: usize = special.end; 11698 { 11699 var scalar_i: u32 = 0; 11700 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11701 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11702 extra_index += 1; 11703 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11704 extra_index += 1; 11705 extra_index += info.body_len; 11706 11707 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( 11708 block, 11709 &seen_values, 11710 item_ref, 11711 cond_ty, 11712 block.src(.{ .switch_case_item = .{ 11713 .switch_node_offset = src_node_offset, 11714 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 11715 .item_idx = .{ .kind = .single, .index = 0 }, 11716 } }), 11717 )); 11718 } 11719 } 11720 { 11721 var multi_i: u32 = 0; 11722 while (multi_i < multi_cases_len) : (multi_i += 1) { 11723 const items_len = sema.code.extra[extra_index]; 11724 extra_index += 1; 11725 const ranges_len = sema.code.extra[extra_index]; 11726 extra_index += 1; 11727 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11728 extra_index += 1; 11729 const items = sema.code.refSlice(extra_index, items_len); 11730 extra_index += items_len + info.body_len; 11731 11732 try case_vals.ensureUnusedCapacity(gpa, items.len); 11733 for (items, 0..) |item_ref, item_i| { 11734 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( 11735 block, 11736 &seen_values, 11737 item_ref, 11738 cond_ty, 11739 block.src(.{ .switch_case_item = .{ 11740 .switch_node_offset = src_node_offset, 11741 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 11742 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 11743 } }), 11744 )); 11745 } 11746 11747 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset); 11748 } 11749 } 11750 }, 11751 11752 .error_union, 11753 .noreturn, 11754 .array, 11755 .@"struct", 11756 .undefined, 11757 .null, 11758 .optional, 11759 .@"opaque", 11760 .vector, 11761 .frame, 11762 .@"anyframe", 11763 .comptime_float, 11764 .float, 11765 => return sema.fail(block, operand_src, "invalid switch operand type '{f}'", .{ 11766 raw_operand_ty.fmt(pt), 11767 }), 11768 } 11769 11770 const spa: SwitchProngAnalysis = .{ 11771 .sema = sema, 11772 .parent_block = block, 11773 .operand = operand, 11774 .else_error_ty = else_error_ty, 11775 .switch_block_inst = inst, 11776 .tag_capture_inst = tag_capture_inst, 11777 }; 11778 11779 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 11780 try sema.air_instructions.append(gpa, .{ 11781 .tag = .block, 11782 .data = undefined, 11783 }); 11784 var label: Block.Label = .{ 11785 .zir_block = inst, 11786 .merges = .{ 11787 .src_locs = .{}, 11788 .results = .{}, 11789 .br_list = .{}, 11790 .block_inst = block_inst, 11791 }, 11792 }; 11793 11794 var child_block: Block = .{ 11795 .parent = block, 11796 .sema = sema, 11797 .namespace = block.namespace, 11798 .instructions = .{}, 11799 .label = &label, 11800 .inlining = block.inlining, 11801 .comptime_reason = block.comptime_reason, 11802 .is_typeof = block.is_typeof, 11803 .c_import_buf = block.c_import_buf, 11804 .runtime_cond = block.runtime_cond, 11805 .runtime_loop = block.runtime_loop, 11806 .runtime_index = block.runtime_index, 11807 .want_safety = block.want_safety, 11808 .error_return_trace_index = block.error_return_trace_index, 11809 .src_base_inst = block.src_base_inst, 11810 .type_name_ctx = block.type_name_ctx, 11811 }; 11812 const merges = &child_block.label.?.merges; 11813 defer child_block.instructions.deinit(gpa); 11814 defer merges.deinit(gpa); 11815 11816 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) { 11817 if (empty_enum) { 11818 return .void_value; 11819 } 11820 if (special_prong == .none) { 11821 return sema.fail(block, src, "switch must handle all possibilities", .{}); 11822 } 11823 const init_cond = switch (operand) { 11824 .simple => |s| s.cond, 11825 .loop => |l| l.init_cond, 11826 }; 11827 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and 11828 raw_operand_ty.zigTypeTag(zcu) == .@"enum" and !raw_operand_ty.isNonexhaustiveEnum(zcu)) 11829 { 11830 try sema.zirDbgStmt(block, cond_dbg_node_index); 11831 const ok = try block.addUnOp(.is_named_enum_value, init_cond); 11832 try sema.addSafetyCheck(block, src, ok, .corrupt_switch); 11833 } 11834 if (err_set and try sema.maybeErrorUnwrap(block, special.body, init_cond, operand_src, false)) { 11835 return .unreachable_value; 11836 } 11837 } 11838 11839 switch (operand) { 11840 .loop => {}, // always runtime; evaluation in comptime scope uses `simple` 11841 .simple => |s| { 11842 if (try sema.resolveDefinedValue(&child_block, src, s.cond)) |cond_val| { 11843 return resolveSwitchComptimeLoop( 11844 sema, 11845 spa, 11846 &child_block, 11847 if (operand_is_ref) 11848 sema.typeOf(s.by_ref) 11849 else 11850 raw_operand_ty, 11851 cond_ty, 11852 cond_val, 11853 src_node_offset, 11854 special, 11855 case_vals, 11856 scalar_cases_len, 11857 multi_cases_len, 11858 err_set, 11859 empty_enum, 11860 operand_is_ref, 11861 ); 11862 } 11863 11864 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline and !extra.data.bits.has_continue) { 11865 return spa.resolveProngComptime( 11866 &child_block, 11867 .special, 11868 special.body, 11869 special.capture, 11870 block.src(.{ .switch_capture = .{ 11871 .switch_node_offset = src_node_offset, 11872 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, 11873 } }), 11874 undefined, // case_vals may be undefined for special prongs 11875 .none, 11876 false, 11877 merges, 11878 ); 11879 } 11880 }, 11881 } 11882 11883 if (child_block.isComptime()) { 11884 _ = try sema.resolveConstDefinedValue(&child_block, operand_src, operand.simple.cond, null); 11885 unreachable; 11886 } 11887 11888 const air_switch_ref = try sema.analyzeSwitchRuntimeBlock( 11889 spa, 11890 &child_block, 11891 src, 11892 switch (operand) { 11893 .simple => |s| s.cond, 11894 .loop => |l| l.init_cond, 11895 }, 11896 cond_ty, 11897 operand_src, 11898 case_vals, 11899 special, 11900 scalar_cases_len, 11901 multi_cases_len, 11902 union_originally, 11903 raw_operand_ty, 11904 err_set, 11905 src_node_offset, 11906 special_prong_src, 11907 seen_enum_fields, 11908 seen_errors, 11909 range_set, 11910 true_count, 11911 false_count, 11912 cond_dbg_node_index, 11913 false, 11914 ); 11915 11916 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| { 11917 var replacement_block = block.makeSubBlock(); 11918 defer replacement_block.instructions.deinit(gpa); 11919 11920 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br); 11921 const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand; 11922 11923 if (extra.data.bits.any_non_inline_capture) { 11924 _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref); 11925 } 11926 11927 const new_operand_val = if (operand_is_ref) 11928 try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src) 11929 else 11930 new_operand_maybe_ref; 11931 11932 const new_cond = try sema.switchCond(&replacement_block, dispatch_src, new_operand_val); 11933 11934 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and 11935 cond_ty.zigTypeTag(zcu) == .@"enum" and !cond_ty.isNonexhaustiveEnum(zcu) and 11936 !try sema.isComptimeKnown(new_cond)) 11937 { 11938 const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond); 11939 try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch); 11940 } 11941 11942 _ = try replacement_block.addInst(.{ 11943 .tag = .switch_dispatch, 11944 .data = .{ .br = .{ 11945 .block_inst = air_switch_ref.toIndex().?, 11946 .operand = new_cond, 11947 } }, 11948 }); 11949 11950 if (replacement_block.instructions.items.len == 1) { 11951 // Optimization: we don't need a block! 11952 sema.air_instructions.set( 11953 @intFromEnum(placeholder_inst), 11954 sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])), 11955 ); 11956 continue; 11957 } 11958 11959 // Replace placeholder with a block. 11960 // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`. 11961 try sema.air_extra.ensureUnusedCapacity( 11962 gpa, 11963 @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len, 11964 ); 11965 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{ 11966 .tag = .block, 11967 .data = .{ .ty_pl = .{ 11968 .ty = .noreturn_type, 11969 .payload = sema.addExtraAssumeCapacity(Air.Block{ 11970 .body_len = @intCast(replacement_block.instructions.items.len), 11971 }), 11972 } }, 11973 }); 11974 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); 11975 } 11976 11977 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false); 11978 } 11979 11980 const SpecialProng = struct { 11981 body: []const Zir.Inst.Index, 11982 end: usize, 11983 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 11984 is_inline: bool, 11985 has_tag_capture: bool, 11986 }; 11987 11988 fn analyzeSwitchRuntimeBlock( 11989 sema: *Sema, 11990 spa: SwitchProngAnalysis, 11991 child_block: *Block, 11992 src: LazySrcLoc, 11993 operand: Air.Inst.Ref, 11994 operand_ty: Type, 11995 operand_src: LazySrcLoc, 11996 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref), 11997 special: SpecialProng, 11998 scalar_cases_len: usize, 11999 multi_cases_len: usize, 12000 union_originally: bool, 12001 maybe_union_ty: Type, 12002 err_set: bool, 12003 switch_node_offset: std.zig.Ast.Node.Offset, 12004 special_prong_src: LazySrcLoc, 12005 seen_enum_fields: []?LazySrcLoc, 12006 seen_errors: SwitchErrorSet, 12007 range_set: RangeSet, 12008 true_count: u8, 12009 false_count: u8, 12010 cond_dbg_node_index: Zir.Inst.Index, 12011 allow_err_code_unwrap: bool, 12012 ) CompileError!Air.Inst.Ref { 12013 const pt = sema.pt; 12014 const zcu = pt.zcu; 12015 const gpa = sema.gpa; 12016 const ip = &zcu.intern_pool; 12017 12018 const block = child_block.parent.?; 12019 12020 const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * 12021 @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 2; 12022 var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra); 12023 defer cases_extra.deinit(gpa); 12024 12025 var branch_hints = try std.ArrayListUnmanaged(std.builtin.BranchHint).initCapacity(gpa, scalar_cases_len); 12026 defer branch_hints.deinit(gpa); 12027 12028 var case_block = child_block.makeSubBlock(); 12029 case_block.runtime_loop = null; 12030 case_block.runtime_cond = operand_src; 12031 case_block.runtime_index.increment(); 12032 case_block.need_debug_scope = null; // this body is emitted regardless 12033 defer case_block.instructions.deinit(gpa); 12034 12035 var extra_index: usize = special.end; 12036 12037 var scalar_i: usize = 0; 12038 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 12039 extra_index += 1; 12040 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12041 extra_index += 1; 12042 const body = sema.code.bodySlice(extra_index, info.body_len); 12043 extra_index += info.body_len; 12044 12045 case_block.instructions.shrinkRetainingCapacity(0); 12046 case_block.error_return_trace_index = child_block.error_return_trace_index; 12047 12048 const item = case_vals.items[scalar_i]; 12049 // `item` is already guaranteed to be constant known. 12050 12051 const analyze_body = if (union_originally) blk: { 12052 const unresolved_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 12053 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable; 12054 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; 12055 break :blk field_ty.zigTypeTag(zcu) != .noreturn; 12056 } else true; 12057 12058 const prong_hint: std.builtin.BranchHint = if (err_set and 12059 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) 12060 h: { 12061 // nothing to do here. weight against error branch 12062 break :h .unlikely; 12063 } else if (analyze_body) h: { 12064 break :h try spa.analyzeProngRuntime( 12065 &case_block, 12066 .normal, 12067 body, 12068 info.capture, 12069 child_block.src(.{ .switch_capture = .{ 12070 .switch_node_offset = switch_node_offset, 12071 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 12072 } }), 12073 &.{item}, 12074 if (info.is_inline) item else .none, 12075 info.has_tag_capture, 12076 ); 12077 } else h: { 12078 _ = try case_block.addNoOp(.unreach); 12079 break :h .none; 12080 }; 12081 12082 try branch_hints.append(gpa, prong_hint); 12083 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12084 1 + // `item`, no ranges 12085 case_block.instructions.items.len); 12086 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12087 .items_len = 1, 12088 .ranges_len = 0, 12089 .body_len = @intCast(case_block.instructions.items.len), 12090 })); 12091 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12092 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12093 } 12094 12095 var cases_len = scalar_cases_len; 12096 var case_val_idx: usize = scalar_cases_len; 12097 var multi_i: u32 = 0; 12098 while (multi_i < multi_cases_len) : (multi_i += 1) { 12099 const items_len = sema.code.extra[extra_index]; 12100 extra_index += 1; 12101 const ranges_len = sema.code.extra[extra_index]; 12102 extra_index += 1; 12103 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12104 extra_index += 1 + items_len + 2 * ranges_len; 12105 12106 const items = case_vals.items[case_val_idx..][0..items_len]; 12107 case_val_idx += items_len; 12108 // TODO: @ptrCast slice once Sema supports it 12109 const ranges: []const [2]Air.Inst.Ref = @as([*]const [2]Air.Inst.Ref, @ptrCast(case_vals.items[case_val_idx..]))[0..ranges_len]; 12110 case_val_idx += ranges_len * 2; 12111 12112 const body = sema.code.bodySlice(extra_index, info.body_len); 12113 extra_index += info.body_len; 12114 12115 case_block.instructions.shrinkRetainingCapacity(0); 12116 case_block.error_return_trace_index = child_block.error_return_trace_index; 12117 12118 // Generate all possible cases as scalar prongs. 12119 if (info.is_inline) { 12120 var emit_bb = false; 12121 12122 for (ranges, 0..) |range_items, range_i| { 12123 var item = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable; 12124 const item_last = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable; 12125 12126 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({ 12127 // Previous validation has resolved any possible lazy values. 12128 const result = try arith.incrementDefinedInt(sema, operand_ty, item); 12129 assert(!result.overflow); 12130 item = result.val; 12131 }) { 12132 cases_len += 1; 12133 12134 const item_ref = Air.internedToRef(item.toIntern()); 12135 12136 case_block.instructions.shrinkRetainingCapacity(0); 12137 case_block.error_return_trace_index = child_block.error_return_trace_index; 12138 12139 if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{ 12140 .switch_node_offset = switch_node_offset, 12141 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12142 .item_idx = .{ .kind = .range, .index = @intCast(range_i) }, 12143 } })); 12144 emit_bb = true; 12145 12146 const prong_hint = try spa.analyzeProngRuntime( 12147 &case_block, 12148 .normal, 12149 body, 12150 info.capture, 12151 child_block.src(.{ .switch_capture = .{ 12152 .switch_node_offset = switch_node_offset, 12153 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12154 } }), 12155 undefined, // case_vals may be undefined for ranges 12156 item_ref, 12157 info.has_tag_capture, 12158 ); 12159 try branch_hints.append(gpa, prong_hint); 12160 12161 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12162 1 + // `item`, no ranges 12163 case_block.instructions.items.len); 12164 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12165 .items_len = 1, 12166 .ranges_len = 0, 12167 .body_len = @intCast(case_block.instructions.items.len), 12168 })); 12169 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12170 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12171 12172 if (item.compareScalar(.eq, item_last, operand_ty, zcu)) break; 12173 } 12174 } 12175 12176 for (items, 0..) |item, item_i| { 12177 cases_len += 1; 12178 12179 case_block.instructions.shrinkRetainingCapacity(0); 12180 case_block.error_return_trace_index = child_block.error_return_trace_index; 12181 12182 const analyze_body = if (union_originally) blk: { 12183 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 12184 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; 12185 break :blk field_ty.zigTypeTag(zcu) != .noreturn; 12186 } else true; 12187 12188 if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{ 12189 .switch_node_offset = switch_node_offset, 12190 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12191 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 12192 } })); 12193 emit_bb = true; 12194 12195 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: { 12196 break :h try spa.analyzeProngRuntime( 12197 &case_block, 12198 .normal, 12199 body, 12200 info.capture, 12201 child_block.src(.{ .switch_capture = .{ 12202 .switch_node_offset = switch_node_offset, 12203 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12204 } }), 12205 &.{item}, 12206 item, 12207 info.has_tag_capture, 12208 ); 12209 } else h: { 12210 _ = try case_block.addNoOp(.unreach); 12211 break :h .none; 12212 }; 12213 try branch_hints.append(gpa, prong_hint); 12214 12215 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12216 1 + // `item`, no ranges 12217 case_block.instructions.items.len); 12218 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12219 .items_len = 1, 12220 .ranges_len = 0, 12221 .body_len = @intCast(case_block.instructions.items.len), 12222 })); 12223 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12224 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12225 } 12226 12227 continue; 12228 } 12229 12230 cases_len += 1; 12231 12232 const analyze_body = if (union_originally) 12233 for (items) |item| { 12234 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 12235 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; 12236 if (field_ty.zigTypeTag(zcu) != .noreturn) break true; 12237 } else false 12238 else 12239 true; 12240 12241 const prong_hint: std.builtin.BranchHint = if (err_set and 12242 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) 12243 h: { 12244 // nothing to do here. weight against error branch 12245 break :h .unlikely; 12246 } else if (analyze_body) h: { 12247 break :h try spa.analyzeProngRuntime( 12248 &case_block, 12249 .normal, 12250 body, 12251 info.capture, 12252 child_block.src(.{ .switch_capture = .{ 12253 .switch_node_offset = switch_node_offset, 12254 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12255 } }), 12256 items, 12257 .none, 12258 false, 12259 ); 12260 } else h: { 12261 _ = try case_block.addNoOp(.unreach); 12262 break :h .none; 12263 }; 12264 12265 try branch_hints.append(gpa, prong_hint); 12266 12267 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12268 items.len + 2 * ranges_len + 12269 case_block.instructions.items.len); 12270 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12271 .items_len = @intCast(items.len), 12272 .ranges_len = @intCast(ranges_len), 12273 .body_len = @intCast(case_block.instructions.items.len), 12274 })); 12275 12276 for (items) |item| { 12277 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12278 } 12279 for (ranges) |range| { 12280 cases_extra.appendSliceAssumeCapacity(&.{ 12281 @intFromEnum(range[0]), 12282 @intFromEnum(range[1]), 12283 }); 12284 } 12285 12286 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12287 } 12288 12289 const else_body: []const Air.Inst.Index = if (special.body.len != 0 or case_block.wantSafety()) else_body: { 12290 var emit_bb = false; 12291 if (special.is_inline) switch (operand_ty.zigTypeTag(zcu)) { 12292 .@"enum" => { 12293 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) { 12294 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{ 12295 operand_ty.fmt(pt), 12296 }); 12297 } 12298 for (seen_enum_fields, 0..) |f, i| { 12299 if (f != null) continue; 12300 cases_len += 1; 12301 12302 const item_val = try pt.enumValueFieldIndex(operand_ty, @intCast(i)); 12303 const item_ref = Air.internedToRef(item_val.toIntern()); 12304 12305 case_block.instructions.shrinkRetainingCapacity(0); 12306 case_block.error_return_trace_index = child_block.error_return_trace_index; 12307 12308 const analyze_body = if (union_originally) blk: { 12309 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?; 12310 break :blk field_ty.zigTypeTag(zcu) != .noreturn; 12311 } else true; 12312 12313 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12314 emit_bb = true; 12315 12316 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: { 12317 break :h try spa.analyzeProngRuntime( 12318 &case_block, 12319 .special, 12320 special.body, 12321 special.capture, 12322 child_block.src(.{ .switch_capture = .{ 12323 .switch_node_offset = switch_node_offset, 12324 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, 12325 } }), 12326 &.{item_ref}, 12327 item_ref, 12328 special.has_tag_capture, 12329 ); 12330 } else h: { 12331 _ = try case_block.addNoOp(.unreach); 12332 break :h .none; 12333 }; 12334 try branch_hints.append(gpa, prong_hint); 12335 12336 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12337 1 + // `item`, no ranges 12338 case_block.instructions.items.len); 12339 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12340 .items_len = 1, 12341 .ranges_len = 0, 12342 .body_len = @intCast(case_block.instructions.items.len), 12343 })); 12344 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12345 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12346 } 12347 }, 12348 .error_set => { 12349 if (operand_ty.isAnyError(zcu)) { 12350 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{ 12351 operand_ty.fmt(pt), 12352 }); 12353 } 12354 const error_names = operand_ty.errorSetNames(zcu); 12355 for (0..error_names.len) |name_index| { 12356 const error_name = error_names.get(ip)[name_index]; 12357 if (seen_errors.contains(error_name)) continue; 12358 cases_len += 1; 12359 12360 const item_val = try pt.intern(.{ .err = .{ 12361 .ty = operand_ty.toIntern(), 12362 .name = error_name, 12363 } }); 12364 const item_ref = Air.internedToRef(item_val); 12365 12366 case_block.instructions.shrinkRetainingCapacity(0); 12367 case_block.error_return_trace_index = child_block.error_return_trace_index; 12368 12369 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12370 emit_bb = true; 12371 12372 const prong_hint = try spa.analyzeProngRuntime( 12373 &case_block, 12374 .special, 12375 special.body, 12376 special.capture, 12377 child_block.src(.{ .switch_capture = .{ 12378 .switch_node_offset = switch_node_offset, 12379 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, 12380 } }), 12381 &.{item_ref}, 12382 item_ref, 12383 special.has_tag_capture, 12384 ); 12385 try branch_hints.append(gpa, prong_hint); 12386 12387 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12388 1 + // `item`, no ranges 12389 case_block.instructions.items.len); 12390 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12391 .items_len = 1, 12392 .ranges_len = 0, 12393 .body_len = @intCast(case_block.instructions.items.len), 12394 })); 12395 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12396 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12397 } 12398 }, 12399 .int => { 12400 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set); 12401 while (try it.next()) |cur| { 12402 cases_len += 1; 12403 12404 const item_ref = Air.internedToRef(cur); 12405 12406 case_block.instructions.shrinkRetainingCapacity(0); 12407 case_block.error_return_trace_index = child_block.error_return_trace_index; 12408 12409 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12410 emit_bb = true; 12411 12412 const prong_hint = try spa.analyzeProngRuntime( 12413 &case_block, 12414 .special, 12415 special.body, 12416 special.capture, 12417 child_block.src(.{ .switch_capture = .{ 12418 .switch_node_offset = switch_node_offset, 12419 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, 12420 } }), 12421 &.{item_ref}, 12422 item_ref, 12423 special.has_tag_capture, 12424 ); 12425 try branch_hints.append(gpa, prong_hint); 12426 12427 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12428 1 + // `item`, no ranges 12429 case_block.instructions.items.len); 12430 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12431 .items_len = 1, 12432 .ranges_len = 0, 12433 .body_len = @intCast(case_block.instructions.items.len), 12434 })); 12435 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12436 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12437 } 12438 }, 12439 .bool => { 12440 if (true_count == 0) { 12441 cases_len += 1; 12442 12443 case_block.instructions.shrinkRetainingCapacity(0); 12444 case_block.error_return_trace_index = child_block.error_return_trace_index; 12445 12446 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12447 emit_bb = true; 12448 12449 const prong_hint = try spa.analyzeProngRuntime( 12450 &case_block, 12451 .special, 12452 special.body, 12453 special.capture, 12454 child_block.src(.{ .switch_capture = .{ 12455 .switch_node_offset = switch_node_offset, 12456 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, 12457 } }), 12458 &.{.bool_true}, 12459 .bool_true, 12460 special.has_tag_capture, 12461 ); 12462 try branch_hints.append(gpa, prong_hint); 12463 12464 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12465 1 + // `item`, no ranges 12466 case_block.instructions.items.len); 12467 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12468 .items_len = 1, 12469 .ranges_len = 0, 12470 .body_len = @intCast(case_block.instructions.items.len), 12471 })); 12472 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true)); 12473 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12474 } 12475 if (false_count == 0) { 12476 cases_len += 1; 12477 12478 case_block.instructions.shrinkRetainingCapacity(0); 12479 case_block.error_return_trace_index = child_block.error_return_trace_index; 12480 12481 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12482 emit_bb = true; 12483 12484 const prong_hint = try spa.analyzeProngRuntime( 12485 &case_block, 12486 .special, 12487 special.body, 12488 special.capture, 12489 child_block.src(.{ .switch_capture = .{ 12490 .switch_node_offset = switch_node_offset, 12491 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, 12492 } }), 12493 &.{.bool_false}, 12494 .bool_false, 12495 special.has_tag_capture, 12496 ); 12497 try branch_hints.append(gpa, prong_hint); 12498 12499 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 12500 1 + // `item`, no ranges 12501 case_block.instructions.items.len); 12502 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ 12503 .items_len = 1, 12504 .ranges_len = 0, 12505 .body_len = @intCast(case_block.instructions.items.len), 12506 })); 12507 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false)); 12508 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12509 } 12510 }, 12511 else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{ 12512 operand_ty.fmt(pt), 12513 }), 12514 }; 12515 12516 case_block.instructions.shrinkRetainingCapacity(0); 12517 case_block.error_return_trace_index = child_block.error_return_trace_index; 12518 12519 if (zcu.backendSupportsFeature(.is_named_enum_value) and 12520 special.body.len != 0 and block.wantSafety() and 12521 operand_ty.zigTypeTag(zcu) == .@"enum" and 12522 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally)) 12523 { 12524 try sema.zirDbgStmt(&case_block, cond_dbg_node_index); 12525 const ok = try case_block.addUnOp(.is_named_enum_value, operand); 12526 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); 12527 } 12528 12529 const analyze_body = if (union_originally and !special.is_inline) 12530 for (seen_enum_fields, 0..) |seen_field, index| { 12531 if (seen_field != null) continue; 12532 const union_obj = zcu.typeToUnion(maybe_union_ty).?; 12533 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]); 12534 if (field_ty.zigTypeTag(zcu) != .noreturn) break true; 12535 } else false 12536 else 12537 true; 12538 const else_hint: std.builtin.BranchHint = if (special.body.len != 0 and err_set and 12539 try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src, allow_err_code_unwrap)) 12540 h: { 12541 // nothing to do here. weight against error branch 12542 break :h .unlikely; 12543 } else if (special.body.len != 0 and analyze_body and !special.is_inline) h: { 12544 break :h try spa.analyzeProngRuntime( 12545 &case_block, 12546 .special, 12547 special.body, 12548 special.capture, 12549 child_block.src(.{ .switch_capture = .{ 12550 .switch_node_offset = switch_node_offset, 12551 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, 12552 } }), 12553 undefined, // case_vals may be undefined for special prongs 12554 .none, 12555 false, 12556 ); 12557 } else h: { 12558 // We still need a terminator in this block, but we have proven 12559 // that it is unreachable. 12560 if (case_block.wantSafety()) { 12561 try sema.zirDbgStmt(&case_block, cond_dbg_node_index); 12562 try sema.safetyPanic(&case_block, src, .corrupt_switch); 12563 } else { 12564 _ = try case_block.addNoOp(.unreach); 12565 } 12566 // Safety check / unreachable branches are cold. 12567 break :h .cold; 12568 }; 12569 12570 try branch_hints.append(gpa, else_hint); 12571 break :else_body case_block.instructions.items; 12572 } else else_body: { 12573 try branch_hints.append(gpa, .none); 12574 break :else_body &.{}; 12575 }; 12576 12577 assert(branch_hints.items.len == cases_len + 1); 12578 12579 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len + 12580 cases_extra.items.len + else_body.len + 12581 (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints 12582 12583 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{ 12584 .cases_len = @intCast(cases_len), 12585 .else_body_len = @intCast(else_body.len), 12586 }); 12587 12588 { 12589 // Add branch hints. 12590 var cur_bag: u32 = 0; 12591 for (branch_hints.items, 0..) |hint, idx| { 12592 const idx_in_bag = idx % 10; 12593 cur_bag |= @as(u32, @intFromEnum(hint)) << @intCast(idx_in_bag * 3); 12594 if (idx_in_bag == 9) { 12595 sema.air_extra.appendAssumeCapacity(cur_bag); 12596 cur_bag = 0; 12597 } 12598 } 12599 if (branch_hints.items.len % 10 != 0) { 12600 sema.air_extra.appendAssumeCapacity(cur_bag); 12601 } 12602 } 12603 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items)); 12604 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_body)); 12605 12606 const has_any_continues = spa.operand == .loop and child_block.label.?.merges.extra_insts.items.len > 0; 12607 12608 return try child_block.addInst(.{ 12609 .tag = if (has_any_continues) .loop_switch_br else .switch_br, 12610 .data = .{ .pl_op = .{ 12611 .operand = operand, 12612 .payload = payload_index, 12613 } }, 12614 }); 12615 } 12616 12617 fn resolveSwitchComptimeLoop( 12618 sema: *Sema, 12619 init_spa: SwitchProngAnalysis, 12620 child_block: *Block, 12621 maybe_ptr_operand_ty: Type, 12622 cond_ty: Type, 12623 init_cond_val: Value, 12624 switch_node_offset: std.zig.Ast.Node.Offset, 12625 special: SpecialProng, 12626 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref), 12627 scalar_cases_len: u32, 12628 multi_cases_len: u32, 12629 err_set: bool, 12630 empty_enum: bool, 12631 operand_is_ref: bool, 12632 ) CompileError!Air.Inst.Ref { 12633 var spa = init_spa; 12634 var cond_val = init_cond_val; 12635 12636 while (true) { 12637 if (resolveSwitchComptime( 12638 sema, 12639 spa, 12640 child_block, 12641 spa.operand.simple.cond, 12642 cond_val, 12643 cond_ty, 12644 switch_node_offset, 12645 special, 12646 case_vals, 12647 scalar_cases_len, 12648 multi_cases_len, 12649 err_set, 12650 empty_enum, 12651 )) |result| { 12652 return result; 12653 } else |err| switch (err) { 12654 error.ComptimeBreak => { 12655 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst)); 12656 if (break_inst.tag != .switch_continue) return error.ComptimeBreak; 12657 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data; 12658 if (extra.block_inst != spa.switch_block_inst) return error.ComptimeBreak; 12659 // This is a `switch_continue` targeting this block. Change the operand and start over. 12660 const src = child_block.nodeOffset(extra.operand_src_node.unwrap().?); 12661 const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand); 12662 const new_operand = try sema.coerce(child_block, maybe_ptr_operand_ty, new_operand_uncoerced, src); 12663 12664 try sema.emitBackwardBranch(child_block, src); 12665 12666 const val, const ref = if (operand_is_ref) 12667 .{ try sema.analyzeLoad(child_block, src, new_operand, src), new_operand } 12668 else 12669 .{ new_operand, undefined }; 12670 12671 const cond_ref = try sema.switchCond(child_block, src, val); 12672 12673 cond_val = try sema.resolveConstDefinedValue(child_block, src, cond_ref, null); 12674 spa.operand = .{ .simple = .{ 12675 .by_val = val, 12676 .by_ref = ref, 12677 .cond = cond_ref, 12678 } }; 12679 }, 12680 else => |e| return e, 12681 } 12682 } 12683 } 12684 12685 fn resolveSwitchComptime( 12686 sema: *Sema, 12687 spa: SwitchProngAnalysis, 12688 child_block: *Block, 12689 cond_operand: Air.Inst.Ref, 12690 operand_val: Value, 12691 operand_ty: Type, 12692 switch_node_offset: std.zig.Ast.Node.Offset, 12693 special: SpecialProng, 12694 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref), 12695 scalar_cases_len: u32, 12696 multi_cases_len: u32, 12697 err_set: bool, 12698 empty_enum: bool, 12699 ) CompileError!Air.Inst.Ref { 12700 const merges = &child_block.label.?.merges; 12701 const resolved_operand_val = try sema.resolveLazyValue(operand_val); 12702 12703 var extra_index: usize = special.end; 12704 { 12705 var scalar_i: usize = 0; 12706 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 12707 extra_index += 1; 12708 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12709 extra_index += 1; 12710 const body = sema.code.bodySlice(extra_index, info.body_len); 12711 extra_index += info.body_len; 12712 12713 const item = case_vals.items[scalar_i]; 12714 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 12715 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) { 12716 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand); 12717 return spa.resolveProngComptime( 12718 child_block, 12719 .normal, 12720 body, 12721 info.capture, 12722 child_block.src(.{ .switch_capture = .{ 12723 .switch_node_offset = switch_node_offset, 12724 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 12725 } }), 12726 &.{item}, 12727 if (info.is_inline) cond_operand else .none, 12728 info.has_tag_capture, 12729 merges, 12730 ); 12731 } 12732 } 12733 } 12734 { 12735 var multi_i: usize = 0; 12736 var case_val_idx: usize = scalar_cases_len; 12737 while (multi_i < multi_cases_len) : (multi_i += 1) { 12738 const items_len = sema.code.extra[extra_index]; 12739 extra_index += 1; 12740 const ranges_len = sema.code.extra[extra_index]; 12741 extra_index += 1; 12742 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12743 extra_index += 1 + items_len; 12744 const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len); 12745 12746 const items = case_vals.items[case_val_idx..][0..items_len]; 12747 case_val_idx += items_len; 12748 12749 for (items) |item| { 12750 // Validation above ensured these will succeed. 12751 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable; 12752 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) { 12753 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand); 12754 return spa.resolveProngComptime( 12755 child_block, 12756 .normal, 12757 body, 12758 info.capture, 12759 child_block.src(.{ .switch_capture = .{ 12760 .switch_node_offset = switch_node_offset, 12761 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12762 } }), 12763 items, 12764 if (info.is_inline) cond_operand else .none, 12765 info.has_tag_capture, 12766 merges, 12767 ); 12768 } 12769 } 12770 12771 var range_i: usize = 0; 12772 while (range_i < ranges_len) : (range_i += 1) { 12773 const range_items = case_vals.items[case_val_idx..][0..2]; 12774 extra_index += 2; 12775 case_val_idx += 2; 12776 12777 // Validation above ensured these will succeed. 12778 const first_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable; 12779 const last_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable; 12780 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and 12781 (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty))) 12782 { 12783 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand); 12784 return spa.resolveProngComptime( 12785 child_block, 12786 .normal, 12787 body, 12788 info.capture, 12789 child_block.src(.{ .switch_capture = .{ 12790 .switch_node_offset = switch_node_offset, 12791 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12792 } }), 12793 undefined, // case_vals may be undefined for ranges 12794 if (info.is_inline) cond_operand else .none, 12795 info.has_tag_capture, 12796 merges, 12797 ); 12798 } 12799 } 12800 12801 extra_index += info.body_len; 12802 } 12803 } 12804 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special.body, cond_operand); 12805 if (empty_enum) { 12806 return .void_value; 12807 } 12808 12809 return spa.resolveProngComptime( 12810 child_block, 12811 .special, 12812 special.body, 12813 special.capture, 12814 child_block.src(.{ .switch_capture = .{ 12815 .switch_node_offset = switch_node_offset, 12816 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, 12817 } }), 12818 undefined, // case_vals may be undefined for special prongs 12819 if (special.is_inline) cond_operand else .none, 12820 special.has_tag_capture, 12821 merges, 12822 ); 12823 } 12824 12825 const RangeSetUnhandledIterator = struct { 12826 pt: Zcu.PerThread, 12827 cur: ?InternPool.Index, 12828 max: InternPool.Index, 12829 range_i: usize, 12830 ranges: []const RangeSet.Range, 12831 limbs: []math.big.Limb, 12832 12833 const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128); 12834 12835 fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator { 12836 const pt = sema.pt; 12837 const int_type = pt.zcu.intern_pool.indexToKey(ty.toIntern()).int_type; 12838 const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits); 12839 return .{ 12840 .pt = pt, 12841 .cur = (try ty.minInt(pt, ty)).toIntern(), 12842 .max = (try ty.maxInt(pt, ty)).toIntern(), 12843 .range_i = 0, 12844 .ranges = range_set.ranges.items, 12845 .limbs = if (needed_limbs > preallocated_limbs) 12846 try sema.arena.alloc(math.big.Limb, needed_limbs) 12847 else 12848 &.{}, 12849 }; 12850 } 12851 12852 fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index { 12853 if (val == it.max) return null; 12854 const int = it.pt.zcu.intern_pool.indexToKey(val).int; 12855 12856 switch (int.storage) { 12857 inline .u64, .i64 => |val_int| { 12858 const next_int = @addWithOverflow(val_int, 1); 12859 if (next_int[1] == 0) 12860 return (try it.pt.intValue(.fromInterned(int.ty), next_int[0])).toIntern(); 12861 }, 12862 .big_int => {}, 12863 .lazy_align, .lazy_size => unreachable, 12864 } 12865 12866 var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined; 12867 const val_bigint = int.storage.toBigInt(&val_space); 12868 12869 var result_limbs: [preallocated_limbs]math.big.Limb = undefined; 12870 var result_bigint = math.big.int.Mutable.init( 12871 if (it.limbs.len > 0) it.limbs else &result_limbs, 12872 0, 12873 ); 12874 12875 result_bigint.addScalar(val_bigint, 1); 12876 return (try it.pt.intValue_big(.fromInterned(int.ty), result_bigint.toConst())).toIntern(); 12877 } 12878 12879 fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index { 12880 var cur = it.cur orelse return null; 12881 while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) { 12882 defer it.range_i += 1; 12883 cur = (try it.addOne(it.ranges[it.range_i].last)) orelse { 12884 it.cur = null; 12885 return null; 12886 }; 12887 } 12888 it.cur = try it.addOne(cur); 12889 return cur; 12890 } 12891 }; 12892 12893 const ResolvedSwitchItem = struct { 12894 ref: Air.Inst.Ref, 12895 val: InternPool.Index, 12896 }; 12897 fn resolveSwitchItemVal( 12898 sema: *Sema, 12899 block: *Block, 12900 item_ref: Zir.Inst.Ref, 12901 /// Coerce `item_ref` to this type. 12902 coerce_ty: Type, 12903 item_src: LazySrcLoc, 12904 ) CompileError!ResolvedSwitchItem { 12905 const uncoerced_item = try sema.resolveInst(item_ref); 12906 12907 // Constructing a LazySrcLoc is costly because we only have the switch AST node. 12908 // Only if we know for sure we need to report a compile error do we resolve the 12909 // full source locations. 12910 12911 const item = try sema.coerce(block, coerce_ty, uncoerced_item, item_src); 12912 12913 const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item, .{ .simple = .switch_item }); 12914 12915 const val = try sema.resolveLazyValue(maybe_lazy); 12916 const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: { 12917 break :blk Air.internedToRef(val.toIntern()); 12918 } else item; 12919 12920 return .{ .ref = new_item, .val = val.toIntern() }; 12921 } 12922 12923 fn validateErrSetSwitch( 12924 sema: *Sema, 12925 block: *Block, 12926 seen_errors: *SwitchErrorSet, 12927 case_vals: *std.ArrayListUnmanaged(Air.Inst.Ref), 12928 operand_ty: Type, 12929 inst_data: @FieldType(Zir.Inst.Data, "pl_node"), 12930 scalar_cases_len: u32, 12931 multi_cases_len: u32, 12932 else_case: struct { body: []const Zir.Inst.Index, end: usize, src: LazySrcLoc }, 12933 has_else: bool, 12934 ) CompileError!?Type { 12935 const gpa = sema.gpa; 12936 const pt = sema.pt; 12937 const zcu = pt.zcu; 12938 const ip = &zcu.intern_pool; 12939 12940 const src_node_offset = inst_data.src_node; 12941 const src = block.nodeOffset(src_node_offset); 12942 12943 var extra_index: usize = else_case.end; 12944 { 12945 var scalar_i: u32 = 0; 12946 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 12947 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 12948 extra_index += 1; 12949 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12950 extra_index += 1 + info.body_len; 12951 12952 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( 12953 block, 12954 seen_errors, 12955 item_ref, 12956 operand_ty, 12957 block.src(.{ .switch_case_item = .{ 12958 .switch_node_offset = src_node_offset, 12959 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) }, 12960 .item_idx = .{ .kind = .single, .index = 0 }, 12961 } }), 12962 )); 12963 } 12964 } 12965 { 12966 var multi_i: u32 = 0; 12967 while (multi_i < multi_cases_len) : (multi_i += 1) { 12968 const items_len = sema.code.extra[extra_index]; 12969 extra_index += 1; 12970 const ranges_len = sema.code.extra[extra_index]; 12971 extra_index += 1; 12972 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12973 extra_index += 1; 12974 const items = sema.code.refSlice(extra_index, items_len); 12975 extra_index += items_len + info.body_len; 12976 12977 try case_vals.ensureUnusedCapacity(gpa, items.len); 12978 for (items, 0..) |item_ref, item_i| { 12979 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( 12980 block, 12981 seen_errors, 12982 item_ref, 12983 operand_ty, 12984 block.src(.{ .switch_case_item = .{ 12985 .switch_node_offset = src_node_offset, 12986 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) }, 12987 .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, 12988 } }), 12989 )); 12990 } 12991 12992 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 12993 } 12994 } 12995 12996 switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) { 12997 .anyerror_type => { 12998 if (!has_else) { 12999 return sema.fail( 13000 block, 13001 src, 13002 "else prong required when switching on type 'anyerror'", 13003 .{}, 13004 ); 13005 } 13006 return .anyerror; 13007 }, 13008 else => |err_set_ty_index| else_validation: { 13009 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names; 13010 var maybe_msg: ?*Zcu.ErrorMsg = null; 13011 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); 13012 13013 for (error_names.get(ip)) |error_name| { 13014 if (!seen_errors.contains(error_name) and !has_else) { 13015 const msg = maybe_msg orelse blk: { 13016 maybe_msg = try sema.errMsg( 13017 src, 13018 "switch must handle all possibilities", 13019 .{}, 13020 ); 13021 break :blk maybe_msg.?; 13022 }; 13023 13024 try sema.errNote( 13025 src, 13026 msg, 13027 "unhandled error value: 'error.{f}'", 13028 .{error_name.fmt(ip)}, 13029 ); 13030 } 13031 } 13032 13033 if (maybe_msg) |msg| { 13034 maybe_msg = null; 13035 try sema.addDeclaredHereNote(msg, operand_ty); 13036 return sema.failWithOwnedErrorMsg(block, msg); 13037 } 13038 13039 if (has_else and seen_errors.count() == error_names.len) { 13040 // In order to enable common patterns for generic code allow simple else bodies 13041 // else => unreachable, 13042 // else => return, 13043 // else => |e| return e, 13044 // even if all the possible errors were already handled. 13045 const tags = sema.code.instructions.items(.tag); 13046 const datas = sema.code.instructions.items(.data); 13047 for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) { 13048 .dbg_stmt, 13049 .dbg_var_val, 13050 .ret_type, 13051 .as_node, 13052 .ret_node, 13053 .@"unreachable", 13054 .@"defer", 13055 .defer_err_code, 13056 .err_union_code, 13057 .ret_err_value_code, 13058 .save_err_ret_index, 13059 .restore_err_ret_index_unconditional, 13060 .restore_err_ret_index_fn_entry, 13061 .is_non_err, 13062 .ret_is_non_err, 13063 .condbr, 13064 => {}, 13065 .extended => switch (datas[@intFromEnum(else_inst)].extended.opcode) { 13066 .restore_err_ret_index => {}, 13067 else => break, 13068 }, 13069 else => break, 13070 } else break :else_validation; 13071 13072 return sema.fail( 13073 block, 13074 else_case.src, 13075 "unreachable else prong; all cases already handled", 13076 .{}, 13077 ); 13078 } 13079 13080 var names: InferredErrorSet.NameMap = .{}; 13081 try names.ensureUnusedCapacity(sema.arena, error_names.len); 13082 for (error_names.get(ip)) |error_name| { 13083 if (seen_errors.contains(error_name)) continue; 13084 13085 names.putAssumeCapacityNoClobber(error_name, {}); 13086 } 13087 // No need to keep the hash map metadata correct; here we 13088 // extract the (sorted) keys only. 13089 return try pt.errorSetFromUnsortedNames(names.keys()); 13090 }, 13091 } 13092 return null; 13093 } 13094 13095 fn validateSwitchRange( 13096 sema: *Sema, 13097 block: *Block, 13098 range_set: *RangeSet, 13099 first_ref: Zir.Inst.Ref, 13100 last_ref: Zir.Inst.Ref, 13101 operand_ty: Type, 13102 item_src: LazySrcLoc, 13103 ) CompileError![2]Air.Inst.Ref { 13104 const first_src: LazySrcLoc = .{ 13105 .base_node_inst = item_src.base_node_inst, 13106 .offset = .{ .switch_case_item_range_first = item_src.offset.switch_case_item }, 13107 }; 13108 const last_src: LazySrcLoc = .{ 13109 .base_node_inst = item_src.base_node_inst, 13110 .offset = .{ .switch_case_item_range_last = item_src.offset.switch_case_item }, 13111 }; 13112 const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, first_src); 13113 const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, last_src); 13114 if (try Value.fromInterned(first.val).compareAll(.gt, Value.fromInterned(last.val), operand_ty, sema.pt)) { 13115 return sema.fail(block, item_src, "range start value is greater than the end value", .{}); 13116 } 13117 const maybe_prev_src = try range_set.add(first.val, last.val, item_src); 13118 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13119 return .{ first.ref, last.ref }; 13120 } 13121 13122 fn validateSwitchItemInt( 13123 sema: *Sema, 13124 block: *Block, 13125 range_set: *RangeSet, 13126 item_ref: Zir.Inst.Ref, 13127 operand_ty: Type, 13128 item_src: LazySrcLoc, 13129 ) CompileError!Air.Inst.Ref { 13130 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); 13131 const maybe_prev_src = try range_set.add(item.val, item.val, item_src); 13132 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13133 return item.ref; 13134 } 13135 13136 fn validateSwitchItemEnum( 13137 sema: *Sema, 13138 block: *Block, 13139 seen_fields: []?LazySrcLoc, 13140 range_set: *RangeSet, 13141 item_ref: Zir.Inst.Ref, 13142 operand_ty: Type, 13143 item_src: LazySrcLoc, 13144 ) CompileError!Air.Inst.Ref { 13145 const ip = &sema.pt.zcu.intern_pool; 13146 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); 13147 const int = ip.indexToKey(item.val).enum_tag.int; 13148 const field_index = ip.loadEnumType(ip.typeOf(item.val)).tagValueIndex(ip, int) orelse { 13149 const maybe_prev_src = try range_set.add(int, int, item_src); 13150 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13151 return item.ref; 13152 }; 13153 const maybe_prev_src = seen_fields[field_index]; 13154 seen_fields[field_index] = item_src; 13155 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13156 return item.ref; 13157 } 13158 13159 fn validateSwitchItemError( 13160 sema: *Sema, 13161 block: *Block, 13162 seen_errors: *SwitchErrorSet, 13163 item_ref: Zir.Inst.Ref, 13164 operand_ty: Type, 13165 item_src: LazySrcLoc, 13166 ) CompileError!Air.Inst.Ref { 13167 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); 13168 const error_name = sema.pt.zcu.intern_pool.indexToKey(item.val).err.name; 13169 const maybe_prev_src = if (try seen_errors.fetchPut(error_name, item_src)) |prev| 13170 prev.value 13171 else 13172 null; 13173 try sema.validateSwitchDupe(block, maybe_prev_src, item_src); 13174 return item.ref; 13175 } 13176 13177 fn validateSwitchDupe( 13178 sema: *Sema, 13179 block: *Block, 13180 maybe_prev_src: ?LazySrcLoc, 13181 item_src: LazySrcLoc, 13182 ) CompileError!void { 13183 const prev_item_src = maybe_prev_src orelse return; 13184 return sema.failWithOwnedErrorMsg(block, msg: { 13185 const msg = try sema.errMsg( 13186 item_src, 13187 "duplicate switch value", 13188 .{}, 13189 ); 13190 errdefer msg.destroy(sema.gpa); 13191 try sema.errNote( 13192 prev_item_src, 13193 msg, 13194 "previous value here", 13195 .{}, 13196 ); 13197 break :msg msg; 13198 }); 13199 } 13200 13201 fn validateSwitchItemBool( 13202 sema: *Sema, 13203 block: *Block, 13204 true_count: *u8, 13205 false_count: *u8, 13206 item_ref: Zir.Inst.Ref, 13207 item_src: LazySrcLoc, 13208 ) CompileError!Air.Inst.Ref { 13209 const item = try sema.resolveSwitchItemVal(block, item_ref, .bool, item_src); 13210 if (Value.fromInterned(item.val).toBool()) { 13211 true_count.* += 1; 13212 } else { 13213 false_count.* += 1; 13214 } 13215 if (true_count.* > 1 or false_count.* > 1) { 13216 return sema.fail(block, item_src, "duplicate switch value", .{}); 13217 } 13218 return item.ref; 13219 } 13220 13221 const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc); 13222 13223 fn validateSwitchItemSparse( 13224 sema: *Sema, 13225 block: *Block, 13226 seen_values: *ValueSrcMap, 13227 item_ref: Zir.Inst.Ref, 13228 operand_ty: Type, 13229 item_src: LazySrcLoc, 13230 ) CompileError!Air.Inst.Ref { 13231 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src); 13232 const kv = try seen_values.fetchPut(sema.gpa, item.val, item_src) orelse return item.ref; 13233 try sema.validateSwitchDupe(block, kv.value, item_src); 13234 unreachable; 13235 } 13236 13237 fn validateSwitchNoRange( 13238 sema: *Sema, 13239 block: *Block, 13240 ranges_len: u32, 13241 operand_ty: Type, 13242 src_node_offset: std.zig.Ast.Node.Offset, 13243 ) CompileError!void { 13244 if (ranges_len == 0) 13245 return; 13246 13247 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); 13248 const range_src = block.src(.{ .node_offset_switch_range = src_node_offset }); 13249 13250 const msg = msg: { 13251 const msg = try sema.errMsg( 13252 operand_src, 13253 "ranges not allowed when switching on type '{f}'", 13254 .{operand_ty.fmt(sema.pt)}, 13255 ); 13256 errdefer msg.destroy(sema.gpa); 13257 try sema.errNote( 13258 range_src, 13259 msg, 13260 "range here", 13261 .{}, 13262 ); 13263 break :msg msg; 13264 }; 13265 return sema.failWithOwnedErrorMsg(block, msg); 13266 } 13267 13268 fn maybeErrorUnwrap( 13269 sema: *Sema, 13270 block: *Block, 13271 body: []const Zir.Inst.Index, 13272 operand: Air.Inst.Ref, 13273 operand_src: LazySrcLoc, 13274 allow_err_code_inst: bool, 13275 ) !bool { 13276 const pt = sema.pt; 13277 const zcu = pt.zcu; 13278 13279 const tags = sema.code.instructions.items(.tag); 13280 for (body) |inst| { 13281 switch (tags[@intFromEnum(inst)]) { 13282 .@"unreachable" => if (!block.wantSafety()) return false, 13283 .err_union_code => if (!allow_err_code_inst) return false, 13284 .save_err_ret_index, 13285 .dbg_stmt, 13286 .str, 13287 .as_node, 13288 .panic, 13289 .field_val, 13290 => {}, 13291 else => return false, 13292 } 13293 } 13294 13295 for (body) |inst| { 13296 const air_inst = switch (tags[@intFromEnum(inst)]) { 13297 .err_union_code => continue, 13298 .dbg_stmt => { 13299 try sema.zirDbgStmt(block, inst); 13300 continue; 13301 }, 13302 .save_err_ret_index => { 13303 try sema.zirSaveErrRetIndex(block, inst); 13304 continue; 13305 }, 13306 .str => try sema.zirStr(inst), 13307 .as_node => try sema.zirAsNode(block, inst), 13308 .field_val => try sema.zirFieldVal(block, inst), 13309 .@"unreachable" => { 13310 try safetyPanicUnwrapError(sema, block, operand_src, operand); 13311 return true; 13312 }, 13313 .panic => { 13314 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 13315 const msg_inst = try sema.resolveInst(inst_data.operand); 13316 13317 const panic_fn = try getBuiltin(sema, operand_src, .@"panic.call"); 13318 const args: [2]Air.Inst.Ref = .{ msg_inst, .null_value }; 13319 try sema.callBuiltin(block, operand_src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check"); 13320 return true; 13321 }, 13322 else => unreachable, 13323 }; 13324 if (sema.typeOf(air_inst).isNoReturn(zcu)) 13325 return true; 13326 sema.inst_map.putAssumeCapacity(inst, air_inst); 13327 } 13328 unreachable; 13329 } 13330 13331 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void { 13332 const pt = sema.pt; 13333 const zcu = pt.zcu; 13334 const index = cond.toIndex() orelse return; 13335 if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) return; 13336 13337 const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node; 13338 const err_operand = try sema.resolveInst(err_inst_data.operand); 13339 const operand_ty = sema.typeOf(err_operand); 13340 if (operand_ty.zigTypeTag(zcu) == .error_set) { 13341 try sema.maybeErrorUnwrapComptime(block, body, err_operand); 13342 return; 13343 } 13344 if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| { 13345 if (!operand_ty.isError(zcu)) return; 13346 if (val.getErrorName(zcu) == .none) return; 13347 try sema.maybeErrorUnwrapComptime(block, body, err_operand); 13348 } 13349 } 13350 13351 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void { 13352 const tags = sema.code.instructions.items(.tag); 13353 const inst = for (body) |inst| { 13354 switch (tags[@intFromEnum(inst)]) { 13355 .dbg_stmt, 13356 .save_err_ret_index, 13357 => {}, 13358 .@"unreachable" => break inst, 13359 else => return, 13360 } 13361 } else return; 13362 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable"; 13363 const src = block.nodeOffset(inst_data.src_node); 13364 13365 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 13366 if (val.getErrorName(sema.pt.zcu).unwrap()) |name| { 13367 return sema.failWithComptimeErrorRetTrace(block, src, name); 13368 } 13369 } 13370 } 13371 13372 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13373 const pt = sema.pt; 13374 const zcu = pt.zcu; 13375 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13376 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13377 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 13378 const name_src = block.builtinCallArgSrc(inst_data.src_node, 1); 13379 const ty = try sema.resolveType(block, ty_src, extra.lhs); 13380 const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name }); 13381 try ty.resolveFields(pt); 13382 const ip = &zcu.intern_pool; 13383 13384 const has_field = hf: { 13385 switch (ip.indexToKey(ty.toIntern())) { 13386 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 13387 .slice => { 13388 if (field_name.eqlSlice("ptr", ip)) break :hf true; 13389 if (field_name.eqlSlice("len", ip)) break :hf true; 13390 break :hf false; 13391 }, 13392 else => {}, 13393 }, 13394 .tuple_type => |tuple| { 13395 const field_index = field_name.toUnsigned(ip) orelse break :hf false; 13396 break :hf field_index < tuple.types.len; 13397 }, 13398 .struct_type => { 13399 break :hf ip.loadStructType(ty.toIntern()).nameIndex(ip, field_name) != null; 13400 }, 13401 .union_type => { 13402 const union_type = ip.loadUnionType(ty.toIntern()); 13403 break :hf union_type.loadTagType(ip).nameIndex(ip, field_name) != null; 13404 }, 13405 .enum_type => { 13406 break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null; 13407 }, 13408 .array_type => break :hf field_name.eqlSlice("len", ip), 13409 else => {}, 13410 } 13411 return sema.fail(block, ty_src, "type '{f}' does not support '@hasField'", .{ 13412 ty.fmt(pt), 13413 }); 13414 }; 13415 return if (has_field) .bool_true else .bool_false; 13416 } 13417 13418 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13419 const pt = sema.pt; 13420 const zcu = pt.zcu; 13421 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13422 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13423 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 13424 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 13425 const container_type = try sema.resolveType(block, lhs_src, extra.lhs); 13426 const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{ .simple = .decl_name }); 13427 13428 try sema.checkNamespaceType(block, lhs_src, container_type); 13429 13430 const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false; 13431 if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| { 13432 if (lookup.accessible) { 13433 return .bool_true; 13434 } 13435 } 13436 return .bool_false; 13437 } 13438 13439 fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13440 const tracy = trace(@src()); 13441 defer tracy.end(); 13442 13443 const pt = sema.pt; 13444 const zcu = pt.zcu; 13445 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; 13446 const extra = sema.code.extraData(Zir.Inst.Import, inst_data.payload_index).data; 13447 const operand_src = block.tokenOffset(inst_data.src_tok); 13448 const operand = sema.code.nullTerminatedString(extra.path); 13449 13450 const result = pt.doImport(block.getFileScope(zcu), operand) catch |err| switch (err) { 13451 error.ModuleNotFound => return sema.fail(block, operand_src, "no module named '{s}' available within module '{s}'", .{ 13452 operand, block.getFileScope(zcu).mod.?.fully_qualified_name, 13453 }), 13454 error.IllegalZigImport => unreachable, // caught before semantic analysis 13455 error.OutOfMemory => |e| return e, 13456 }; 13457 const file_index = result.file; 13458 const file = zcu.fileByIndex(file_index); 13459 switch (file.getMode()) { 13460 .zig => { 13461 try pt.ensureFileAnalyzed(file_index); 13462 const ty = zcu.fileRootType(file_index); 13463 try sema.declareDependency(.{ .interned = ty }); 13464 try sema.addTypeReferenceEntry(operand_src, ty); 13465 return Air.internedToRef(ty); 13466 }, 13467 .zon => { 13468 const res_ty: InternPool.Index = b: { 13469 if (extra.res_ty == .none) break :b .none; 13470 const res_ty_inst = try sema.resolveInst(extra.res_ty); 13471 const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst); 13472 if (res_ty.isGenericPoison()) break :b .none; 13473 break :b res_ty.toIntern(); 13474 }; 13475 13476 try sema.declareDependency(.{ .zon_file = file_index }); 13477 const interned = try LowerZon.run( 13478 sema, 13479 file, 13480 file_index, 13481 res_ty, 13482 operand_src, 13483 block, 13484 ); 13485 return Air.internedToRef(interned); 13486 }, 13487 } 13488 } 13489 13490 fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13491 const tracy = trace(@src()); 13492 defer tracy.end(); 13493 13494 const pt = sema.pt; 13495 const zcu = pt.zcu; 13496 13497 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 13498 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 13499 const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .operand_embedFile }); 13500 13501 if (name.len == 0) { 13502 return sema.fail(block, operand_src, "file path name cannot be empty", .{}); 13503 } 13504 13505 const ef_idx = pt.embedFile(block.getFileScope(zcu), name) catch |err| switch (err) { 13506 error.ImportOutsideModulePath => { 13507 return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name}); 13508 }, 13509 error.CurrentWorkingDirectoryUnlinked => { 13510 // TODO: this should be some kind of retryable failure, in case the cwd is put back 13511 return sema.fail(block, operand_src, "unable to resolve '{s}': working directory has been unlinked", .{name}); 13512 }, 13513 error.OutOfMemory => |e| return e, 13514 }; 13515 try sema.declareDependency(.{ .embed_file = ef_idx }); 13516 13517 const result = ef_idx.get(zcu); 13518 if (result.val == .none) { 13519 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(result.err.?) }); 13520 } 13521 13522 return Air.internedToRef(result.val); 13523 } 13524 13525 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13526 const pt = sema.pt; 13527 const zcu = pt.zcu; 13528 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 13529 const name = try zcu.intern_pool.getOrPutString( 13530 sema.gpa, 13531 pt.tid, 13532 inst_data.get(sema.code), 13533 .no_embedded_nulls, 13534 ); 13535 _ = try pt.getErrorValue(name); 13536 const error_set_type = try pt.singleErrorSetType(name); 13537 return Air.internedToRef((try pt.intern(.{ .err = .{ 13538 .ty = error_set_type.toIntern(), 13539 .name = name, 13540 } }))); 13541 } 13542 13543 fn zirShl( 13544 sema: *Sema, 13545 block: *Block, 13546 inst: Zir.Inst.Index, 13547 air_tag: Air.Inst.Tag, 13548 ) CompileError!Air.Inst.Ref { 13549 const tracy = trace(@src()); 13550 defer tracy.end(); 13551 13552 const pt = sema.pt; 13553 const zcu = pt.zcu; 13554 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13555 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13556 const lhs = try sema.resolveInst(extra.lhs); 13557 const rhs = try sema.resolveInst(extra.rhs); 13558 const lhs_ty = sema.typeOf(lhs); 13559 const rhs_ty = sema.typeOf(rhs); 13560 13561 const src = block.nodeOffset(inst_data.src_node); 13562 const lhs_src, const rhs_src = switch (air_tag) { 13563 .shl, .shl_sat => .{ 13564 block.src(.{ .node_offset_bin_lhs = inst_data.src_node }), 13565 block.src(.{ .node_offset_bin_rhs = inst_data.src_node }), 13566 }, 13567 .shl_exact => .{ 13568 block.builtinCallArgSrc(inst_data.src_node, 0), 13569 block.builtinCallArgSrc(inst_data.src_node, 1), 13570 }, 13571 else => unreachable, 13572 }; 13573 13574 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13575 13576 const scalar_ty = lhs_ty.scalarType(zcu); 13577 const scalar_rhs_ty = rhs_ty.scalarType(zcu); 13578 13579 _ = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); 13580 13581 const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs); 13582 const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs); 13583 13584 if (maybe_rhs_val) |rhs_val| { 13585 if (rhs_val.isUndef(zcu)) { 13586 return pt.undefRef(sema.typeOf(lhs)); 13587 } 13588 // If rhs is 0, return lhs without doing any calculations. 13589 if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { 13590 return lhs; 13591 } 13592 if (air_tag != .shl_sat and scalar_ty.zigTypeTag(zcu) != .comptime_int) { 13593 const bit_value = try pt.intValue(.comptime_int, scalar_ty.intInfo(zcu).bits); 13594 if (rhs_ty.zigTypeTag(zcu) == .vector) { 13595 var i: usize = 0; 13596 while (i < rhs_ty.vectorLen(zcu)) : (i += 1) { 13597 const rhs_elem = try rhs_val.elemValue(pt, i); 13598 if (rhs_elem.compareHetero(.gte, bit_value, zcu)) { 13599 return sema.fail(block, rhs_src, "shift amount '{f}' at index '{d}' is too large for operand type '{f}'", .{ 13600 rhs_elem.fmtValueSema(pt, sema), 13601 i, 13602 scalar_ty.fmt(pt), 13603 }); 13604 } 13605 } 13606 } else if (rhs_val.compareHetero(.gte, bit_value, zcu)) { 13607 return sema.fail(block, rhs_src, "shift amount '{f}' is too large for operand type '{f}'", .{ 13608 rhs_val.fmtValueSema(pt, sema), 13609 scalar_ty.fmt(pt), 13610 }); 13611 } 13612 } 13613 if (rhs_ty.zigTypeTag(zcu) == .vector) { 13614 var i: usize = 0; 13615 while (i < rhs_ty.vectorLen(zcu)) : (i += 1) { 13616 const rhs_elem = try rhs_val.elemValue(pt, i); 13617 if (rhs_elem.compareHetero(.lt, try pt.intValue(scalar_rhs_ty, 0), zcu)) { 13618 return sema.fail(block, rhs_src, "shift by negative amount '{f}' at index '{d}'", .{ 13619 rhs_elem.fmtValueSema(pt, sema), 13620 i, 13621 }); 13622 } 13623 } 13624 } else if (rhs_val.compareHetero(.lt, try pt.intValue(rhs_ty, 0), zcu)) { 13625 return sema.fail(block, rhs_src, "shift by negative amount '{f}'", .{ 13626 rhs_val.fmtValueSema(pt, sema), 13627 }); 13628 } 13629 } else if (scalar_rhs_ty.isSignedInt(zcu)) { 13630 return sema.fail(block, rhs_src, "shift by signed type '{f}'", .{rhs_ty.fmt(pt)}); 13631 } 13632 13633 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { 13634 if (lhs_val.isUndef(zcu)) return pt.undefRef(lhs_ty); 13635 const rhs_val = maybe_rhs_val orelse { 13636 if (scalar_ty.zigTypeTag(zcu) == .comptime_int) { 13637 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 13638 } 13639 break :rs rhs_src; 13640 }; 13641 const val = if (scalar_ty.zigTypeTag(zcu) == .comptime_int) 13642 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, pt) 13643 else switch (air_tag) { 13644 .shl_exact => val: { 13645 const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, pt); 13646 if (shifted.overflow_bit.compareAllWithZero(.eq, zcu)) { 13647 break :val shifted.wrapped_result; 13648 } 13649 return sema.fail(block, src, "operation caused overflow", .{}); 13650 }, 13651 .shl_sat => try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, pt), 13652 .shl => try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, pt), 13653 else => unreachable, 13654 }; 13655 return Air.internedToRef(val.toIntern()); 13656 } else lhs_src; 13657 13658 const rt_rhs = switch (air_tag) { 13659 else => unreachable, 13660 .shl, .shl_exact => rhs, 13661 // The backend can handle a large runtime rhs better than we can, but 13662 // we can limit a large comptime rhs better here. This also has the 13663 // necessary side effect of preventing rhs from being a `comptime_int`. 13664 .shl_sat => if (maybe_rhs_val) |rhs_val| Air.internedToRef(rt_rhs: { 13665 const bit_count = scalar_ty.intInfo(zcu).bits; 13666 const rt_rhs_scalar_ty = try pt.smallestUnsignedInt(bit_count); 13667 if (!rhs_ty.isVector(zcu)) break :rt_rhs (try pt.intValue( 13668 rt_rhs_scalar_ty, 13669 @min(try rhs_val.getUnsignedIntSema(pt) orelse bit_count, bit_count), 13670 )).toIntern(); 13671 const rhs_len = rhs_ty.vectorLen(zcu); 13672 const rhs_elems = try sema.arena.alloc(InternPool.Index, rhs_len); 13673 for (rhs_elems, 0..) |*rhs_elem, i| rhs_elem.* = (try pt.intValue( 13674 rt_rhs_scalar_ty, 13675 @min(try (try rhs_val.elemValue(pt, i)).getUnsignedIntSema(pt) orelse bit_count, bit_count), 13676 )).toIntern(); 13677 break :rt_rhs try pt.intern(.{ .aggregate = .{ 13678 .ty = (try pt.vectorType(.{ 13679 .len = rhs_len, 13680 .child = rt_rhs_scalar_ty.toIntern(), 13681 })).toIntern(), 13682 .storage = .{ .elems = rhs_elems }, 13683 } }); 13684 }) else rhs, 13685 }; 13686 13687 try sema.requireRuntimeBlock(block, src, runtime_src); 13688 if (block.wantSafety()) { 13689 const bit_count = scalar_ty.intInfo(zcu).bits; 13690 if (air_tag != .shl_sat and !std.math.isPowerOfTwo(bit_count)) { 13691 const bit_count_val = try pt.intValue(scalar_rhs_ty, bit_count); 13692 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { 13693 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern()); 13694 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); 13695 break :ok try block.addReduce(lt, .And); 13696 } else ok: { 13697 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern()); 13698 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); 13699 }; 13700 try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big); 13701 } 13702 13703 if (air_tag == .shl_exact) { 13704 const op_ov_tuple_ty = try pt.overflowArithmeticTupleType(lhs_ty); 13705 const op_ov = try block.addInst(.{ 13706 .tag = .shl_with_overflow, 13707 .data = .{ .ty_pl = .{ 13708 .ty = Air.internedToRef(op_ov_tuple_ty.toIntern()), 13709 .payload = try sema.addExtra(Air.Bin{ 13710 .lhs = lhs, 13711 .rhs = rhs, 13712 }), 13713 } }, 13714 }); 13715 const ov_bit = try sema.tupleFieldValByIndex(block, op_ov, 1, op_ov_tuple_ty); 13716 const any_ov_bit = if (lhs_ty.zigTypeTag(zcu) == .vector) 13717 try block.addReduce(ov_bit, .Or) 13718 else 13719 ov_bit; 13720 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, .zero_u1); 13721 13722 try sema.addSafetyCheck(block, src, no_ov, .shl_overflow); 13723 return sema.tupleFieldValByIndex(block, op_ov, 0, op_ov_tuple_ty); 13724 } 13725 } 13726 return block.addBinOp(air_tag, lhs, rt_rhs); 13727 } 13728 13729 fn zirShr( 13730 sema: *Sema, 13731 block: *Block, 13732 inst: Zir.Inst.Index, 13733 air_tag: Air.Inst.Tag, 13734 ) CompileError!Air.Inst.Ref { 13735 const tracy = trace(@src()); 13736 defer tracy.end(); 13737 13738 const pt = sema.pt; 13739 const zcu = pt.zcu; 13740 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13741 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13742 const lhs = try sema.resolveInst(extra.lhs); 13743 const rhs = try sema.resolveInst(extra.rhs); 13744 const lhs_ty = sema.typeOf(lhs); 13745 const rhs_ty = sema.typeOf(rhs); 13746 13747 const src = block.nodeOffset(inst_data.src_node); 13748 const lhs_src = switch (air_tag) { 13749 .shr => block.src(.{ .node_offset_bin_lhs = inst_data.src_node }), 13750 .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 0), 13751 else => unreachable, 13752 }; 13753 const rhs_src = switch (air_tag) { 13754 .shr => block.src(.{ .node_offset_bin_rhs = inst_data.src_node }), 13755 .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 1), 13756 else => unreachable, 13757 }; 13758 13759 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13760 const scalar_ty = lhs_ty.scalarType(zcu); 13761 13762 const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs); 13763 const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs); 13764 13765 const runtime_src = if (maybe_rhs_val) |rhs_val| rs: { 13766 if (rhs_val.isUndef(zcu)) { 13767 return pt.undefRef(lhs_ty); 13768 } 13769 // If rhs is 0, return lhs without doing any calculations. 13770 if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { 13771 return lhs; 13772 } 13773 if (scalar_ty.zigTypeTag(zcu) != .comptime_int) { 13774 const bit_value = try pt.intValue(.comptime_int, scalar_ty.intInfo(zcu).bits); 13775 if (rhs_ty.zigTypeTag(zcu) == .vector) { 13776 var i: usize = 0; 13777 while (i < rhs_ty.vectorLen(zcu)) : (i += 1) { 13778 const rhs_elem = try rhs_val.elemValue(pt, i); 13779 if (rhs_elem.compareHetero(.gte, bit_value, zcu)) { 13780 return sema.fail(block, rhs_src, "shift amount '{f}' at index '{d}' is too large for operand type '{f}'", .{ 13781 rhs_elem.fmtValueSema(pt, sema), 13782 i, 13783 scalar_ty.fmt(pt), 13784 }); 13785 } 13786 } 13787 } else if (rhs_val.compareHetero(.gte, bit_value, zcu)) { 13788 return sema.fail(block, rhs_src, "shift amount '{f}' is too large for operand type '{f}'", .{ 13789 rhs_val.fmtValueSema(pt, sema), 13790 scalar_ty.fmt(pt), 13791 }); 13792 } 13793 } 13794 if (rhs_ty.zigTypeTag(zcu) == .vector) { 13795 var i: usize = 0; 13796 while (i < rhs_ty.vectorLen(zcu)) : (i += 1) { 13797 const rhs_elem = try rhs_val.elemValue(pt, i); 13798 if (rhs_elem.compareHetero(.lt, try pt.intValue(rhs_ty.childType(zcu), 0), zcu)) { 13799 return sema.fail(block, rhs_src, "shift by negative amount '{f}' at index '{d}'", .{ 13800 rhs_elem.fmtValueSema(pt, sema), 13801 i, 13802 }); 13803 } 13804 } 13805 } else if (rhs_val.compareHetero(.lt, try pt.intValue(rhs_ty, 0), zcu)) { 13806 return sema.fail(block, rhs_src, "shift by negative amount '{f}'", .{ 13807 rhs_val.fmtValueSema(pt, sema), 13808 }); 13809 } 13810 if (maybe_lhs_val) |lhs_val| { 13811 if (lhs_val.isUndef(zcu)) { 13812 return pt.undefRef(lhs_ty); 13813 } 13814 if (air_tag == .shr_exact) { 13815 // Detect if any ones would be shifted out. 13816 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, pt); 13817 if (!(try truncated.compareAllWithZeroSema(.eq, pt))) { 13818 return sema.fail(block, src, "exact shift shifted out 1 bits", .{}); 13819 } 13820 } 13821 const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, pt); 13822 return Air.internedToRef(val.toIntern()); 13823 } else { 13824 break :rs lhs_src; 13825 } 13826 } else rhs_src; 13827 13828 if (maybe_rhs_val == null and scalar_ty.zigTypeTag(zcu) == .comptime_int) { 13829 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 13830 } 13831 13832 try sema.requireRuntimeBlock(block, src, runtime_src); 13833 const result = try block.addBinOp(air_tag, lhs, rhs); 13834 if (block.wantSafety()) { 13835 const bit_count = scalar_ty.intInfo(zcu).bits; 13836 if (!std.math.isPowerOfTwo(bit_count)) { 13837 const bit_count_val = try pt.intValue(rhs_ty.scalarType(zcu), bit_count); 13838 13839 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { 13840 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern()); 13841 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); 13842 break :ok try block.addReduce(lt, .And); 13843 } else ok: { 13844 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern()); 13845 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); 13846 }; 13847 try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big); 13848 } 13849 13850 if (air_tag == .shr_exact) { 13851 const back = try block.addBinOp(.shl, result, rhs); 13852 13853 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { 13854 const eql = try block.addCmpVector(lhs, back, .eq); 13855 break :ok try block.addReduce(eql, .And); 13856 } else try block.addBinOp(.cmp_eq, lhs, back); 13857 try sema.addSafetyCheck(block, src, ok, .shr_overflow); 13858 } 13859 } 13860 return result; 13861 } 13862 13863 fn zirBitwise( 13864 sema: *Sema, 13865 block: *Block, 13866 inst: Zir.Inst.Index, 13867 air_tag: Air.Inst.Tag, 13868 ) CompileError!Air.Inst.Ref { 13869 const tracy = trace(@src()); 13870 defer tracy.end(); 13871 13872 const pt = sema.pt; 13873 const zcu = pt.zcu; 13874 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13875 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 13876 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 13877 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 13878 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13879 const lhs = try sema.resolveInst(extra.lhs); 13880 const rhs = try sema.resolveInst(extra.rhs); 13881 const lhs_ty = sema.typeOf(lhs); 13882 const rhs_ty = sema.typeOf(rhs); 13883 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13884 13885 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 13886 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); 13887 const scalar_type = resolved_type.scalarType(zcu); 13888 const scalar_tag = scalar_type.zigTypeTag(zcu); 13889 13890 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 13891 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 13892 13893 const is_int_or_bool = scalar_tag == .int or scalar_tag == .comptime_int or scalar_tag == .bool; 13894 13895 if (!is_int_or_bool) { 13896 return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(zcu)), @tagName(rhs_ty.zigTypeTag(zcu)) }); 13897 } 13898 13899 const runtime_src = runtime: { 13900 // TODO: ask the linker what kind of relocations are available, and 13901 // in some cases emit a Value that means "this decl's address AND'd with this operand". 13902 if (try sema.resolveValueResolveLazy(casted_lhs)) |lhs_val| { 13903 if (try sema.resolveValueResolveLazy(casted_rhs)) |rhs_val| { 13904 const result_val = switch (air_tag) { 13905 .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, pt), 13906 .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, pt), 13907 .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, pt), 13908 else => unreachable, 13909 }; 13910 return Air.internedToRef(result_val.toIntern()); 13911 } else { 13912 break :runtime rhs_src; 13913 } 13914 } else { 13915 break :runtime lhs_src; 13916 } 13917 }; 13918 13919 try sema.requireRuntimeBlock(block, src, runtime_src); 13920 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 13921 } 13922 13923 fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13924 const pt = sema.pt; 13925 const zcu = pt.zcu; 13926 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 13927 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 13928 const src = block.nodeOffset(inst_data.src_node); 13929 const operand = try sema.resolveInst(inst_data.operand); 13930 const operand_ty = sema.typeOf(operand); 13931 const scalar_ty = operand_ty.scalarType(zcu); 13932 const scalar_tag = scalar_ty.zigTypeTag(zcu); 13933 13934 if (scalar_tag != .int and scalar_tag != .bool) 13935 return sema.fail(block, operand_src, "bitwise not operation on type '{f}'", .{operand_ty.fmt(pt)}); 13936 13937 return analyzeBitNot(sema, block, operand, src); 13938 } 13939 13940 fn analyzeBitNot( 13941 sema: *Sema, 13942 block: *Block, 13943 operand: Air.Inst.Ref, 13944 src: LazySrcLoc, 13945 ) CompileError!Air.Inst.Ref { 13946 const pt = sema.pt; 13947 const zcu = pt.zcu; 13948 const operand_ty = sema.typeOf(operand); 13949 const scalar_ty = operand_ty.scalarType(zcu); 13950 if (try sema.resolveValue(operand)) |val| { 13951 if (val.isUndef(zcu)) { 13952 return pt.undefRef(operand_ty); 13953 } else if (operand_ty.zigTypeTag(zcu) == .vector) { 13954 const vec_len = try sema.usizeCast(block, src, operand_ty.vectorLen(zcu)); 13955 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 13956 for (elems, 0..) |*elem, i| { 13957 const elem_val = try val.elemValue(pt, i); 13958 elem.* = (try elem_val.bitwiseNot(scalar_ty, sema.arena, pt)).toIntern(); 13959 } 13960 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 13961 .ty = operand_ty.toIntern(), 13962 .storage = .{ .elems = elems }, 13963 } }))); 13964 } else { 13965 const result_val = try val.bitwiseNot(operand_ty, sema.arena, pt); 13966 return Air.internedToRef(result_val.toIntern()); 13967 } 13968 } 13969 13970 try sema.requireRuntimeBlock(block, src, null); 13971 return block.addTyOp(.not, operand_ty, operand); 13972 } 13973 13974 fn analyzeTupleCat( 13975 sema: *Sema, 13976 block: *Block, 13977 src_node: std.zig.Ast.Node.Offset, 13978 lhs: Air.Inst.Ref, 13979 rhs: Air.Inst.Ref, 13980 ) CompileError!Air.Inst.Ref { 13981 const pt = sema.pt; 13982 const zcu = pt.zcu; 13983 const lhs_ty = sema.typeOf(lhs); 13984 const rhs_ty = sema.typeOf(rhs); 13985 const src = block.nodeOffset(src_node); 13986 13987 const lhs_len = lhs_ty.structFieldCount(zcu); 13988 const rhs_len = rhs_ty.structFieldCount(zcu); 13989 const dest_fields = lhs_len + rhs_len; 13990 13991 if (dest_fields == 0) { 13992 return .empty_tuple; 13993 } 13994 if (lhs_len == 0) { 13995 return rhs; 13996 } 13997 if (rhs_len == 0) { 13998 return lhs; 13999 } 14000 const final_len = try sema.usizeCast(block, src, dest_fields); 14001 14002 const types = try sema.arena.alloc(InternPool.Index, final_len); 14003 const values = try sema.arena.alloc(InternPool.Index, final_len); 14004 14005 const opt_runtime_src = rs: { 14006 var runtime_src: ?LazySrcLoc = null; 14007 var i: u32 = 0; 14008 while (i < lhs_len) : (i += 1) { 14009 types[i] = lhs_ty.fieldType(i, zcu).toIntern(); 14010 const default_val = lhs_ty.structFieldDefaultValue(i, zcu); 14011 values[i] = default_val.toIntern(); 14012 const operand_src = block.src(.{ .array_cat_lhs = .{ 14013 .array_cat_offset = src_node, 14014 .elem_index = i, 14015 } }); 14016 if (default_val.toIntern() == .unreachable_value) { 14017 runtime_src = operand_src; 14018 values[i] = .none; 14019 } 14020 } 14021 i = 0; 14022 while (i < rhs_len) : (i += 1) { 14023 types[i + lhs_len] = rhs_ty.fieldType(i, zcu).toIntern(); 14024 const default_val = rhs_ty.structFieldDefaultValue(i, zcu); 14025 values[i + lhs_len] = default_val.toIntern(); 14026 const operand_src = block.src(.{ .array_cat_rhs = .{ 14027 .array_cat_offset = src_node, 14028 .elem_index = i, 14029 } }); 14030 if (default_val.toIntern() == .unreachable_value) { 14031 runtime_src = operand_src; 14032 values[i + lhs_len] = .none; 14033 } 14034 } 14035 break :rs runtime_src; 14036 }; 14037 14038 const tuple_ty = try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{ 14039 .types = types, 14040 .values = values, 14041 }); 14042 14043 const runtime_src = opt_runtime_src orelse { 14044 const tuple_val = try pt.intern(.{ .aggregate = .{ 14045 .ty = tuple_ty, 14046 .storage = .{ .elems = values }, 14047 } }); 14048 return Air.internedToRef(tuple_val); 14049 }; 14050 14051 try sema.requireRuntimeBlock(block, src, runtime_src); 14052 14053 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); 14054 var i: u32 = 0; 14055 while (i < lhs_len) : (i += 1) { 14056 element_refs[i] = try sema.tupleFieldValByIndex(block, lhs, i, lhs_ty); 14057 } 14058 i = 0; 14059 while (i < rhs_len) : (i += 1) { 14060 element_refs[i + lhs_len] = 14061 try sema.tupleFieldValByIndex(block, rhs, i, rhs_ty); 14062 } 14063 14064 return block.addAggregateInit(.fromInterned(tuple_ty), element_refs); 14065 } 14066 14067 fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14068 const tracy = trace(@src()); 14069 defer tracy.end(); 14070 14071 const pt = sema.pt; 14072 const zcu = pt.zcu; 14073 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14074 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14075 const lhs = try sema.resolveInst(extra.lhs); 14076 const rhs = try sema.resolveInst(extra.rhs); 14077 const lhs_ty = sema.typeOf(lhs); 14078 const rhs_ty = sema.typeOf(rhs); 14079 const src = block.nodeOffset(inst_data.src_node); 14080 14081 const lhs_is_tuple = lhs_ty.isTuple(zcu); 14082 const rhs_is_tuple = rhs_ty.isTuple(zcu); 14083 if (lhs_is_tuple and rhs_is_tuple) { 14084 return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); 14085 } 14086 14087 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 14088 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 14089 14090 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: { 14091 if (lhs_is_tuple) break :lhs_info undefined; 14092 return sema.fail(block, lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)}); 14093 }; 14094 const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse { 14095 assert(!rhs_is_tuple); 14096 return sema.fail(block, rhs_src, "expected indexable; found '{f}'", .{rhs_ty.fmt(pt)}); 14097 }; 14098 14099 const resolved_elem_ty = t: { 14100 var trash_block = block.makeSubBlock(); 14101 trash_block.comptime_reason = null; 14102 defer trash_block.instructions.deinit(sema.gpa); 14103 14104 const instructions = [_]Air.Inst.Ref{ 14105 try trash_block.addBitCast(lhs_info.elem_type, .void_value), 14106 try trash_block.addBitCast(rhs_info.elem_type, .void_value), 14107 }; 14108 break :t try sema.resolvePeerTypes(block, src, &instructions, .{ 14109 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14110 }); 14111 }; 14112 14113 // When there is a sentinel mismatch, no sentinel on the result. 14114 // Otherwise, use the sentinel value provided by either operand, 14115 // coercing it to the peer-resolved element type. 14116 const res_sent_val: ?Value = s: { 14117 if (lhs_info.sentinel) |lhs_sent_val| { 14118 const lhs_sent = Air.internedToRef(lhs_sent_val.toIntern()); 14119 if (rhs_info.sentinel) |rhs_sent_val| { 14120 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern()); 14121 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); 14122 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); 14123 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?; 14124 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?; 14125 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { 14126 break :s lhs_sent_casted_val; 14127 } else { 14128 break :s null; 14129 } 14130 } else { 14131 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); 14132 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?; 14133 break :s lhs_sent_casted_val; 14134 } 14135 } else { 14136 if (rhs_info.sentinel) |rhs_sent_val| { 14137 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern()); 14138 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); 14139 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?; 14140 break :s rhs_sent_casted_val; 14141 } else { 14142 break :s null; 14143 } 14144 } 14145 }; 14146 14147 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); 14148 const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len); 14149 const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) { 14150 error.Overflow => return sema.fail( 14151 block, 14152 src, 14153 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle", 14154 .{ lhs_len, rhs_len }, 14155 ), 14156 }; 14157 14158 const result_ty = try pt.arrayType(.{ 14159 .len = result_len, 14160 .sentinel = if (res_sent_val) |v| v.toIntern() else .none, 14161 .child = resolved_elem_ty.toIntern(), 14162 }); 14163 const ptr_addrspace = p: { 14164 if (lhs_ty.zigTypeTag(zcu) == .pointer) break :p lhs_ty.ptrAddressSpace(zcu); 14165 if (rhs_ty.zigTypeTag(zcu) == .pointer) break :p rhs_ty.ptrAddressSpace(zcu); 14166 break :p null; 14167 }; 14168 14169 const runtime_src = if (switch (lhs_ty.zigTypeTag(zcu)) { 14170 .array, .@"struct" => try sema.resolveValue(lhs), 14171 .pointer => try sema.resolveDefinedValue(block, lhs_src, lhs), 14172 else => unreachable, 14173 }) |lhs_val| rs: { 14174 if (switch (rhs_ty.zigTypeTag(zcu)) { 14175 .array, .@"struct" => try sema.resolveValue(rhs), 14176 .pointer => try sema.resolveDefinedValue(block, rhs_src, rhs), 14177 else => unreachable, 14178 }) |rhs_val| { 14179 const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu)) 14180 try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :rs lhs_src 14181 else if (lhs_ty.isSlice(zcu)) 14182 try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :rs lhs_src 14183 else 14184 lhs_val; 14185 14186 const rhs_sub_val = if (rhs_ty.isSinglePointer(zcu)) 14187 try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty) orelse break :rs rhs_src 14188 else if (rhs_ty.isSlice(zcu)) 14189 try sema.maybeDerefSliceAsArray(block, rhs_src, rhs_val) orelse break :rs rhs_src 14190 else 14191 rhs_val; 14192 14193 const element_vals = try sema.arena.alloc(InternPool.Index, result_len); 14194 var elem_i: u32 = 0; 14195 while (elem_i < lhs_len) : (elem_i += 1) { 14196 const lhs_elem_i = elem_i; 14197 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else Value.@"unreachable"; 14198 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(pt, lhs_elem_i) else elem_default_val; 14199 const elem_val_inst = Air.internedToRef(elem_val.toIntern()); 14200 const operand_src = block.src(.{ .array_cat_lhs = .{ 14201 .array_cat_offset = inst_data.src_node, 14202 .elem_index = elem_i, 14203 } }); 14204 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src); 14205 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined); 14206 element_vals[elem_i] = coerced_elem_val.toIntern(); 14207 } 14208 while (elem_i < result_len) : (elem_i += 1) { 14209 const rhs_elem_i = elem_i - lhs_len; 14210 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else Value.@"unreachable"; 14211 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(pt, rhs_elem_i) else elem_default_val; 14212 const elem_val_inst = Air.internedToRef(elem_val.toIntern()); 14213 const operand_src = block.src(.{ .array_cat_rhs = .{ 14214 .array_cat_offset = inst_data.src_node, 14215 .elem_index = @intCast(rhs_elem_i), 14216 } }); 14217 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src); 14218 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined); 14219 element_vals[elem_i] = coerced_elem_val.toIntern(); 14220 } 14221 return sema.addConstantMaybeRef(try pt.intern(.{ .aggregate = .{ 14222 .ty = result_ty.toIntern(), 14223 .storage = .{ .elems = element_vals }, 14224 } }), ptr_addrspace != null); 14225 } else break :rs rhs_src; 14226 } else lhs_src; 14227 14228 try sema.requireRuntimeBlock(block, src, runtime_src); 14229 14230 if (ptr_addrspace) |ptr_as| { 14231 const constant_alloc_ty = try pt.ptrTypeSema(.{ 14232 .child = result_ty.toIntern(), 14233 .flags = .{ 14234 .address_space = ptr_as, 14235 .is_const = true, 14236 }, 14237 }); 14238 const alloc_ty = try pt.ptrTypeSema(.{ 14239 .child = result_ty.toIntern(), 14240 .flags = .{ .address_space = ptr_as }, 14241 }); 14242 const elem_ptr_ty = try pt.ptrTypeSema(.{ 14243 .child = resolved_elem_ty.toIntern(), 14244 .flags = .{ .address_space = ptr_as }, 14245 }); 14246 14247 const mutable_alloc = try block.addTy(.alloc, alloc_ty); 14248 14249 // if both the source and destination are arrays 14250 // we can hotpath via a memcpy. 14251 if (lhs_ty.zigTypeTag(zcu) == .pointer and 14252 rhs_ty.zigTypeTag(zcu) == .pointer) 14253 { 14254 const slice_ty = try pt.ptrTypeSema(.{ 14255 .child = resolved_elem_ty.toIntern(), 14256 .flags = .{ 14257 .size = .slice, 14258 .address_space = ptr_as, 14259 }, 14260 }); 14261 14262 const many_ty = slice_ty.slicePtrFieldType(zcu); 14263 const many_alloc = try block.addBitCast(many_ty, mutable_alloc); 14264 14265 // lhs_dest_slice = dest[0..lhs.len] 14266 const slice_ty_ref = Air.internedToRef(slice_ty.toIntern()); 14267 const lhs_len_ref = try pt.intRef(.usize, lhs_len); 14268 const lhs_dest_slice = try block.addInst(.{ 14269 .tag = .slice, 14270 .data = .{ .ty_pl = .{ 14271 .ty = slice_ty_ref, 14272 .payload = try sema.addExtra(Air.Bin{ 14273 .lhs = many_alloc, 14274 .rhs = lhs_len_ref, 14275 }), 14276 } }, 14277 }); 14278 14279 _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs); 14280 14281 // rhs_dest_slice = dest[lhs.len..][0..rhs.len] 14282 const rhs_len_ref = try pt.intRef(.usize, rhs_len); 14283 const rhs_dest_offset = try block.addInst(.{ 14284 .tag = .ptr_add, 14285 .data = .{ .ty_pl = .{ 14286 .ty = Air.internedToRef(many_ty.toIntern()), 14287 .payload = try sema.addExtra(Air.Bin{ 14288 .lhs = many_alloc, 14289 .rhs = lhs_len_ref, 14290 }), 14291 } }, 14292 }); 14293 const rhs_dest_slice = try block.addInst(.{ 14294 .tag = .slice, 14295 .data = .{ .ty_pl = .{ 14296 .ty = slice_ty_ref, 14297 .payload = try sema.addExtra(Air.Bin{ 14298 .lhs = rhs_dest_offset, 14299 .rhs = rhs_len_ref, 14300 }), 14301 } }, 14302 }); 14303 14304 _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs); 14305 14306 if (res_sent_val) |sent_val| { 14307 const elem_index = try pt.intRef(.usize, result_len); 14308 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); 14309 const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern()); 14310 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 14311 } 14312 14313 return block.addBitCast(constant_alloc_ty, mutable_alloc); 14314 } 14315 14316 var elem_i: u32 = 0; 14317 while (elem_i < lhs_len) : (elem_i += 1) { 14318 const elem_index = try pt.intRef(.usize, elem_i); 14319 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); 14320 const operand_src = block.src(.{ .array_cat_lhs = .{ 14321 .array_cat_offset = inst_data.src_node, 14322 .elem_index = elem_i, 14323 } }); 14324 const init = try sema.elemVal(block, operand_src, lhs, elem_index, src, true); 14325 try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store); 14326 } 14327 while (elem_i < result_len) : (elem_i += 1) { 14328 const rhs_elem_i = elem_i - lhs_len; 14329 const elem_index = try pt.intRef(.usize, elem_i); 14330 const rhs_index = try pt.intRef(.usize, rhs_elem_i); 14331 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); 14332 const operand_src = block.src(.{ .array_cat_rhs = .{ 14333 .array_cat_offset = inst_data.src_node, 14334 .elem_index = @intCast(rhs_elem_i), 14335 } }); 14336 const init = try sema.elemVal(block, operand_src, rhs, rhs_index, src, true); 14337 try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store); 14338 } 14339 if (res_sent_val) |sent_val| { 14340 const elem_index = try pt.intRef(.usize, result_len); 14341 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); 14342 const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern()); 14343 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 14344 } 14345 14346 return block.addBitCast(constant_alloc_ty, mutable_alloc); 14347 } 14348 14349 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); 14350 { 14351 var elem_i: u32 = 0; 14352 while (elem_i < lhs_len) : (elem_i += 1) { 14353 const index = try pt.intRef(.usize, elem_i); 14354 const operand_src = block.src(.{ .array_cat_lhs = .{ 14355 .array_cat_offset = inst_data.src_node, 14356 .elem_index = elem_i, 14357 } }); 14358 const init = try sema.elemVal(block, operand_src, lhs, index, src, true); 14359 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src); 14360 } 14361 while (elem_i < result_len) : (elem_i += 1) { 14362 const rhs_elem_i = elem_i - lhs_len; 14363 const index = try pt.intRef(.usize, rhs_elem_i); 14364 const operand_src = block.src(.{ .array_cat_rhs = .{ 14365 .array_cat_offset = inst_data.src_node, 14366 .elem_index = @intCast(rhs_elem_i), 14367 } }); 14368 const init = try sema.elemVal(block, operand_src, rhs, index, src, true); 14369 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src); 14370 } 14371 } 14372 14373 return block.addAggregateInit(result_ty, element_refs); 14374 } 14375 14376 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo { 14377 const pt = sema.pt; 14378 const zcu = pt.zcu; 14379 const operand_ty = sema.typeOf(operand); 14380 switch (operand_ty.zigTypeTag(zcu)) { 14381 .array => return operand_ty.arrayInfo(zcu), 14382 .pointer => { 14383 const ptr_info = operand_ty.ptrInfo(zcu); 14384 switch (ptr_info.flags.size) { 14385 .slice => { 14386 const val = try sema.resolveConstDefinedValue(block, src, operand, .{ .simple = .slice_cat_operand }); 14387 return .{ 14388 .elem_type = .fromInterned(ptr_info.child), 14389 .sentinel = switch (ptr_info.sentinel) { 14390 .none => null, 14391 else => Value.fromInterned(ptr_info.sentinel), 14392 }, 14393 .len = try val.sliceLen(pt), 14394 }; 14395 }, 14396 .one => { 14397 if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { 14398 return Type.fromInterned(ptr_info.child).arrayInfo(zcu); 14399 } 14400 }, 14401 .c, .many => {}, 14402 } 14403 }, 14404 .@"struct" => { 14405 if (operand_ty.isTuple(zcu) and peer_ty.isIndexable(zcu)) { 14406 assert(!peer_ty.isTuple(zcu)); 14407 return .{ 14408 .elem_type = peer_ty.elemType2(zcu), 14409 .sentinel = null, 14410 .len = operand_ty.arrayLen(zcu), 14411 }; 14412 } 14413 }, 14414 else => {}, 14415 } 14416 return null; 14417 } 14418 14419 fn analyzeTupleMul( 14420 sema: *Sema, 14421 block: *Block, 14422 src_node: std.zig.Ast.Node.Offset, 14423 operand: Air.Inst.Ref, 14424 factor: usize, 14425 ) CompileError!Air.Inst.Ref { 14426 const pt = sema.pt; 14427 const zcu = pt.zcu; 14428 const operand_ty = sema.typeOf(operand); 14429 const src = block.nodeOffset(src_node); 14430 const len_src = block.src(.{ .node_offset_bin_rhs = src_node }); 14431 14432 const tuple_len = operand_ty.structFieldCount(zcu); 14433 const final_len = std.math.mul(usize, tuple_len, factor) catch 14434 return sema.fail(block, len_src, "operation results in overflow", .{}); 14435 14436 if (final_len == 0) { 14437 return .empty_tuple; 14438 } 14439 const types = try sema.arena.alloc(InternPool.Index, final_len); 14440 const values = try sema.arena.alloc(InternPool.Index, final_len); 14441 14442 const opt_runtime_src = rs: { 14443 var runtime_src: ?LazySrcLoc = null; 14444 for (0..tuple_len) |i| { 14445 types[i] = operand_ty.fieldType(i, zcu).toIntern(); 14446 values[i] = operand_ty.structFieldDefaultValue(i, zcu).toIntern(); 14447 const operand_src = block.src(.{ .array_cat_lhs = .{ 14448 .array_cat_offset = src_node, 14449 .elem_index = @intCast(i), 14450 } }); 14451 if (values[i] == .unreachable_value) { 14452 runtime_src = operand_src; 14453 values[i] = .none; // TODO don't treat unreachable_value as special 14454 } 14455 } 14456 for (0..factor) |i| { 14457 mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]); 14458 mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]); 14459 } 14460 break :rs runtime_src; 14461 }; 14462 14463 const tuple_ty = try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{ 14464 .types = types, 14465 .values = values, 14466 }); 14467 14468 const runtime_src = opt_runtime_src orelse { 14469 const tuple_val = try pt.intern(.{ .aggregate = .{ 14470 .ty = tuple_ty, 14471 .storage = .{ .elems = values }, 14472 } }); 14473 return Air.internedToRef(tuple_val); 14474 }; 14475 14476 try sema.requireRuntimeBlock(block, src, runtime_src); 14477 14478 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); 14479 var i: u32 = 0; 14480 while (i < tuple_len) : (i += 1) { 14481 element_refs[i] = try sema.tupleFieldValByIndex(block, operand, @intCast(i), operand_ty); 14482 } 14483 i = 1; 14484 while (i < factor) : (i += 1) { 14485 @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]); 14486 } 14487 14488 return block.addAggregateInit(.fromInterned(tuple_ty), element_refs); 14489 } 14490 14491 fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14492 const tracy = trace(@src()); 14493 defer tracy.end(); 14494 14495 const pt = sema.pt; 14496 const zcu = pt.zcu; 14497 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14498 const extra = sema.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data; 14499 const uncoerced_lhs = try sema.resolveInst(extra.lhs); 14500 const uncoerced_lhs_ty = sema.typeOf(uncoerced_lhs); 14501 const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); 14502 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 14503 const operator_src = block.src(.{ .node_offset_main_token = inst_data.src_node }); 14504 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 14505 14506 const lhs, const lhs_ty = coerced_lhs: { 14507 // If we have a result type, we might be able to do this more efficiently 14508 // by coercing the LHS first. Specifically, if we want an array or vector 14509 // and have a tuple, coerce the tuple immediately. 14510 no_coerce: { 14511 if (extra.res_ty == .none) break :no_coerce; 14512 const res_ty = try sema.resolveTypeOrPoison(block, src, extra.res_ty) orelse break :no_coerce; 14513 if (!uncoerced_lhs_ty.isTuple(zcu)) break :no_coerce; 14514 const lhs_len = uncoerced_lhs_ty.structFieldCount(zcu); 14515 const lhs_dest_ty = switch (res_ty.zigTypeTag(zcu)) { 14516 else => break :no_coerce, 14517 .array => try pt.arrayType(.{ 14518 .child = res_ty.childType(zcu).toIntern(), 14519 .len = lhs_len, 14520 .sentinel = if (res_ty.sentinel(zcu)) |s| s.toIntern() else .none, 14521 }), 14522 .vector => try pt.vectorType(.{ 14523 .child = res_ty.childType(zcu).toIntern(), 14524 .len = lhs_len, 14525 }), 14526 }; 14527 // Attempt to coerce to this type, but don't emit an error if it fails. Instead, 14528 // just exit out of this path and let the usual error happen later, so that error 14529 // messages are consistent. 14530 const coerced = sema.coerceExtra(block, lhs_dest_ty, uncoerced_lhs, lhs_src, .{ .report_err = false }) catch |err| switch (err) { 14531 error.NotCoercible => break :no_coerce, 14532 else => |e| return e, 14533 }; 14534 break :coerced_lhs .{ coerced, lhs_dest_ty }; 14535 } 14536 break :coerced_lhs .{ uncoerced_lhs, uncoerced_lhs_ty }; 14537 }; 14538 14539 if (lhs_ty.isTuple(zcu)) { 14540 // In `**` rhs must be comptime-known, but lhs can be runtime-known 14541 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor }); 14542 const factor_casted = try sema.usizeCast(block, rhs_src, factor); 14543 return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted); 14544 } 14545 14546 // Analyze the lhs first, to catch the case that someone tried to do exponentiation 14547 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse { 14548 const msg = msg: { 14549 const msg = try sema.errMsg(lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)}); 14550 errdefer msg.destroy(sema.gpa); 14551 switch (lhs_ty.zigTypeTag(zcu)) { 14552 .int, .float, .comptime_float, .comptime_int, .vector => { 14553 try sema.errNote(operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{}); 14554 }, 14555 else => {}, 14556 } 14557 break :msg msg; 14558 }; 14559 return sema.failWithOwnedErrorMsg(block, msg); 14560 }; 14561 14562 // In `**` rhs must be comptime-known, but lhs can be runtime-known 14563 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor }); 14564 14565 const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch 14566 return sema.fail(block, rhs_src, "operation results in overflow", .{}); 14567 const result_len = try sema.usizeCast(block, src, result_len_u64); 14568 14569 const result_ty = try pt.arrayType(.{ 14570 .len = result_len, 14571 .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none, 14572 .child = lhs_info.elem_type.toIntern(), 14573 }); 14574 14575 const ptr_addrspace = if (lhs_ty.zigTypeTag(zcu) == .pointer) lhs_ty.ptrAddressSpace(zcu) else null; 14576 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); 14577 14578 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| ct: { 14579 const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu)) 14580 try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :ct 14581 else if (lhs_ty.isSlice(zcu)) 14582 try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :ct 14583 else 14584 lhs_val; 14585 14586 const val = v: { 14587 // Optimization for the common pattern of a single element repeated N times, such 14588 // as zero-filling a byte array. 14589 if (lhs_len == 1 and lhs_info.sentinel == null) { 14590 const elem_val = try lhs_sub_val.elemValue(pt, 0); 14591 break :v try pt.intern(.{ .aggregate = .{ 14592 .ty = result_ty.toIntern(), 14593 .storage = .{ .repeated_elem = elem_val.toIntern() }, 14594 } }); 14595 } 14596 14597 const element_vals = try sema.arena.alloc(InternPool.Index, result_len); 14598 var elem_i: usize = 0; 14599 while (elem_i < result_len) { 14600 var lhs_i: usize = 0; 14601 while (lhs_i < lhs_len) : (lhs_i += 1) { 14602 const elem_val = try lhs_sub_val.elemValue(pt, lhs_i); 14603 element_vals[elem_i] = elem_val.toIntern(); 14604 elem_i += 1; 14605 } 14606 } 14607 break :v try pt.intern(.{ .aggregate = .{ 14608 .ty = result_ty.toIntern(), 14609 .storage = .{ .elems = element_vals }, 14610 } }); 14611 }; 14612 return sema.addConstantMaybeRef(val, ptr_addrspace != null); 14613 } 14614 14615 try sema.requireRuntimeBlock(block, src, lhs_src); 14616 14617 // Grab all the LHS values ahead of time, rather than repeatedly emitting instructions 14618 // to get the same elem values. 14619 const lhs_vals = try sema.arena.alloc(Air.Inst.Ref, lhs_len); 14620 for (lhs_vals, 0..) |*lhs_val, idx| { 14621 const idx_ref = try pt.intRef(.usize, idx); 14622 lhs_val.* = try sema.elemVal(block, lhs_src, lhs, idx_ref, src, false); 14623 } 14624 14625 if (ptr_addrspace) |ptr_as| { 14626 const alloc_ty = try pt.ptrTypeSema(.{ 14627 .child = result_ty.toIntern(), 14628 .flags = .{ 14629 .address_space = ptr_as, 14630 .is_const = true, 14631 }, 14632 }); 14633 const alloc = try block.addTy(.alloc, alloc_ty); 14634 const elem_ptr_ty = try pt.ptrTypeSema(.{ 14635 .child = lhs_info.elem_type.toIntern(), 14636 .flags = .{ .address_space = ptr_as }, 14637 }); 14638 14639 var elem_i: usize = 0; 14640 while (elem_i < result_len) { 14641 for (lhs_vals) |lhs_val| { 14642 const elem_index = try pt.intRef(.usize, elem_i); 14643 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 14644 try sema.storePtr2(block, src, elem_ptr, src, lhs_val, lhs_src, .store); 14645 elem_i += 1; 14646 } 14647 } 14648 if (lhs_info.sentinel) |sent_val| { 14649 const elem_index = try pt.intRef(.usize, result_len); 14650 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 14651 const init = Air.internedToRef(sent_val.toIntern()); 14652 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 14653 } 14654 14655 return alloc; 14656 } 14657 14658 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); 14659 for (0..try sema.usizeCast(block, rhs_src, factor)) |i| { 14660 @memcpy(element_refs[i * lhs_len ..][0..lhs_len], lhs_vals); 14661 } 14662 return block.addAggregateInit(result_ty, element_refs); 14663 } 14664 14665 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14666 const pt = sema.pt; 14667 const zcu = pt.zcu; 14668 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 14669 const src = block.nodeOffset(inst_data.src_node); 14670 const lhs_src = src; 14671 const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 14672 14673 const rhs = try sema.resolveInst(inst_data.operand); 14674 const rhs_ty = sema.typeOf(rhs); 14675 const rhs_scalar_ty = rhs_ty.scalarType(zcu); 14676 14677 if (rhs_scalar_ty.isUnsignedInt(zcu) or switch (rhs_scalar_ty.zigTypeTag(zcu)) { 14678 .int, .comptime_int, .float, .comptime_float => false, 14679 else => true, 14680 }) { 14681 return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}); 14682 } 14683 14684 if (rhs_scalar_ty.isAnyFloat()) { 14685 // We handle float negation here to ensure negative zero is represented in the bits. 14686 if (try sema.resolveValue(rhs)) |rhs_val| { 14687 const result = try arith.negateFloat(sema, rhs_ty, rhs_val); 14688 return Air.internedToRef(result.toIntern()); 14689 } 14690 try sema.requireRuntimeBlock(block, src, null); 14691 return block.addUnOp(if (block.float_mode == .optimized) .neg_optimized else .neg, rhs); 14692 } 14693 14694 const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern()); 14695 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true); 14696 } 14697 14698 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14699 const pt = sema.pt; 14700 const zcu = pt.zcu; 14701 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 14702 const src = block.nodeOffset(inst_data.src_node); 14703 const lhs_src = src; 14704 const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 14705 14706 const rhs = try sema.resolveInst(inst_data.operand); 14707 const rhs_ty = sema.typeOf(rhs); 14708 const rhs_scalar_ty = rhs_ty.scalarType(zcu); 14709 14710 switch (rhs_scalar_ty.zigTypeTag(zcu)) { 14711 .int, .comptime_int, .float, .comptime_float => {}, 14712 else => return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}), 14713 } 14714 14715 const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern()); 14716 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true); 14717 } 14718 14719 fn zirArithmetic( 14720 sema: *Sema, 14721 block: *Block, 14722 inst: Zir.Inst.Index, 14723 zir_tag: Zir.Inst.Tag, 14724 safety: bool, 14725 ) CompileError!Air.Inst.Ref { 14726 const tracy = trace(@src()); 14727 defer tracy.end(); 14728 14729 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14730 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 14731 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 14732 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 14733 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14734 const lhs = try sema.resolveInst(extra.lhs); 14735 const rhs = try sema.resolveInst(extra.rhs); 14736 14737 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, src, lhs_src, rhs_src, safety); 14738 } 14739 14740 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14741 const pt = sema.pt; 14742 const zcu = pt.zcu; 14743 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14744 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 14745 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 14746 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 14747 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14748 const lhs = try sema.resolveInst(extra.lhs); 14749 const rhs = try sema.resolveInst(extra.rhs); 14750 const lhs_ty = sema.typeOf(lhs); 14751 const rhs_ty = sema.typeOf(rhs); 14752 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 14753 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 14754 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14755 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 14756 14757 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 14758 .override = &.{ lhs_src, rhs_src }, 14759 }); 14760 14761 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14762 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14763 14764 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 14765 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 14766 14767 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 14768 14769 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div); 14770 14771 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 14772 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 14773 14774 if ((lhs_ty.zigTypeTag(zcu) == .comptime_float and rhs_ty.zigTypeTag(zcu) == .comptime_int) or 14775 (lhs_ty.zigTypeTag(zcu) == .comptime_int and rhs_ty.zigTypeTag(zcu) == .comptime_float)) 14776 { 14777 // If it makes a difference whether we coerce to ints or floats before doing the division, error. 14778 // If lhs % rhs is 0, it doesn't matter. 14779 const lhs_val = maybe_lhs_val orelse unreachable; 14780 const rhs_val = maybe_rhs_val orelse unreachable; 14781 const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, pt) catch unreachable; 14782 if (!rem.compareAllWithZero(.eq, zcu)) { 14783 return sema.fail( 14784 block, 14785 src, 14786 "ambiguous coercion of division operands '{f}' and '{f}'; non-zero remainder '{f}'", 14787 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt), rem.fmtValueSema(pt, sema) }, 14788 ); 14789 } 14790 } 14791 14792 // TODO: emit compile error when .div is used on integers and there would be an 14793 // ambiguous result between div_floor and div_trunc. 14794 14795 // The rules here are like those in `analyzeArithmetic`: 14796 // 14797 // * If both operands are comptime-known, call the corresponding function in `arith`. 14798 // Inputs which would be IB at runtime are compile errors. 14799 // 14800 // * Otherwise, if one operand is comptime-known `undefined`, we either trigger a compile error 14801 // or return `undefined`, depending on whether this operator can trigger IB. 14802 // 14803 // * No other comptime operand determines a comptime result, so remaining cases are runtime ops. 14804 14805 const allow_div_zero = !is_int and 14806 resolved_type.toIntern() != .comptime_float_type and 14807 block.float_mode == .strict; 14808 14809 if (maybe_lhs_val) |lhs_val| { 14810 if (maybe_rhs_val) |rhs_val| { 14811 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div); 14812 return Air.internedToRef(result.toIntern()); 14813 } 14814 if (allow_div_zero) { 14815 if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 14816 } else { 14817 if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src); 14818 } 14819 } else if (maybe_rhs_val) |rhs_val| { 14820 if (allow_div_zero) { 14821 if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 14822 } else { 14823 if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src); 14824 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 14825 } 14826 } 14827 14828 if (block.wantSafety()) { 14829 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14830 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14831 } 14832 14833 const air_tag = if (is_int) blk: { 14834 if (lhs_ty.isSignedInt(zcu) or rhs_ty.isSignedInt(zcu)) { 14835 return sema.fail( 14836 block, 14837 src, 14838 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, or @divExact", 14839 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) }, 14840 ); 14841 } 14842 break :blk Air.Inst.Tag.div_trunc; 14843 } else switch (block.float_mode) { 14844 .optimized => Air.Inst.Tag.div_float_optimized, 14845 .strict => Air.Inst.Tag.div_float, 14846 }; 14847 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 14848 } 14849 14850 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14851 const pt = sema.pt; 14852 const zcu = pt.zcu; 14853 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14854 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 14855 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 14856 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 14857 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14858 const lhs = try sema.resolveInst(extra.lhs); 14859 const rhs = try sema.resolveInst(extra.rhs); 14860 const lhs_ty = sema.typeOf(lhs); 14861 const rhs_ty = sema.typeOf(rhs); 14862 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 14863 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 14864 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14865 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 14866 14867 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 14868 .override = &.{ lhs_src, rhs_src }, 14869 }); 14870 14871 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14872 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14873 14874 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 14875 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 14876 14877 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 14878 14879 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); 14880 14881 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 14882 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 14883 14884 // Because `@divExact` can trigger Illegal Behavior, undefined operands trigger Illegal Behavior. 14885 14886 if (maybe_lhs_val) |lhs_val| { 14887 if (maybe_rhs_val) |rhs_val| { 14888 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_exact); 14889 return Air.internedToRef(result.toIntern()); 14890 } 14891 if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src); 14892 } else if (maybe_rhs_val) |rhs_val| { 14893 if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src); 14894 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 14895 } 14896 14897 // Depending on whether safety is enabled, we will have a slightly different strategy 14898 // here. The `div_exact` AIR instruction causes illegal behavior if a remainder 14899 // is produced, so in the safety check case, it cannot be used. Instead we do a 14900 // div_trunc and check for remainder. 14901 14902 if (block.wantSafety()) { 14903 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14904 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14905 14906 const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs); 14907 const ok = if (!is_int) ok: { 14908 const floored = try block.addUnOp(.floor, result); 14909 14910 if (resolved_type.zigTypeTag(zcu) == .vector) { 14911 const eql = try block.addCmpVector(result, floored, .eq); 14912 break :ok try block.addReduce(eql, .And); 14913 } else { 14914 const is_in_range = try block.addBinOp(switch (block.float_mode) { 14915 .strict => .cmp_eq, 14916 .optimized => .cmp_eq_optimized, 14917 }, result, floored); 14918 break :ok is_in_range; 14919 } 14920 } else ok: { 14921 const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); 14922 14923 const scalar_zero = switch (scalar_tag) { 14924 .comptime_float, .float => try pt.floatValue(resolved_type.scalarType(zcu), 0.0), 14925 .comptime_int, .int => try pt.intValue(resolved_type.scalarType(zcu), 0), 14926 else => unreachable, 14927 }; 14928 if (resolved_type.zigTypeTag(zcu) == .vector) { 14929 const zero_val = try sema.splat(resolved_type, scalar_zero); 14930 const zero = Air.internedToRef(zero_val.toIntern()); 14931 const eql = try block.addCmpVector(remainder, zero, .eq); 14932 break :ok try block.addReduce(eql, .And); 14933 } else { 14934 const zero = Air.internedToRef(scalar_zero.toIntern()); 14935 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero); 14936 break :ok is_in_range; 14937 } 14938 }; 14939 try sema.addSafetyCheck(block, src, ok, .exact_division_remainder); 14940 return result; 14941 } 14942 14943 return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs); 14944 } 14945 14946 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14947 const pt = sema.pt; 14948 const zcu = pt.zcu; 14949 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14950 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 14951 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 14952 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 14953 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14954 const lhs = try sema.resolveInst(extra.lhs); 14955 const rhs = try sema.resolveInst(extra.rhs); 14956 const lhs_ty = sema.typeOf(lhs); 14957 const rhs_ty = sema.typeOf(rhs); 14958 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 14959 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 14960 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14961 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 14962 14963 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 14964 .override = &.{ lhs_src, rhs_src }, 14965 }); 14966 14967 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14968 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14969 14970 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 14971 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 14972 14973 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 14974 14975 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor); 14976 14977 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 14978 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 14979 14980 const allow_div_zero = !is_int and 14981 resolved_type.toIntern() != .comptime_float_type and 14982 block.float_mode == .strict; 14983 14984 if (maybe_lhs_val) |lhs_val| { 14985 if (maybe_rhs_val) |rhs_val| { 14986 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_floor); 14987 return Air.internedToRef(result.toIntern()); 14988 } 14989 if (allow_div_zero) { 14990 if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 14991 } else { 14992 if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src); 14993 } 14994 } else if (maybe_rhs_val) |rhs_val| { 14995 if (allow_div_zero) { 14996 if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 14997 } else { 14998 if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src); 14999 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15000 } 15001 } 15002 15003 if (block.wantSafety()) { 15004 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 15005 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15006 } 15007 15008 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); 15009 } 15010 15011 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15012 const pt = sema.pt; 15013 const zcu = pt.zcu; 15014 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15015 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15016 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 15017 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 15018 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15019 const lhs = try sema.resolveInst(extra.lhs); 15020 const rhs = try sema.resolveInst(extra.rhs); 15021 const lhs_ty = sema.typeOf(lhs); 15022 const rhs_ty = sema.typeOf(rhs); 15023 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15024 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15025 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15026 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15027 15028 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15029 .override = &.{ lhs_src, rhs_src }, 15030 }); 15031 15032 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15033 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15034 15035 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 15036 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15037 15038 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15039 15040 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc); 15041 15042 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15043 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15044 15045 const allow_div_zero = !is_int and 15046 resolved_type.toIntern() != .comptime_float_type and 15047 block.float_mode == .strict; 15048 15049 if (maybe_lhs_val) |lhs_val| { 15050 if (maybe_rhs_val) |rhs_val| { 15051 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_trunc); 15052 return Air.internedToRef(result.toIntern()); 15053 } 15054 if (allow_div_zero) { 15055 if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15056 } else { 15057 if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src); 15058 } 15059 } else if (maybe_rhs_val) |rhs_val| { 15060 if (allow_div_zero) { 15061 if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15062 } else { 15063 if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src); 15064 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15065 } 15066 } 15067 15068 if (block.wantSafety()) { 15069 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 15070 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15071 } 15072 15073 return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs); 15074 } 15075 15076 fn addDivIntOverflowSafety( 15077 sema: *Sema, 15078 block: *Block, 15079 src: LazySrcLoc, 15080 resolved_type: Type, 15081 lhs_scalar_ty: Type, 15082 maybe_lhs_val: ?Value, 15083 maybe_rhs_val: ?Value, 15084 casted_lhs: Air.Inst.Ref, 15085 casted_rhs: Air.Inst.Ref, 15086 is_int: bool, 15087 ) CompileError!void { 15088 const pt = sema.pt; 15089 const zcu = pt.zcu; 15090 if (!is_int) return; 15091 15092 // If the LHS is unsigned, it cannot cause overflow. 15093 if (!lhs_scalar_ty.isSignedInt(zcu)) return; 15094 15095 // If the LHS is widened to a larger integer type, no overflow is possible. 15096 if (lhs_scalar_ty.intInfo(zcu).bits < resolved_type.intInfo(zcu).bits) { 15097 return; 15098 } 15099 15100 const min_int = try resolved_type.minInt(pt, resolved_type); 15101 const neg_one_scalar = try pt.intValue(lhs_scalar_ty, -1); 15102 const neg_one = try sema.splat(resolved_type, neg_one_scalar); 15103 15104 // If the LHS is comptime-known to be not equal to the min int, 15105 // no overflow is possible. 15106 if (maybe_lhs_val) |lhs_val| { 15107 if (try lhs_val.compareAll(.neq, min_int, resolved_type, pt)) return; 15108 } 15109 15110 // If the RHS is comptime-known to not be equal to -1, no overflow is possible. 15111 if (maybe_rhs_val) |rhs_val| { 15112 if (try rhs_val.compareAll(.neq, neg_one, resolved_type, pt)) return; 15113 } 15114 15115 if (resolved_type.zigTypeTag(zcu) == .vector) { 15116 const vec_len = resolved_type.vectorLen(zcu); 15117 15118 // This is a bool vector whose elements are true if the LHS element does NOT equal `min_int`. 15119 const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val) |lhs_val| ok: { 15120 // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements. 15121 const min_int_scalar = try min_int.elemValue(pt, 0); 15122 const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len); 15123 for (elems_ok, 0..) |*elem_ok, elem_idx| { 15124 const elem_val = try lhs_val.elemValue(pt, elem_idx); 15125 elem_ok.* = if (elem_val.eqlScalarNum(min_int_scalar, zcu)) .bool_false else .bool_true; 15126 } 15127 break :ok Air.internedToRef(try pt.intern(.{ .aggregate = .{ 15128 .ty = (try pt.vectorType(.{ 15129 .len = vec_len, 15130 .child = .bool_type, 15131 })).toIntern(), 15132 .storage = .{ .elems = elems_ok }, 15133 } })); 15134 } else ok: { 15135 // The operand isn't comptime-known; add a runtime comparison. 15136 const min_int_ref = Air.internedToRef(min_int.toIntern()); 15137 break :ok try block.addCmpVector(casted_lhs, min_int_ref, .neq); 15138 }; 15139 15140 // This is a bool vector whose elements are true if the RHS element does NOT equal -1. 15141 const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val) |rhs_val| ok: { 15142 // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements. 15143 const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len); 15144 for (elems_ok, 0..) |*elem_ok, elem_idx| { 15145 const elem_val = try rhs_val.elemValue(pt, elem_idx); 15146 elem_ok.* = if (elem_val.eqlScalarNum(neg_one_scalar, zcu)) .bool_false else .bool_true; 15147 } 15148 break :ok Air.internedToRef(try pt.intern(.{ .aggregate = .{ 15149 .ty = (try pt.vectorType(.{ 15150 .len = vec_len, 15151 .child = .bool_type, 15152 })).toIntern(), 15153 .storage = .{ .elems = elems_ok }, 15154 } })); 15155 } else ok: { 15156 // The operand isn't comptime-known; add a runtime comparison. 15157 const neg_one_ref = Air.internedToRef(neg_one.toIntern()); 15158 break :ok try block.addCmpVector(casted_rhs, neg_one_ref, .neq); 15159 }; 15160 15161 const ok = try block.addReduce(try block.addBinOp(.bool_or, lhs_ok, rhs_ok), .And); 15162 try sema.addSafetyCheck(block, src, ok, .integer_overflow); 15163 } else { 15164 const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val == null) ok: { 15165 const min_int_ref = Air.internedToRef(min_int.toIntern()); 15166 break :ok try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); 15167 } else .none; // means false 15168 const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val == null) ok: { 15169 const neg_one_ref = Air.internedToRef(neg_one.toIntern()); 15170 break :ok try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref); 15171 } else .none; // means false 15172 15173 const ok = if (lhs_ok != .none and rhs_ok != .none) 15174 try block.addBinOp(.bool_or, lhs_ok, rhs_ok) 15175 else if (lhs_ok != .none) 15176 lhs_ok 15177 else if (rhs_ok != .none) 15178 rhs_ok 15179 else 15180 unreachable; 15181 15182 try sema.addSafetyCheck(block, src, ok, .integer_overflow); 15183 } 15184 } 15185 15186 fn addDivByZeroSafety( 15187 sema: *Sema, 15188 block: *Block, 15189 src: LazySrcLoc, 15190 resolved_type: Type, 15191 maybe_rhs_val: ?Value, 15192 casted_rhs: Air.Inst.Ref, 15193 is_int: bool, 15194 ) CompileError!void { 15195 // Strict IEEE floats have well-defined division by zero. 15196 if (!is_int and block.float_mode == .strict) return; 15197 15198 // If rhs was comptime-known to be zero a compile error would have been 15199 // emitted above. 15200 if (maybe_rhs_val != null) return; 15201 15202 const pt = sema.pt; 15203 const zcu = pt.zcu; 15204 const scalar_zero = if (is_int) 15205 try pt.intValue(resolved_type.scalarType(zcu), 0) 15206 else 15207 try pt.floatValue(resolved_type.scalarType(zcu), 0.0); 15208 const ok = if (resolved_type.zigTypeTag(zcu) == .vector) ok: { 15209 const zero_val = try sema.splat(resolved_type, scalar_zero); 15210 const zero = Air.internedToRef(zero_val.toIntern()); 15211 const ok = try block.addCmpVector(casted_rhs, zero, .neq); 15212 break :ok try block.addReduce(ok, .And); 15213 } else ok: { 15214 const zero = Air.internedToRef(scalar_zero.toIntern()); 15215 break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); 15216 }; 15217 try sema.addSafetyCheck(block, src, ok, .divide_by_zero); 15218 } 15219 15220 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag { 15221 if (is_int) return normal; 15222 return switch (block.float_mode) { 15223 .strict => normal, 15224 .optimized => optimized, 15225 }; 15226 } 15227 15228 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15229 const pt = sema.pt; 15230 const zcu = pt.zcu; 15231 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15232 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15233 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 15234 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 15235 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15236 const lhs = try sema.resolveInst(extra.lhs); 15237 const rhs = try sema.resolveInst(extra.rhs); 15238 const lhs_ty = sema.typeOf(lhs); 15239 const rhs_ty = sema.typeOf(rhs); 15240 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15241 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15242 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15243 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15244 15245 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15246 .override = &.{ lhs_src, rhs_src }, 15247 }); 15248 15249 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15250 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15251 15252 const lhs_scalar_ty = lhs_ty.scalarType(zcu); 15253 const rhs_scalar_ty = rhs_ty.scalarType(zcu); 15254 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15255 15256 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15257 15258 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem); 15259 15260 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15261 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15262 15263 const lhs_maybe_negative = a: { 15264 if (lhs_scalar_ty.isUnsignedInt(zcu)) break :a false; 15265 const lhs_val = maybe_lhs_val orelse break :a true; 15266 if (lhs_val.compareAllWithZero(.gte, zcu)) break :a false; 15267 break :a true; 15268 }; 15269 const rhs_maybe_negative = a: { 15270 if (rhs_scalar_ty.isUnsignedInt(zcu)) break :a false; 15271 const rhs_val = maybe_rhs_val orelse break :a true; 15272 if (rhs_val.compareAllWithZero(.gte, zcu)) break :a false; 15273 break :a true; 15274 }; 15275 15276 if (maybe_lhs_val) |lhs_val| { 15277 if (maybe_rhs_val) |rhs_val| { 15278 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem); 15279 if (lhs_maybe_negative or rhs_maybe_negative) { 15280 if (!result.compareAllWithZero(.eq, zcu)) { 15281 // Non-zero result means ambiguity between mod and rem 15282 return sema.failWithModRemNegative(block, src: { 15283 if (lhs_maybe_negative) break :src lhs_src; 15284 if (rhs_maybe_negative) break :src rhs_src; 15285 unreachable; 15286 }, lhs_ty, rhs_ty); 15287 } 15288 } 15289 return Air.internedToRef(result.toIntern()); 15290 } 15291 } 15292 15293 // Result not comptime-known, so floats and signed integers are illegal due to mod/rem ambiguity 15294 if (lhs_maybe_negative or rhs_maybe_negative) { 15295 return sema.failWithModRemNegative(block, src: { 15296 if (lhs_maybe_negative) break :src lhs_src; 15297 if (rhs_maybe_negative) break :src rhs_src; 15298 unreachable; 15299 }, lhs_ty, rhs_ty); 15300 } 15301 15302 const allow_div_zero = !is_int and 15303 resolved_type.toIntern() != .comptime_float_type and 15304 block.float_mode == .strict; 15305 15306 if (maybe_lhs_val) |lhs_val| { 15307 if (allow_div_zero) { 15308 if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15309 } else { 15310 if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src); 15311 } 15312 } else if (maybe_rhs_val) |rhs_val| { 15313 if (allow_div_zero) { 15314 if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15315 } else { 15316 if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src); 15317 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15318 } 15319 } 15320 15321 if (block.wantSafety()) { 15322 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15323 } 15324 15325 const air_tag = airTag(block, is_int, .rem, .rem_optimized); 15326 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15327 } 15328 15329 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15330 const pt = sema.pt; 15331 const zcu = pt.zcu; 15332 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15333 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15334 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 15335 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 15336 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15337 const lhs = try sema.resolveInst(extra.lhs); 15338 const rhs = try sema.resolveInst(extra.rhs); 15339 const lhs_ty = sema.typeOf(lhs); 15340 const rhs_ty = sema.typeOf(rhs); 15341 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15342 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15343 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15344 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15345 15346 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15347 .override = &.{ lhs_src, rhs_src }, 15348 }); 15349 15350 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15351 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15352 15353 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15354 15355 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15356 15357 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod); 15358 15359 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15360 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15361 15362 const allow_div_zero = !is_int and 15363 resolved_type.toIntern() != .comptime_float_type and 15364 block.float_mode == .strict; 15365 15366 if (maybe_lhs_val) |lhs_val| { 15367 if (maybe_rhs_val) |rhs_val| { 15368 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .mod); 15369 return Air.internedToRef(result.toIntern()); 15370 } 15371 if (allow_div_zero) { 15372 if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15373 } else { 15374 if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src); 15375 } 15376 } else if (maybe_rhs_val) |rhs_val| { 15377 if (allow_div_zero) { 15378 if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15379 } else { 15380 if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src); 15381 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15382 } 15383 } 15384 15385 if (block.wantSafety()) { 15386 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15387 } 15388 15389 const air_tag = airTag(block, is_int, .mod, .mod_optimized); 15390 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15391 } 15392 15393 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15394 const pt = sema.pt; 15395 const zcu = pt.zcu; 15396 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15397 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 15398 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 15399 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 15400 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15401 const lhs = try sema.resolveInst(extra.lhs); 15402 const rhs = try sema.resolveInst(extra.rhs); 15403 const lhs_ty = sema.typeOf(lhs); 15404 const rhs_ty = sema.typeOf(rhs); 15405 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15406 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15407 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15408 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); 15409 15410 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15411 .override = &.{ lhs_src, rhs_src }, 15412 }); 15413 15414 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15415 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15416 15417 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); 15418 15419 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 15420 15421 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem); 15422 15423 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15424 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15425 15426 const allow_div_zero = !is_int and 15427 resolved_type.toIntern() != .comptime_float_type and 15428 block.float_mode == .strict; 15429 15430 if (maybe_lhs_val) |lhs_val| { 15431 if (maybe_rhs_val) |rhs_val| { 15432 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem); 15433 return Air.internedToRef(result.toIntern()); 15434 } 15435 if (allow_div_zero) { 15436 if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15437 } else { 15438 if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src); 15439 } 15440 } else if (maybe_rhs_val) |rhs_val| { 15441 if (allow_div_zero) { 15442 if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15443 } else { 15444 if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src); 15445 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); 15446 } 15447 } 15448 15449 if (block.wantSafety()) { 15450 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15451 } 15452 15453 const air_tag = airTag(block, is_int, .rem, .rem_optimized); 15454 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15455 } 15456 15457 fn zirOverflowArithmetic( 15458 sema: *Sema, 15459 block: *Block, 15460 extended: Zir.Inst.Extended.InstData, 15461 zir_tag: Zir.Inst.Extended, 15462 ) CompileError!Air.Inst.Ref { 15463 const tracy = trace(@src()); 15464 defer tracy.end(); 15465 15466 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 15467 const src = block.nodeOffset(extra.node); 15468 15469 const lhs_src = block.builtinCallArgSrc(extra.node, 0); 15470 const rhs_src = block.builtinCallArgSrc(extra.node, 1); 15471 15472 const uncasted_lhs = try sema.resolveInst(extra.lhs); 15473 const uncasted_rhs = try sema.resolveInst(extra.rhs); 15474 15475 const lhs_ty = sema.typeOf(uncasted_lhs); 15476 const rhs_ty = sema.typeOf(uncasted_rhs); 15477 const pt = sema.pt; 15478 const zcu = pt.zcu; 15479 const ip = &zcu.intern_pool; 15480 15481 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15482 15483 const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs }; 15484 const dest_ty = if (zir_tag == .shl_with_overflow) 15485 lhs_ty 15486 else 15487 try sema.resolvePeerTypes(block, src, instructions, .{ 15488 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 15489 }); 15490 15491 const rhs_dest_ty = if (zir_tag == .shl_with_overflow) 15492 try sema.log2IntType(block, lhs_ty, src) 15493 else 15494 dest_ty; 15495 15496 const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src); 15497 const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src); 15498 15499 if (dest_ty.scalarType(zcu).zigTypeTag(zcu) != .int) { 15500 return sema.fail(block, src, "expected vector of integers or integer tag type, found '{f}'", .{dest_ty.fmt(pt)}); 15501 } 15502 15503 const maybe_lhs_val = try sema.resolveValue(lhs); 15504 const maybe_rhs_val = try sema.resolveValue(rhs); 15505 15506 const tuple_ty = try pt.overflowArithmeticTupleType(dest_ty); 15507 const overflow_ty: Type = .fromInterned(ip.indexToKey(tuple_ty.toIntern()).tuple_type.types.get(ip)[1]); 15508 15509 var result: struct { 15510 inst: Air.Inst.Ref = .none, 15511 wrapped: Value = Value.@"unreachable", 15512 overflow_bit: Value, 15513 } = result: { 15514 switch (zir_tag) { 15515 .add_with_overflow => { 15516 // If either of the arguments is zero, `false` is returned and the other is stored 15517 // to the result, even if it is undefined.. 15518 // Otherwise, if either of the argument is undefined, undefined is returned. 15519 if (maybe_lhs_val) |lhs_val| { 15520 if (!lhs_val.isUndef(zcu) and (try lhs_val.compareAllWithZeroSema(.eq, pt))) { 15521 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; 15522 } 15523 } 15524 if (maybe_rhs_val) |rhs_val| { 15525 if (!rhs_val.isUndef(zcu) and (try rhs_val.compareAllWithZeroSema(.eq, pt))) { 15526 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15527 } 15528 } 15529 if (maybe_lhs_val) |lhs_val| { 15530 if (maybe_rhs_val) |rhs_val| { 15531 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) { 15532 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15533 } 15534 15535 const result = try arith.addWithOverflow(sema, dest_ty, lhs_val, rhs_val); 15536 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15537 } 15538 } 15539 }, 15540 .sub_with_overflow => { 15541 // If the rhs is zero, then the result is lhs and no overflow occured. 15542 // Otherwise, if either result is undefined, both results are undefined. 15543 if (maybe_rhs_val) |rhs_val| { 15544 if (rhs_val.isUndef(zcu)) { 15545 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15546 } else if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { 15547 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15548 } else if (maybe_lhs_val) |lhs_val| { 15549 if (lhs_val.isUndef(zcu)) { 15550 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15551 } 15552 15553 const result = try arith.subWithOverflow(sema, dest_ty, lhs_val, rhs_val); 15554 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15555 } 15556 } 15557 }, 15558 .mul_with_overflow => { 15559 // If either of the arguments is zero, the result is zero and no overflow occured. 15560 // If either of the arguments is one, the result is the other and no overflow occured. 15561 // Otherwise, if either of the arguments is undefined, both results are undefined. 15562 const scalar_one = try pt.intValue(dest_ty.scalarType(zcu), 1); 15563 if (maybe_lhs_val) |lhs_val| { 15564 if (!lhs_val.isUndef(zcu)) { 15565 if (try lhs_val.compareAllWithZeroSema(.eq, pt)) { 15566 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15567 } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { 15568 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; 15569 } 15570 } 15571 } 15572 15573 if (maybe_rhs_val) |rhs_val| { 15574 if (!rhs_val.isUndef(zcu)) { 15575 if (try rhs_val.compareAllWithZeroSema(.eq, pt)) { 15576 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; 15577 } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { 15578 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15579 } 15580 } 15581 } 15582 15583 if (maybe_lhs_val) |lhs_val| { 15584 if (maybe_rhs_val) |rhs_val| { 15585 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) { 15586 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15587 } 15588 15589 const result = try arith.mulWithOverflow(sema, dest_ty, lhs_val, rhs_val); 15590 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15591 } 15592 } 15593 }, 15594 .shl_with_overflow => { 15595 // If lhs is zero, the result is zero and no overflow occurred. 15596 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred. 15597 // Oterhwise if either of the arguments is undefined, both results are undefined. 15598 if (maybe_lhs_val) |lhs_val| { 15599 if (!lhs_val.isUndef(zcu) and (try lhs_val.compareAllWithZeroSema(.eq, pt))) { 15600 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15601 } 15602 } 15603 if (maybe_rhs_val) |rhs_val| { 15604 if (!rhs_val.isUndef(zcu) and (try rhs_val.compareAllWithZeroSema(.eq, pt))) { 15605 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; 15606 } 15607 } 15608 if (maybe_lhs_val) |lhs_val| { 15609 if (maybe_rhs_val) |rhs_val| { 15610 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) { 15611 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15612 } 15613 15614 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, pt); 15615 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15616 } 15617 } 15618 }, 15619 else => unreachable, 15620 } 15621 15622 const air_tag: Air.Inst.Tag = switch (zir_tag) { 15623 .add_with_overflow => .add_with_overflow, 15624 .mul_with_overflow => .mul_with_overflow, 15625 .sub_with_overflow => .sub_with_overflow, 15626 .shl_with_overflow => .shl_with_overflow, 15627 else => unreachable, 15628 }; 15629 15630 const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src; 15631 try sema.requireRuntimeBlock(block, src, runtime_src); 15632 15633 return block.addInst(.{ 15634 .tag = air_tag, 15635 .data = .{ .ty_pl = .{ 15636 .ty = Air.internedToRef(tuple_ty.toIntern()), 15637 .payload = try block.sema.addExtra(Air.Bin{ 15638 .lhs = lhs, 15639 .rhs = rhs, 15640 }), 15641 } }, 15642 }); 15643 }; 15644 15645 if (result.inst != .none) { 15646 if (try sema.resolveValue(result.inst)) |some| { 15647 result.wrapped = some; 15648 result.inst = .none; 15649 } 15650 } 15651 15652 if (result.inst == .none) { 15653 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 15654 .ty = tuple_ty.toIntern(), 15655 .storage = .{ .elems = &.{ 15656 result.wrapped.toIntern(), 15657 result.overflow_bit.toIntern(), 15658 } }, 15659 } }))); 15660 } 15661 15662 const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2); 15663 element_refs[0] = result.inst; 15664 element_refs[1] = Air.internedToRef(result.overflow_bit.toIntern()); 15665 return block.addAggregateInit(tuple_ty, element_refs); 15666 } 15667 15668 fn splat(sema: *Sema, ty: Type, val: Value) !Value { 15669 const pt = sema.pt; 15670 const zcu = pt.zcu; 15671 if (ty.zigTypeTag(zcu) != .vector) return val; 15672 const repeated = try pt.intern(.{ .aggregate = .{ 15673 .ty = ty.toIntern(), 15674 .storage = .{ .repeated_elem = val.toIntern() }, 15675 } }); 15676 return Value.fromInterned(repeated); 15677 } 15678 15679 fn analyzeArithmetic( 15680 sema: *Sema, 15681 block: *Block, 15682 /// TODO performance investigation: make this comptime? 15683 zir_tag: Zir.Inst.Tag, 15684 lhs: Air.Inst.Ref, 15685 rhs: Air.Inst.Ref, 15686 src: LazySrcLoc, 15687 lhs_src: LazySrcLoc, 15688 rhs_src: LazySrcLoc, 15689 want_safety: bool, 15690 ) CompileError!Air.Inst.Ref { 15691 const pt = sema.pt; 15692 const zcu = pt.zcu; 15693 const lhs_ty = sema.typeOf(lhs); 15694 const rhs_ty = sema.typeOf(rhs); 15695 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 15696 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 15697 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15698 15699 if (lhs_zig_ty_tag == .pointer) { 15700 if (rhs_zig_ty_tag == .pointer) { 15701 if (lhs_ty.ptrSize(zcu) != .slice and rhs_ty.ptrSize(zcu) != .slice) { 15702 if (zir_tag != .sub) { 15703 return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction"); 15704 } 15705 if (!lhs_ty.elemType2(zcu).eql(rhs_ty.elemType2(zcu), zcu)) { 15706 return sema.fail(block, src, "incompatible pointer arithmetic operands '{f}' and '{f}'", .{ 15707 lhs_ty.fmt(pt), rhs_ty.fmt(pt), 15708 }); 15709 } 15710 15711 const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu); 15712 if (elem_size == 0) { 15713 return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{ 15714 lhs_ty.elemType2(zcu).fmt(pt), 15715 }); 15716 } 15717 15718 const runtime_src = runtime_src: { 15719 if (try sema.resolveValue(lhs)) |lhs_value| { 15720 if (try sema.resolveValue(rhs)) |rhs_value| { 15721 const lhs_ptr = switch (zcu.intern_pool.indexToKey(lhs_value.toIntern())) { 15722 .undef => return sema.failWithUseOfUndef(block, lhs_src), 15723 .ptr => |ptr| ptr, 15724 else => unreachable, 15725 }; 15726 const rhs_ptr = switch (zcu.intern_pool.indexToKey(rhs_value.toIntern())) { 15727 .undef => return sema.failWithUseOfUndef(block, rhs_src), 15728 .ptr => |ptr| ptr, 15729 else => unreachable, 15730 }; 15731 // Make sure the pointers point to the same data. 15732 if (!lhs_ptr.base_addr.eql(rhs_ptr.base_addr)) break :runtime_src src; 15733 const address = std.math.sub(u64, lhs_ptr.byte_offset, rhs_ptr.byte_offset) catch 15734 return sema.fail(block, src, "operation results in overflow", .{}); 15735 const result = address / elem_size; 15736 return try pt.intRef(.usize, result); 15737 } else { 15738 break :runtime_src lhs_src; 15739 } 15740 } else { 15741 break :runtime_src rhs_src; 15742 } 15743 }; 15744 15745 try sema.requireRuntimeBlock(block, src, runtime_src); 15746 try sema.checkLogicalPtrOperation(block, src, lhs_ty); 15747 try sema.checkLogicalPtrOperation(block, src, rhs_ty); 15748 const lhs_int = try block.addBitCast(.usize, lhs); 15749 const rhs_int = try block.addBitCast(.usize, rhs); 15750 const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int); 15751 return try block.addBinOp(.div_exact, address, try pt.intRef(.usize, elem_size)); 15752 } 15753 } else { 15754 switch (lhs_ty.ptrSize(zcu)) { 15755 .one, .slice => {}, 15756 .many, .c => { 15757 const air_tag: Air.Inst.Tag = switch (zir_tag) { 15758 .add => .ptr_add, 15759 .sub => .ptr_sub, 15760 else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), 15761 }; 15762 15763 if (!try lhs_ty.elemType2(zcu).hasRuntimeBitsSema(pt)) { 15764 return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{ 15765 lhs_ty.elemType2(zcu).fmt(pt), 15766 }); 15767 } 15768 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); 15769 }, 15770 } 15771 } 15772 } 15773 15774 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ 15775 .override = &.{ lhs_src, rhs_src }, 15776 }); 15777 15778 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15779 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15780 15781 const scalar_type = resolved_type.scalarType(zcu); 15782 const scalar_tag = scalar_type.zigTypeTag(zcu); 15783 15784 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag); 15785 15786 // The rules we'll implement below are as follows: 15787 // 15788 // * If both operands are comptime-known, we call the corresponding function in `arith` to get 15789 // the comptime-known result. Inputs which would be IB at runtime are compile errors. 15790 // 15791 // * Otherwise, if one operand is comptime-known `undefined`, we trigger a compile error if this 15792 // operator can ever possibly trigger IB, or otherwise return comptime-known `undefined`. 15793 // 15794 // * No other comptime operand detemines a comptime result; e.g. `0 * x` isn't always `0` because 15795 // of `undefined`. Therefore, the remaining cases all become runtime operations. 15796 15797 const is_int = switch (scalar_tag) { 15798 .int, .comptime_int => true, 15799 .float, .comptime_float => false, 15800 else => unreachable, 15801 }; 15802 15803 const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs); 15804 const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs); 15805 15806 if (maybe_lhs_val) |lhs_val| { 15807 if (maybe_rhs_val) |rhs_val| { 15808 const result_val = switch (zir_tag) { 15809 .add, .add_unsafe => try arith.add(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), 15810 .addwrap => try arith.addWrap(sema, resolved_type, lhs_val, rhs_val), 15811 .add_sat => try arith.addSat(sema, resolved_type, lhs_val, rhs_val), 15812 .sub => try arith.sub(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), 15813 .subwrap => try arith.subWrap(sema, resolved_type, lhs_val, rhs_val), 15814 .sub_sat => try arith.subSat(sema, resolved_type, lhs_val, rhs_val), 15815 .mul => try arith.mul(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), 15816 .mulwrap => try arith.mulWrap(sema, resolved_type, lhs_val, rhs_val), 15817 .mul_sat => try arith.mulSat(sema, resolved_type, lhs_val, rhs_val), 15818 else => unreachable, 15819 }; 15820 return Air.internedToRef(result_val.toIntern()); 15821 } 15822 } 15823 15824 const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag, const allow_undef: bool = switch (zir_tag) { 15825 .add, .add_unsafe => .{ if (block.float_mode == .optimized) .add_optimized else .add, .add_safe, !is_int }, 15826 .addwrap => .{ .add_wrap, .add_wrap, true }, 15827 .add_sat => .{ .add_sat, .add_sat, true }, 15828 .sub => .{ if (block.float_mode == .optimized) .sub_optimized else .sub, .sub_safe, !is_int }, 15829 .subwrap => .{ .sub_wrap, .sub_wrap, true }, 15830 .sub_sat => .{ .sub_sat, .sub_sat, true }, 15831 .mul => .{ if (block.float_mode == .optimized) .mul_optimized else .mul, .mul_safe, !is_int }, 15832 .mulwrap => .{ .mul_wrap, .mul_wrap, true }, 15833 .mul_sat => .{ .mul_sat, .mul_sat, true }, 15834 else => unreachable, 15835 }; 15836 15837 if (allow_undef) { 15838 if (maybe_lhs_val) |lhs_val| { 15839 if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15840 } 15841 if (maybe_rhs_val) |rhs_val| { 15842 if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type); 15843 } 15844 } else { 15845 if (maybe_lhs_val) |lhs_val| { 15846 if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src); 15847 } 15848 if (maybe_rhs_val) |rhs_val| { 15849 if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src); 15850 } 15851 } 15852 15853 if (block.wantSafety() and want_safety and scalar_tag == .int) { 15854 if (air_tag != air_tag_safe) try sema.preparePanicId(src, .integer_overflow); 15855 return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs); 15856 } 15857 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15858 } 15859 15860 fn analyzePtrArithmetic( 15861 sema: *Sema, 15862 block: *Block, 15863 op_src: LazySrcLoc, 15864 ptr: Air.Inst.Ref, 15865 uncasted_offset: Air.Inst.Ref, 15866 air_tag: Air.Inst.Tag, 15867 ptr_src: LazySrcLoc, 15868 offset_src: LazySrcLoc, 15869 ) CompileError!Air.Inst.Ref { 15870 // TODO if the operand is comptime-known to be negative, or is a negative int, 15871 // coerce to isize instead of usize. 15872 const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src); 15873 const pt = sema.pt; 15874 const zcu = pt.zcu; 15875 const opt_ptr_val = try sema.resolveValue(ptr); 15876 const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset); 15877 const ptr_ty = sema.typeOf(ptr); 15878 const ptr_info = ptr_ty.ptrInfo(zcu); 15879 assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c); 15880 15881 const new_ptr_ty = t: { 15882 // Calculate the new pointer alignment. 15883 // This code is duplicated in `Type.elemPtrType`. 15884 if (ptr_info.flags.alignment == .none) { 15885 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. 15886 break :t ptr_ty; 15887 } 15888 // If the addend is not a comptime-known value we can still count on 15889 // it being a multiple of the type size. 15890 const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt); 15891 const addend = if (opt_off_val) |off_val| a: { 15892 const off_int = try sema.usizeCast(block, offset_src, try off_val.toUnsignedIntSema(pt)); 15893 break :a elem_size * off_int; 15894 } else elem_size; 15895 15896 // The resulting pointer is aligned to the lcd between the offset (an 15897 // arbitrary number) and the alignment factor (always a power of two, 15898 // non zero). 15899 const new_align: Alignment = @enumFromInt(@min( 15900 @ctz(addend), 15901 @intFromEnum(ptr_info.flags.alignment), 15902 )); 15903 assert(new_align != .none); 15904 15905 break :t try pt.ptrTypeSema(.{ 15906 .child = ptr_info.child, 15907 .sentinel = ptr_info.sentinel, 15908 .flags = .{ 15909 .size = ptr_info.flags.size, 15910 .alignment = new_align, 15911 .is_const = ptr_info.flags.is_const, 15912 .is_volatile = ptr_info.flags.is_volatile, 15913 .is_allowzero = ptr_info.flags.is_allowzero, 15914 .address_space = ptr_info.flags.address_space, 15915 }, 15916 }); 15917 }; 15918 15919 const runtime_src = rs: { 15920 if (opt_ptr_val) |ptr_val| { 15921 if (opt_off_val) |offset_val| { 15922 if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty); 15923 15924 const offset_int = try sema.usizeCast(block, offset_src, try offset_val.toUnsignedIntSema(pt)); 15925 if (offset_int == 0) return ptr; 15926 if (air_tag == .ptr_sub) { 15927 const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt); 15928 const new_ptr_val = try sema.ptrSubtract(block, op_src, ptr_val, offset_int * elem_size, new_ptr_ty); 15929 return Air.internedToRef(new_ptr_val.toIntern()); 15930 } else { 15931 const new_ptr_val = try pt.getCoerced(try ptr_val.ptrElem(offset_int, pt), new_ptr_ty); 15932 return Air.internedToRef(new_ptr_val.toIntern()); 15933 } 15934 } else break :rs offset_src; 15935 } else break :rs ptr_src; 15936 }; 15937 15938 try sema.requireRuntimeBlock(block, op_src, runtime_src); 15939 try sema.checkLogicalPtrOperation(block, op_src, ptr_ty); 15940 15941 return block.addInst(.{ 15942 .tag = air_tag, 15943 .data = .{ .ty_pl = .{ 15944 .ty = Air.internedToRef(new_ptr_ty.toIntern()), 15945 .payload = try sema.addExtra(Air.Bin{ 15946 .lhs = ptr, 15947 .rhs = offset, 15948 }), 15949 } }, 15950 }); 15951 } 15952 15953 fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15954 const tracy = trace(@src()); 15955 defer tracy.end(); 15956 15957 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 15958 const src = block.nodeOffset(inst_data.src_node); 15959 const ptr_src = src; // TODO better source location 15960 const ptr = try sema.resolveInst(inst_data.operand); 15961 return sema.analyzeLoad(block, src, ptr, ptr_src); 15962 } 15963 15964 fn zirAsm( 15965 sema: *Sema, 15966 block: *Block, 15967 extended: Zir.Inst.Extended.InstData, 15968 tmpl_is_expr: bool, 15969 ) CompileError!Air.Inst.Ref { 15970 const tracy = trace(@src()); 15971 defer tracy.end(); 15972 15973 const pt = sema.pt; 15974 const zcu = pt.zcu; 15975 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); 15976 const src = block.nodeOffset(extra.data.src_node); 15977 const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node }); 15978 const small: Zir.Inst.Asm.Small = @bitCast(extended.small); 15979 const outputs_len = small.outputs_len; 15980 const inputs_len = small.inputs_len; 15981 const is_volatile = small.is_volatile; 15982 const is_global_assembly = sema.func_index == .none; 15983 const zir_tags = sema.code.instructions.items(.tag); 15984 15985 const asm_source: []const u8 = if (tmpl_is_expr) s: { 15986 const tmpl: Zir.Inst.Ref = @enumFromInt(@intFromEnum(extra.data.asm_source)); 15987 break :s try sema.resolveConstString(block, src, tmpl, .{ .simple = .inline_assembly_code }); 15988 } else sema.code.nullTerminatedString(extra.data.asm_source); 15989 15990 if (is_global_assembly) { 15991 if (outputs_len != 0) { 15992 return sema.fail(block, src, "module-level assembly does not support outputs", .{}); 15993 } 15994 if (inputs_len != 0) { 15995 return sema.fail(block, src, "module-level assembly does not support inputs", .{}); 15996 } 15997 if (extra.data.clobbers != .none) { 15998 return sema.fail(block, src, "module-level assembly does not support clobbers", .{}); 15999 } 16000 if (is_volatile) { 16001 return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{}); 16002 } 16003 try zcu.addGlobalAssembly(sema.owner, asm_source); 16004 return .void_value; 16005 } 16006 16007 try sema.requireRuntimeBlock(block, src, null); 16008 16009 var extra_i = extra.end; 16010 var output_type_bits = extra.data.output_type_bits; 16011 var needed_capacity: usize = @typeInfo(Air.Asm).@"struct".fields.len + outputs_len + inputs_len; 16012 16013 const ConstraintName = struct { c: []const u8, n: []const u8 }; 16014 const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len); 16015 const outputs = try sema.arena.alloc(ConstraintName, outputs_len); 16016 var expr_ty = Air.Inst.Ref.void_type; 16017 16018 for (out_args, 0..) |*arg, out_i| { 16019 const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); 16020 extra_i = output.end; 16021 16022 const is_type = @as(u1, @truncate(output_type_bits)) != 0; 16023 output_type_bits >>= 1; 16024 16025 if (is_type) { 16026 // Indicate the output is the asm instruction return value. 16027 arg.* = .none; 16028 const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); 16029 expr_ty = Air.internedToRef(out_ty.toIntern()); 16030 } else { 16031 arg.* = try sema.resolveInst(output.data.operand); 16032 } 16033 16034 const constraint = sema.code.nullTerminatedString(output.data.constraint); 16035 const name = sema.code.nullTerminatedString(output.data.name); 16036 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; 16037 16038 if (output.data.operand.toIndex()) |index| { 16039 if (zir_tags[@intFromEnum(index)] == .ref) { 16040 // TODO: better error location; it would be even nicer if there were notes that pointed at the output and the variable definition 16041 return sema.fail(block, src, "asm cannot output to const local '{s}'", .{name}); 16042 } 16043 } 16044 16045 outputs[out_i] = .{ .c = constraint, .n = name }; 16046 } 16047 16048 const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len); 16049 const inputs = try sema.arena.alloc(ConstraintName, inputs_len); 16050 16051 for (args, 0..) |*arg, arg_i| { 16052 const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); 16053 extra_i = input.end; 16054 16055 const uncasted_arg = try sema.resolveInst(input.data.operand); 16056 const uncasted_arg_ty = sema.typeOf(uncasted_arg); 16057 switch (uncasted_arg_ty.zigTypeTag(zcu)) { 16058 .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src), 16059 .comptime_float => arg.* = try sema.coerce(block, .f64, uncasted_arg, src), 16060 else => { 16061 arg.* = uncasted_arg; 16062 }, 16063 } 16064 16065 const constraint = sema.code.nullTerminatedString(input.data.constraint); 16066 const name = sema.code.nullTerminatedString(input.data.name); 16067 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; 16068 inputs[arg_i] = .{ .c = constraint, .n = name }; 16069 } 16070 16071 const clobbers = if (extra.data.clobbers == .none) empty: { 16072 const clobbers_ty = try sema.getBuiltinType(src, .@"assembly.Clobbers"); 16073 break :empty try sema.structInitEmpty(block, clobbers_ty, src, src); 16074 } else try sema.resolveInst(extra.data.clobbers); // Already coerced by AstGen. 16075 const clobbers_val = try sema.resolveConstDefinedValue(block, src, clobbers, .{ .simple = .clobber }); 16076 needed_capacity += asm_source.len / 4 + 1; 16077 16078 const gpa = sema.gpa; 16079 try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); 16080 const asm_air = try block.addInst(.{ 16081 .tag = .assembly, 16082 .data = .{ .ty_pl = .{ 16083 .ty = expr_ty, 16084 .payload = sema.addExtraAssumeCapacity(Air.Asm{ 16085 .source_len = @intCast(asm_source.len), 16086 .inputs_len = @intCast(args.len), 16087 .clobbers = clobbers_val.toIntern(), 16088 .flags = .{ 16089 .is_volatile = is_volatile, 16090 .outputs_len = outputs_len, 16091 }, 16092 }), 16093 } }, 16094 }); 16095 sema.appendRefsAssumeCapacity(out_args); 16096 sema.appendRefsAssumeCapacity(args); 16097 for (outputs) |o| { 16098 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16099 @memcpy(buffer[0..o.c.len], o.c); 16100 buffer[o.c.len] = 0; 16101 @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n); 16102 buffer[o.c.len + 1 + o.n.len] = 0; 16103 sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4; 16104 } 16105 for (inputs) |input| { 16106 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16107 @memcpy(buffer[0..input.c.len], input.c); 16108 buffer[input.c.len] = 0; 16109 @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n); 16110 buffer[input.c.len + 1 + input.n.len] = 0; 16111 sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; 16112 } 16113 { 16114 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16115 @memcpy(buffer[0..asm_source.len], asm_source); 16116 buffer[asm_source.len] = 0; 16117 sema.air_extra.items.len += asm_source.len / 4 + 1; 16118 } 16119 return asm_air; 16120 } 16121 16122 /// Only called for equality operators. See also `zirCmp`. 16123 fn zirCmpEq( 16124 sema: *Sema, 16125 block: *Block, 16126 inst: Zir.Inst.Index, 16127 op: std.math.CompareOperator, 16128 air_tag: Air.Inst.Tag, 16129 ) CompileError!Air.Inst.Ref { 16130 const tracy = trace(@src()); 16131 defer tracy.end(); 16132 16133 const pt = sema.pt; 16134 const zcu = pt.zcu; 16135 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 16136 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 16137 const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); 16138 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 16139 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 16140 const lhs = try sema.resolveInst(extra.lhs); 16141 const rhs = try sema.resolveInst(extra.rhs); 16142 16143 const lhs_ty = sema.typeOf(lhs); 16144 const rhs_ty = sema.typeOf(rhs); 16145 const lhs_ty_tag = lhs_ty.zigTypeTag(zcu); 16146 const rhs_ty_tag = rhs_ty.zigTypeTag(zcu); 16147 if (lhs_ty_tag == .null and rhs_ty_tag == .null) { 16148 // null == null, null != null 16149 return if (op == .eq) .bool_true else .bool_false; 16150 } 16151 16152 // comparing null with optionals 16153 if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) { 16154 return sema.analyzeIsNull(block, rhs, op == .neq); 16155 } 16156 if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) { 16157 return sema.analyzeIsNull(block, lhs, op == .neq); 16158 } 16159 16160 if (lhs_ty_tag == .null or rhs_ty_tag == .null) { 16161 const non_null_type = if (lhs_ty_tag == .null) rhs_ty else lhs_ty; 16162 return sema.fail(block, src, "comparison of '{f}' with null", .{non_null_type.fmt(pt)}); 16163 } 16164 16165 if (lhs_ty_tag == .@"union" and (rhs_ty_tag == .enum_literal or rhs_ty_tag == .@"enum")) { 16166 return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op); 16167 } 16168 if (rhs_ty_tag == .@"union" and (lhs_ty_tag == .enum_literal or lhs_ty_tag == .@"enum")) { 16169 return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op); 16170 } 16171 16172 if (lhs_ty_tag == .error_set and rhs_ty_tag == .error_set) { 16173 const runtime_src: LazySrcLoc = src: { 16174 if (try sema.resolveValue(lhs)) |lval| { 16175 if (try sema.resolveValue(rhs)) |rval| { 16176 if (lval.isUndef(zcu) or rval.isUndef(zcu)) return .undef_bool; 16177 const lkey = zcu.intern_pool.indexToKey(lval.toIntern()); 16178 const rkey = zcu.intern_pool.indexToKey(rval.toIntern()); 16179 return if ((lkey.err.name == rkey.err.name) == (op == .eq)) 16180 .bool_true 16181 else 16182 .bool_false; 16183 } else { 16184 break :src rhs_src; 16185 } 16186 } else { 16187 break :src lhs_src; 16188 } 16189 }; 16190 try sema.requireRuntimeBlock(block, src, runtime_src); 16191 return block.addBinOp(air_tag, lhs, rhs); 16192 } 16193 if (lhs_ty_tag == .type and rhs_ty_tag == .type) { 16194 const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); 16195 const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); 16196 return if (lhs_as_type.eql(rhs_as_type, zcu) == (op == .eq)) .bool_true else .bool_false; 16197 } 16198 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); 16199 } 16200 16201 fn analyzeCmpUnionTag( 16202 sema: *Sema, 16203 block: *Block, 16204 src: LazySrcLoc, 16205 un: Air.Inst.Ref, 16206 un_src: LazySrcLoc, 16207 tag: Air.Inst.Ref, 16208 tag_src: LazySrcLoc, 16209 op: std.math.CompareOperator, 16210 ) CompileError!Air.Inst.Ref { 16211 const pt = sema.pt; 16212 const zcu = pt.zcu; 16213 const union_ty = sema.typeOf(un); 16214 try union_ty.resolveFields(pt); 16215 const union_tag_ty = union_ty.unionTagType(zcu) orelse { 16216 const msg = msg: { 16217 const msg = try sema.errMsg(un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); 16218 errdefer msg.destroy(sema.gpa); 16219 try sema.errNote(union_ty.srcLoc(zcu), msg, "union '{f}' is not a tagged union", .{union_ty.fmt(pt)}); 16220 break :msg msg; 16221 }; 16222 return sema.failWithOwnedErrorMsg(block, msg); 16223 }; 16224 // Coerce both the union and the tag to the union's tag type, and then execute the 16225 // enum comparison codepath. 16226 const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); 16227 const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); 16228 16229 if (try sema.resolveValue(coerced_tag)) |enum_val| { 16230 if (enum_val.isUndef(zcu)) return .undef_bool; 16231 const field_ty = union_ty.unionFieldType(enum_val, zcu).?; 16232 if (field_ty.zigTypeTag(zcu) == .noreturn) { 16233 return .bool_false; 16234 } 16235 } 16236 16237 return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src); 16238 } 16239 16240 /// Only called for non-equality operators. See also `zirCmpEq`. 16241 fn zirCmp( 16242 sema: *Sema, 16243 block: *Block, 16244 inst: Zir.Inst.Index, 16245 op: std.math.CompareOperator, 16246 ) CompileError!Air.Inst.Ref { 16247 const tracy = trace(@src()); 16248 defer tracy.end(); 16249 16250 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 16251 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 16252 const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); 16253 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 16254 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 16255 const lhs = try sema.resolveInst(extra.lhs); 16256 const rhs = try sema.resolveInst(extra.rhs); 16257 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false); 16258 } 16259 16260 fn analyzeCmp( 16261 sema: *Sema, 16262 block: *Block, 16263 src: LazySrcLoc, 16264 lhs: Air.Inst.Ref, 16265 rhs: Air.Inst.Ref, 16266 op: std.math.CompareOperator, 16267 lhs_src: LazySrcLoc, 16268 rhs_src: LazySrcLoc, 16269 is_equality_cmp: bool, 16270 ) CompileError!Air.Inst.Ref { 16271 const pt = sema.pt; 16272 const zcu = pt.zcu; 16273 const lhs_ty = sema.typeOf(lhs); 16274 const rhs_ty = sema.typeOf(rhs); 16275 if (lhs_ty.zigTypeTag(zcu) != .optional and rhs_ty.zigTypeTag(zcu) != .optional) { 16276 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 16277 } 16278 16279 if (lhs_ty.zigTypeTag(zcu) == .vector and rhs_ty.zigTypeTag(zcu) == .vector) { 16280 return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src); 16281 } 16282 if (lhs_ty.isNumeric(zcu) and rhs_ty.isNumeric(zcu)) { 16283 // This operation allows any combination of integer and float types, regardless of the 16284 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for 16285 // numeric types. 16286 return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); 16287 } 16288 if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_union and rhs_ty.zigTypeTag(zcu) == .error_set) { 16289 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { 16290 if (lhs_val.errorUnionIsPayload(zcu)) return .bool_false; 16291 } 16292 const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs); 16293 return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src); 16294 } 16295 if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_set and rhs_ty.zigTypeTag(zcu) == .error_union) { 16296 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { 16297 if (rhs_val.errorUnionIsPayload(zcu)) return .bool_false; 16298 } 16299 const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs); 16300 return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src); 16301 } 16302 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 16303 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); 16304 if (!resolved_type.isSelfComparable(zcu, is_equality_cmp)) { 16305 return sema.fail(block, src, "operator {s} not allowed for type '{f}'", .{ 16306 compareOperatorName(op), resolved_type.fmt(pt), 16307 }); 16308 } 16309 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 16310 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 16311 return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src); 16312 } 16313 16314 fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { 16315 return switch (comp) { 16316 .lt => "<", 16317 .lte => "<=", 16318 .eq => "==", 16319 .gte => ">=", 16320 .gt => ">", 16321 .neq => "!=", 16322 }; 16323 } 16324 16325 fn cmpSelf( 16326 sema: *Sema, 16327 block: *Block, 16328 src: LazySrcLoc, 16329 casted_lhs: Air.Inst.Ref, 16330 casted_rhs: Air.Inst.Ref, 16331 op: std.math.CompareOperator, 16332 lhs_src: LazySrcLoc, 16333 rhs_src: LazySrcLoc, 16334 ) CompileError!Air.Inst.Ref { 16335 const pt = sema.pt; 16336 const zcu = pt.zcu; 16337 const resolved_type = sema.typeOf(casted_lhs); 16338 16339 const maybe_lhs_val = try sema.resolveValue(casted_lhs); 16340 const maybe_rhs_val = try sema.resolveValue(casted_rhs); 16341 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; 16342 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; 16343 16344 const runtime_src: LazySrcLoc = src: { 16345 if (maybe_lhs_val) |lhs_val| { 16346 if (maybe_rhs_val) |rhs_val| { 16347 if (resolved_type.zigTypeTag(zcu) == .vector) { 16348 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type); 16349 return Air.internedToRef(cmp_val.toIntern()); 16350 } 16351 16352 return if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type)) 16353 .bool_true 16354 else 16355 .bool_false; 16356 } else { 16357 if (resolved_type.zigTypeTag(zcu) == .bool) { 16358 // We can lower bool eq/neq more efficiently. 16359 return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src); 16360 } 16361 break :src rhs_src; 16362 } 16363 } else { 16364 // For bools, we still check the other operand, because we can lower 16365 // bool eq/neq more efficiently. 16366 if (resolved_type.zigTypeTag(zcu) == .bool) { 16367 if (maybe_rhs_val) |rhs_val| { 16368 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src); 16369 } 16370 } 16371 break :src lhs_src; 16372 } 16373 }; 16374 try sema.requireRuntimeBlock(block, src, runtime_src); 16375 if (resolved_type.zigTypeTag(zcu) == .vector) { 16376 return block.addCmpVector(casted_lhs, casted_rhs, op); 16377 } 16378 const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized); 16379 return block.addBinOp(tag, casted_lhs, casted_rhs); 16380 } 16381 16382 /// cmp_eq (x, false) => not(x) 16383 /// cmp_eq (x, true ) => x 16384 /// cmp_neq(x, false) => x 16385 /// cmp_neq(x, true ) => not(x) 16386 fn runtimeBoolCmp( 16387 sema: *Sema, 16388 block: *Block, 16389 src: LazySrcLoc, 16390 op: std.math.CompareOperator, 16391 lhs: Air.Inst.Ref, 16392 rhs: bool, 16393 runtime_src: LazySrcLoc, 16394 ) CompileError!Air.Inst.Ref { 16395 if ((op == .neq) == rhs) { 16396 try sema.requireRuntimeBlock(block, src, runtime_src); 16397 return block.addTyOp(.not, .bool, lhs); 16398 } else { 16399 return lhs; 16400 } 16401 } 16402 16403 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16404 const pt = sema.pt; 16405 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16406 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 16407 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 16408 switch (ty.zigTypeTag(pt.zcu)) { 16409 .@"fn", 16410 .noreturn, 16411 .undefined, 16412 .null, 16413 .@"opaque", 16414 => return sema.fail(block, operand_src, "no size available for type '{f}'", .{ty.fmt(pt)}), 16415 16416 .type, 16417 .enum_literal, 16418 .comptime_float, 16419 .comptime_int, 16420 .void, 16421 => return .zero, 16422 16423 .bool, 16424 .int, 16425 .float, 16426 .pointer, 16427 .array, 16428 .@"struct", 16429 .optional, 16430 .error_union, 16431 .error_set, 16432 .@"enum", 16433 .@"union", 16434 .vector, 16435 .frame, 16436 .@"anyframe", 16437 => {}, 16438 } 16439 const val = try ty.abiSizeLazy(pt); 16440 return Air.internedToRef(val.toIntern()); 16441 } 16442 16443 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16444 const pt = sema.pt; 16445 const zcu = pt.zcu; 16446 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16447 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 16448 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); 16449 switch (operand_ty.zigTypeTag(zcu)) { 16450 .@"fn", 16451 .noreturn, 16452 .undefined, 16453 .null, 16454 .@"opaque", 16455 => return sema.fail(block, operand_src, "no size available for type '{f}'", .{operand_ty.fmt(pt)}), 16456 16457 .type, 16458 .enum_literal, 16459 .comptime_float, 16460 .comptime_int, 16461 .void, 16462 => return .zero, 16463 16464 .bool, 16465 .int, 16466 .float, 16467 .pointer, 16468 .array, 16469 .@"struct", 16470 .optional, 16471 .error_union, 16472 .error_set, 16473 .@"enum", 16474 .@"union", 16475 .vector, 16476 .frame, 16477 .@"anyframe", 16478 => {}, 16479 } 16480 const bit_size = try operand_ty.bitSizeSema(pt); 16481 return pt.intRef(.comptime_int, bit_size); 16482 } 16483 16484 fn zirThis( 16485 sema: *Sema, 16486 block: *Block, 16487 extended: Zir.Inst.Extended.InstData, 16488 ) CompileError!Air.Inst.Ref { 16489 _ = extended; 16490 const pt = sema.pt; 16491 const zcu = pt.zcu; 16492 const namespace = pt.zcu.namespacePtr(block.namespace); 16493 16494 switch (pt.zcu.intern_pool.indexToKey(namespace.owner_type)) { 16495 .opaque_type => { 16496 // Opaque types are never outdated since they don't undergo type resolution, so nothing to do! 16497 return Air.internedToRef(namespace.owner_type); 16498 }, 16499 .struct_type, .union_type => { 16500 const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); 16501 try sema.declareDependency(.{ .interned = new_ty }); 16502 return Air.internedToRef(new_ty); 16503 }, 16504 .enum_type => { 16505 const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type); 16506 try sema.declareDependency(.{ .interned = new_ty }); 16507 // Since this is an enum, it has to be resolved immediately. 16508 // `ensureTypeUpToDate` has resolved the new type if necessary. 16509 // We just need to check for resolution failures. 16510 const ty_unit: AnalUnit = .wrap(.{ .type = new_ty }); 16511 if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) { 16512 return error.AnalysisFail; 16513 } 16514 return Air.internedToRef(new_ty); 16515 }, 16516 else => unreachable, 16517 } 16518 } 16519 16520 fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 16521 const pt = sema.pt; 16522 const zcu = pt.zcu; 16523 const ip = &zcu.intern_pool; 16524 const captures = Type.fromInterned(zcu.namespacePtr(block.namespace).owner_type).getCaptures(zcu); 16525 16526 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 16527 const src = block.nodeOffset(src_node); 16528 16529 const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) { 16530 .@"comptime" => |index| return Air.internedToRef(index), 16531 .runtime => |index| index, 16532 .nav_val => |nav| return sema.analyzeNavVal(block, src, nav), 16533 .nav_ref => |nav| return sema.analyzeNavRef(block, src, nav), 16534 }; 16535 16536 // The comptime case is handled already above. Runtime case below. 16537 16538 if (!block.is_typeof and sema.func_index == .none) { 16539 const msg = msg: { 16540 const name = name: { 16541 // TODO: we should probably store this name in the ZIR to avoid this complexity. 16542 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; 16543 const tree = file.getTree(zcu) catch |err| { 16544 // In this case we emit a warning + a less precise source location. 16545 log.warn("unable to load {f}: {s}", .{ 16546 file.path.fmt(zcu.comp), @errorName(err), 16547 }); 16548 break :name null; 16549 }; 16550 const node = src_node.toAbsolute(src_base_node); 16551 const token = tree.nodeMainToken(node); 16552 break :name tree.tokenSlice(token); 16553 }; 16554 16555 const msg = if (name) |some| 16556 try sema.errMsg(src, "'{s}' not accessible outside function scope", .{some}) 16557 else 16558 try sema.errMsg(src, "variable not accessible outside function scope", .{}); 16559 errdefer msg.destroy(sema.gpa); 16560 16561 // TODO add "declared here" note 16562 break :msg msg; 16563 }; 16564 return sema.failWithOwnedErrorMsg(block, msg); 16565 } 16566 16567 if (!block.is_typeof and !block.isComptime() and sema.func_index != .none) { 16568 const msg = msg: { 16569 const name = name: { 16570 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; 16571 const tree = file.getTree(zcu) catch |err| { 16572 // In this case we emit a warning + a less precise source location. 16573 log.warn("unable to load {f}: {s}", .{ 16574 file.path.fmt(zcu.comp), @errorName(err), 16575 }); 16576 break :name null; 16577 }; 16578 const node = src_node.toAbsolute(src_base_node); 16579 const token = tree.nodeMainToken(node); 16580 break :name tree.tokenSlice(token); 16581 }; 16582 16583 const msg = if (name) |some| 16584 try sema.errMsg(src, "'{s}' not accessible from inner function", .{some}) 16585 else 16586 try sema.errMsg(src, "variable not accessible from inner function", .{}); 16587 errdefer msg.destroy(sema.gpa); 16588 16589 try sema.errNote(block.nodeOffset(.zero), msg, "crossed function definition here", .{}); 16590 16591 // TODO add "declared here" note 16592 break :msg msg; 16593 }; 16594 return sema.failWithOwnedErrorMsg(block, msg); 16595 } 16596 16597 assert(block.is_typeof); 16598 // We need a dummy runtime instruction with the correct type. 16599 return block.addTy(.alloc, .fromInterned(capture_ty)); 16600 } 16601 16602 fn zirRetAddr( 16603 sema: *Sema, 16604 block: *Block, 16605 extended: Zir.Inst.Extended.InstData, 16606 ) CompileError!Air.Inst.Ref { 16607 _ = sema; 16608 _ = extended; 16609 if (block.isComptime()) { 16610 // TODO: we could give a meaningful lazy value here. #14938 16611 return .zero_usize; 16612 } else { 16613 return block.addNoOp(.ret_addr); 16614 } 16615 } 16616 16617 fn zirFrameAddress( 16618 sema: *Sema, 16619 block: *Block, 16620 extended: Zir.Inst.Extended.InstData, 16621 ) CompileError!Air.Inst.Ref { 16622 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 16623 const src = block.nodeOffset(src_node); 16624 try sema.requireRuntimeBlock(block, src, null); 16625 return try block.addNoOp(.frame_addr); 16626 } 16627 16628 fn zirBuiltinSrc( 16629 sema: *Sema, 16630 block: *Block, 16631 extended: Zir.Inst.Extended.InstData, 16632 ) CompileError!Air.Inst.Ref { 16633 const tracy = trace(@src()); 16634 defer tracy.end(); 16635 16636 const pt = sema.pt; 16637 const zcu = pt.zcu; 16638 const ip = &zcu.intern_pool; 16639 const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data; 16640 const fn_name = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).name; 16641 const gpa = sema.gpa; 16642 const file_scope = block.getFileScope(zcu); 16643 16644 const func_name_val = v: { 16645 const func_name_len = fn_name.length(ip); 16646 const array_ty = try pt.intern(.{ .array_type = .{ 16647 .len = func_name_len, 16648 .sentinel = .zero_u8, 16649 .child = .u8_type, 16650 } }); 16651 break :v try pt.intern(.{ .slice = .{ 16652 .ty = .slice_const_u8_sentinel_0_type, 16653 .ptr = try pt.intern(.{ .ptr = .{ 16654 .ty = .manyptr_const_u8_sentinel_0_type, 16655 .base_addr = .{ .uav = .{ 16656 .orig_ty = .slice_const_u8_sentinel_0_type, 16657 .val = try pt.intern(.{ .aggregate = .{ 16658 .ty = array_ty, 16659 .storage = .{ .bytes = fn_name.toString() }, 16660 } }), 16661 } }, 16662 .byte_offset = 0, 16663 } }), 16664 .len = (try pt.intValue(.usize, func_name_len)).toIntern(), 16665 } }); 16666 }; 16667 16668 const module_name_val = v: { 16669 const module_name = file_scope.mod.?.fully_qualified_name; 16670 const array_ty = try pt.intern(.{ .array_type = .{ 16671 .len = module_name.len, 16672 .sentinel = .zero_u8, 16673 .child = .u8_type, 16674 } }); 16675 break :v try pt.intern(.{ .slice = .{ 16676 .ty = .slice_const_u8_sentinel_0_type, 16677 .ptr = try pt.intern(.{ .ptr = .{ 16678 .ty = .manyptr_const_u8_sentinel_0_type, 16679 .base_addr = .{ .uav = .{ 16680 .orig_ty = .slice_const_u8_sentinel_0_type, 16681 .val = try pt.intern(.{ .aggregate = .{ 16682 .ty = array_ty, 16683 .storage = .{ 16684 .bytes = try ip.getOrPutString(gpa, pt.tid, module_name, .maybe_embedded_nulls), 16685 }, 16686 } }), 16687 } }, 16688 .byte_offset = 0, 16689 } }), 16690 .len = (try pt.intValue(.usize, module_name.len)).toIntern(), 16691 } }); 16692 }; 16693 16694 const file_name_val = v: { 16695 const file_name = file_scope.sub_file_path; 16696 const array_ty = try pt.intern(.{ .array_type = .{ 16697 .len = file_name.len, 16698 .sentinel = .zero_u8, 16699 .child = .u8_type, 16700 } }); 16701 break :v try pt.intern(.{ .slice = .{ 16702 .ty = .slice_const_u8_sentinel_0_type, 16703 .ptr = try pt.intern(.{ .ptr = .{ 16704 .ty = .manyptr_const_u8_sentinel_0_type, 16705 .base_addr = .{ .uav = .{ 16706 .orig_ty = .slice_const_u8_sentinel_0_type, 16707 .val = try pt.intern(.{ .aggregate = .{ 16708 .ty = array_ty, 16709 .storage = .{ 16710 .bytes = try ip.getOrPutString(gpa, pt.tid, file_name, .maybe_embedded_nulls), 16711 }, 16712 } }), 16713 } }, 16714 .byte_offset = 0, 16715 } }), 16716 .len = (try pt.intValue(.usize, file_name.len)).toIntern(), 16717 } }); 16718 }; 16719 16720 const src_loc_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .SourceLocation); 16721 const fields = .{ 16722 // module: [:0]const u8, 16723 module_name_val, 16724 // file: [:0]const u8, 16725 file_name_val, 16726 // fn_name: [:0]const u8, 16727 func_name_val, 16728 // line: u32, 16729 (try pt.intValue(.u32, extra.line + 1)).toIntern(), 16730 // column: u32, 16731 (try pt.intValue(.u32, extra.column + 1)).toIntern(), 16732 }; 16733 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 16734 .ty = src_loc_ty.toIntern(), 16735 .storage = .{ .elems = &fields }, 16736 } }))); 16737 } 16738 16739 fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16740 const pt = sema.pt; 16741 const zcu = pt.zcu; 16742 const gpa = sema.gpa; 16743 const ip = &zcu.intern_pool; 16744 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16745 const src = block.nodeOffset(inst_data.src_node); 16746 const ty = try sema.resolveType(block, src, inst_data.operand); 16747 const type_info_ty = try sema.getBuiltinType(src, .Type); 16748 const type_info_tag_ty = type_info_ty.unionTagType(zcu).?; 16749 16750 if (ty.typeDeclInst(zcu)) |type_decl_inst| { 16751 try sema.declareDependency(.{ .namespace = type_decl_inst }); 16752 } 16753 16754 switch (ty.zigTypeTag(zcu)) { 16755 .type, 16756 .void, 16757 .bool, 16758 .noreturn, 16759 .comptime_float, 16760 .comptime_int, 16761 .undefined, 16762 .null, 16763 .enum_literal, 16764 => |type_info_tag| return unionInitFromEnumTag(sema, block, src, type_info_ty, @intFromEnum(type_info_tag), .void_value), 16765 16766 .@"fn" => { 16767 const fn_info_ty = try sema.getBuiltinType(src, .@"Type.Fn"); 16768 const param_info_ty = try sema.getBuiltinType(src, .@"Type.Fn.Param"); 16769 16770 const func_ty_info = zcu.typeToFunc(ty).?; 16771 const param_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); 16772 for (param_vals, 0..) |*param_val, i| { 16773 const param_ty = func_ty_info.param_types.get(ip)[i]; 16774 const is_generic = param_ty == .generic_poison_type; 16775 const param_ty_val = try pt.intern(.{ .opt = .{ 16776 .ty = try pt.intern(.{ .opt_type = .type_type }), 16777 .val = if (is_generic) .none else param_ty, 16778 } }); 16779 16780 const is_noalias = blk: { 16781 const index = std.math.cast(u5, i) orelse break :blk false; 16782 break :blk @as(u1, @truncate(func_ty_info.noalias_bits >> index)) != 0; 16783 }; 16784 16785 const param_fields = .{ 16786 // is_generic: bool, 16787 Value.makeBool(is_generic).toIntern(), 16788 // is_noalias: bool, 16789 Value.makeBool(is_noalias).toIntern(), 16790 // type: ?type, 16791 param_ty_val, 16792 }; 16793 param_val.* = try pt.intern(.{ .aggregate = .{ 16794 .ty = param_info_ty.toIntern(), 16795 .storage = .{ .elems = ¶m_fields }, 16796 } }); 16797 } 16798 16799 const args_val = v: { 16800 const new_decl_ty = try pt.arrayType(.{ 16801 .len = param_vals.len, 16802 .child = param_info_ty.toIntern(), 16803 }); 16804 const new_decl_val = try pt.intern(.{ .aggregate = .{ 16805 .ty = new_decl_ty.toIntern(), 16806 .storage = .{ .elems = param_vals }, 16807 } }); 16808 const slice_ty = (try pt.ptrTypeSema(.{ 16809 .child = param_info_ty.toIntern(), 16810 .flags = .{ 16811 .size = .slice, 16812 .is_const = true, 16813 }, 16814 })).toIntern(); 16815 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 16816 break :v try pt.intern(.{ .slice = .{ 16817 .ty = slice_ty, 16818 .ptr = try pt.intern(.{ .ptr = .{ 16819 .ty = manyptr_ty, 16820 .base_addr = .{ .uav = .{ 16821 .orig_ty = manyptr_ty, 16822 .val = new_decl_val, 16823 } }, 16824 .byte_offset = 0, 16825 } }), 16826 .len = (try pt.intValue(.usize, param_vals.len)).toIntern(), 16827 } }); 16828 }; 16829 16830 const ret_ty_opt = try pt.intern(.{ .opt = .{ 16831 .ty = try pt.intern(.{ .opt_type = .type_type }), 16832 .val = opt_val: { 16833 const ret_ty: Type = .fromInterned(func_ty_info.return_type); 16834 if (ret_ty.toIntern() == .generic_poison_type) break :opt_val .none; 16835 if (ret_ty.zigTypeTag(zcu) == .error_union) { 16836 if (ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) { 16837 break :opt_val .none; 16838 } 16839 } 16840 break :opt_val ret_ty.toIntern(); 16841 }, 16842 } }); 16843 16844 const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); 16845 const callconv_val = Value.uninterpret(func_ty_info.cc, callconv_ty, pt) catch |err| switch (err) { 16846 error.TypeMismatch => @panic("std.builtin is corrupt"), 16847 error.OutOfMemory => |e| return e, 16848 }; 16849 16850 const field_values: [5]InternPool.Index = .{ 16851 // calling_convention: CallingConvention, 16852 callconv_val.toIntern(), 16853 // is_generic: bool, 16854 Value.makeBool(func_ty_info.is_generic).toIntern(), 16855 // is_var_args: bool, 16856 Value.makeBool(func_ty_info.is_var_args).toIntern(), 16857 // return_type: ?type, 16858 ret_ty_opt, 16859 // args: []const Fn.Param, 16860 args_val, 16861 }; 16862 return Air.internedToRef((try pt.internUnion(.{ 16863 .ty = type_info_ty.toIntern(), 16864 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"fn"))).toIntern(), 16865 .val = try pt.intern(.{ .aggregate = .{ 16866 .ty = fn_info_ty.toIntern(), 16867 .storage = .{ .elems = &field_values }, 16868 } }), 16869 }))); 16870 }, 16871 .int => { 16872 const int_info_ty = try sema.getBuiltinType(src, .@"Type.Int"); 16873 const signedness_ty = try sema.getBuiltinType(src, .Signedness); 16874 const info = ty.intInfo(zcu); 16875 const field_values = .{ 16876 // signedness: Signedness, 16877 (try pt.enumValueFieldIndex(signedness_ty, @intFromEnum(info.signedness))).toIntern(), 16878 // bits: u16, 16879 (try pt.intValue(.u16, info.bits)).toIntern(), 16880 }; 16881 return Air.internedToRef((try pt.internUnion(.{ 16882 .ty = type_info_ty.toIntern(), 16883 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.int))).toIntern(), 16884 .val = try pt.intern(.{ .aggregate = .{ 16885 .ty = int_info_ty.toIntern(), 16886 .storage = .{ .elems = &field_values }, 16887 } }), 16888 }))); 16889 }, 16890 .float => { 16891 const float_info_ty = try sema.getBuiltinType(src, .@"Type.Float"); 16892 16893 const field_vals = .{ 16894 // bits: u16, 16895 (try pt.intValue(.u16, ty.bitSize(zcu))).toIntern(), 16896 }; 16897 return Air.internedToRef((try pt.internUnion(.{ 16898 .ty = type_info_ty.toIntern(), 16899 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.float))).toIntern(), 16900 .val = try pt.intern(.{ .aggregate = .{ 16901 .ty = float_info_ty.toIntern(), 16902 .storage = .{ .elems = &field_vals }, 16903 } }), 16904 }))); 16905 }, 16906 .pointer => { 16907 const info = ty.ptrInfo(zcu); 16908 const alignment = if (info.flags.alignment.toByteUnits()) |alignment| 16909 try pt.intValue(.comptime_int, alignment) 16910 else 16911 try Type.fromInterned(info.child).lazyAbiAlignment(pt); 16912 16913 const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace); 16914 const pointer_ty = try sema.getBuiltinType(src, .@"Type.Pointer"); 16915 const ptr_size_ty = try sema.getBuiltinType(src, .@"Type.Pointer.Size"); 16916 16917 const field_values = .{ 16918 // size: Size, 16919 (try pt.enumValueFieldIndex(ptr_size_ty, @intFromEnum(info.flags.size))).toIntern(), 16920 // is_const: bool, 16921 Value.makeBool(info.flags.is_const).toIntern(), 16922 // is_volatile: bool, 16923 Value.makeBool(info.flags.is_volatile).toIntern(), 16924 // alignment: comptime_int, 16925 alignment.toIntern(), 16926 // address_space: AddressSpace 16927 (try pt.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).toIntern(), 16928 // child: type, 16929 info.child, 16930 // is_allowzero: bool, 16931 Value.makeBool(info.flags.is_allowzero).toIntern(), 16932 // sentinel: ?*const anyopaque, 16933 (try sema.optRefValue(switch (info.sentinel) { 16934 .none => null, 16935 else => Value.fromInterned(info.sentinel), 16936 })).toIntern(), 16937 }; 16938 return Air.internedToRef((try pt.internUnion(.{ 16939 .ty = type_info_ty.toIntern(), 16940 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.pointer))).toIntern(), 16941 .val = try pt.intern(.{ .aggregate = .{ 16942 .ty = pointer_ty.toIntern(), 16943 .storage = .{ .elems = &field_values }, 16944 } }), 16945 }))); 16946 }, 16947 .array => { 16948 const array_field_ty = try sema.getBuiltinType(src, .@"Type.Array"); 16949 16950 const info = ty.arrayInfo(zcu); 16951 const field_values = .{ 16952 // len: comptime_int, 16953 (try pt.intValue(.comptime_int, info.len)).toIntern(), 16954 // child: type, 16955 info.elem_type.toIntern(), 16956 // sentinel: ?*const anyopaque, 16957 (try sema.optRefValue(info.sentinel)).toIntern(), 16958 }; 16959 return Air.internedToRef((try pt.internUnion(.{ 16960 .ty = type_info_ty.toIntern(), 16961 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.array))).toIntern(), 16962 .val = try pt.intern(.{ .aggregate = .{ 16963 .ty = array_field_ty.toIntern(), 16964 .storage = .{ .elems = &field_values }, 16965 } }), 16966 }))); 16967 }, 16968 .vector => { 16969 const vector_field_ty = try sema.getBuiltinType(src, .@"Type.Vector"); 16970 16971 const info = ty.arrayInfo(zcu); 16972 const field_values = .{ 16973 // len: comptime_int, 16974 (try pt.intValue(.comptime_int, info.len)).toIntern(), 16975 // child: type, 16976 info.elem_type.toIntern(), 16977 }; 16978 return Air.internedToRef((try pt.internUnion(.{ 16979 .ty = type_info_ty.toIntern(), 16980 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.vector))).toIntern(), 16981 .val = try pt.intern(.{ .aggregate = .{ 16982 .ty = vector_field_ty.toIntern(), 16983 .storage = .{ .elems = &field_values }, 16984 } }), 16985 }))); 16986 }, 16987 .optional => { 16988 const optional_field_ty = try sema.getBuiltinType(src, .@"Type.Optional"); 16989 16990 const field_values = .{ 16991 // child: type, 16992 ty.optionalChild(zcu).toIntern(), 16993 }; 16994 return Air.internedToRef((try pt.internUnion(.{ 16995 .ty = type_info_ty.toIntern(), 16996 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.optional))).toIntern(), 16997 .val = try pt.intern(.{ .aggregate = .{ 16998 .ty = optional_field_ty.toIntern(), 16999 .storage = .{ .elems = &field_values }, 17000 } }), 17001 }))); 17002 }, 17003 .error_set => { 17004 // Get the Error type 17005 const error_field_ty = try sema.getBuiltinType(src, .@"Type.Error"); 17006 17007 // Build our list of Error values 17008 // Optional value is only null if anyerror 17009 // Value can be zero-length slice otherwise 17010 const error_field_vals = switch (try sema.resolveInferredErrorSetTy(block, src, ty.toIntern())) { 17011 .anyerror_type => null, 17012 else => |err_set_ty_index| blk: { 17013 const names = ip.indexToKey(err_set_ty_index).error_set_type.names; 17014 const vals = try sema.arena.alloc(InternPool.Index, names.len); 17015 for (vals, 0..) |*field_val, error_index| { 17016 const error_name = names.get(ip)[error_index]; 17017 const error_name_len = error_name.length(ip); 17018 const error_name_val = v: { 17019 const new_decl_ty = try pt.arrayType(.{ 17020 .len = error_name_len, 17021 .sentinel = .zero_u8, 17022 .child = .u8_type, 17023 }); 17024 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17025 .ty = new_decl_ty.toIntern(), 17026 .storage = .{ .bytes = error_name.toString() }, 17027 } }); 17028 break :v try pt.intern(.{ .slice = .{ 17029 .ty = .slice_const_u8_sentinel_0_type, 17030 .ptr = try pt.intern(.{ .ptr = .{ 17031 .ty = .manyptr_const_u8_sentinel_0_type, 17032 .base_addr = .{ .uav = .{ 17033 .val = new_decl_val, 17034 .orig_ty = .slice_const_u8_sentinel_0_type, 17035 } }, 17036 .byte_offset = 0, 17037 } }), 17038 .len = (try pt.intValue(.usize, error_name_len)).toIntern(), 17039 } }); 17040 }; 17041 17042 const error_field_fields = .{ 17043 // name: [:0]const u8, 17044 error_name_val, 17045 }; 17046 field_val.* = try pt.intern(.{ .aggregate = .{ 17047 .ty = error_field_ty.toIntern(), 17048 .storage = .{ .elems = &error_field_fields }, 17049 } }); 17050 } 17051 17052 break :blk vals; 17053 }, 17054 }; 17055 17056 // Build our ?[]const Error value 17057 const slice_errors_ty = try pt.ptrTypeSema(.{ 17058 .child = error_field_ty.toIntern(), 17059 .flags = .{ 17060 .size = .slice, 17061 .is_const = true, 17062 }, 17063 }); 17064 const opt_slice_errors_ty = try pt.optionalType(slice_errors_ty.toIntern()); 17065 const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: { 17066 const array_errors_ty = try pt.arrayType(.{ 17067 .len = vals.len, 17068 .child = error_field_ty.toIntern(), 17069 }); 17070 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17071 .ty = array_errors_ty.toIntern(), 17072 .storage = .{ .elems = vals }, 17073 } }); 17074 const manyptr_errors_ty = slice_errors_ty.slicePtrFieldType(zcu).toIntern(); 17075 break :v try pt.intern(.{ .slice = .{ 17076 .ty = slice_errors_ty.toIntern(), 17077 .ptr = try pt.intern(.{ .ptr = .{ 17078 .ty = manyptr_errors_ty, 17079 .base_addr = .{ .uav = .{ 17080 .orig_ty = manyptr_errors_ty, 17081 .val = new_decl_val, 17082 } }, 17083 .byte_offset = 0, 17084 } }), 17085 .len = (try pt.intValue(.usize, vals.len)).toIntern(), 17086 } }); 17087 } else .none; 17088 const errors_val = try pt.intern(.{ .opt = .{ 17089 .ty = opt_slice_errors_ty.toIntern(), 17090 .val = errors_payload_val, 17091 } }); 17092 17093 // Construct Type{ .error_set = errors_val } 17094 return Air.internedToRef((try pt.internUnion(.{ 17095 .ty = type_info_ty.toIntern(), 17096 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.error_set))).toIntern(), 17097 .val = errors_val, 17098 }))); 17099 }, 17100 .error_union => { 17101 const error_union_field_ty = try sema.getBuiltinType(src, .@"Type.ErrorUnion"); 17102 17103 const field_values = .{ 17104 // error_set: type, 17105 ty.errorUnionSet(zcu).toIntern(), 17106 // payload: type, 17107 ty.errorUnionPayload(zcu).toIntern(), 17108 }; 17109 return Air.internedToRef((try pt.internUnion(.{ 17110 .ty = type_info_ty.toIntern(), 17111 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.error_union))).toIntern(), 17112 .val = try pt.intern(.{ .aggregate = .{ 17113 .ty = error_union_field_ty.toIntern(), 17114 .storage = .{ .elems = &field_values }, 17115 } }), 17116 }))); 17117 }, 17118 .@"enum" => { 17119 const is_exhaustive = Value.makeBool(ip.loadEnumType(ty.toIntern()).tag_mode != .nonexhaustive); 17120 17121 const enum_field_ty = try sema.getBuiltinType(src, .@"Type.EnumField"); 17122 17123 const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len); 17124 for (enum_field_vals, 0..) |*field_val, tag_index| { 17125 const enum_type = ip.loadEnumType(ty.toIntern()); 17126 const value_val = if (enum_type.values.len > 0) 17127 try ip.getCoercedInts( 17128 zcu.gpa, 17129 pt.tid, 17130 ip.indexToKey(enum_type.values.get(ip)[tag_index]).int, 17131 .comptime_int_type, 17132 ) 17133 else 17134 (try pt.intValue(.comptime_int, tag_index)).toIntern(); 17135 17136 // TODO: write something like getCoercedInts to avoid needing to dupe 17137 const name_val = v: { 17138 const tag_name = enum_type.names.get(ip)[tag_index]; 17139 const tag_name_len = tag_name.length(ip); 17140 const new_decl_ty = try pt.arrayType(.{ 17141 .len = tag_name_len, 17142 .sentinel = .zero_u8, 17143 .child = .u8_type, 17144 }); 17145 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17146 .ty = new_decl_ty.toIntern(), 17147 .storage = .{ .bytes = tag_name.toString() }, 17148 } }); 17149 break :v try pt.intern(.{ .slice = .{ 17150 .ty = .slice_const_u8_sentinel_0_type, 17151 .ptr = try pt.intern(.{ .ptr = .{ 17152 .ty = .manyptr_const_u8_sentinel_0_type, 17153 .base_addr = .{ .uav = .{ 17154 .val = new_decl_val, 17155 .orig_ty = .slice_const_u8_sentinel_0_type, 17156 } }, 17157 .byte_offset = 0, 17158 } }), 17159 .len = (try pt.intValue(.usize, tag_name_len)).toIntern(), 17160 } }); 17161 }; 17162 17163 const enum_field_fields = .{ 17164 // name: [:0]const u8, 17165 name_val, 17166 // value: comptime_int, 17167 value_val, 17168 }; 17169 field_val.* = try pt.intern(.{ .aggregate = .{ 17170 .ty = enum_field_ty.toIntern(), 17171 .storage = .{ .elems = &enum_field_fields }, 17172 } }); 17173 } 17174 17175 const fields_val = v: { 17176 const fields_array_ty = try pt.arrayType(.{ 17177 .len = enum_field_vals.len, 17178 .child = enum_field_ty.toIntern(), 17179 }); 17180 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17181 .ty = fields_array_ty.toIntern(), 17182 .storage = .{ .elems = enum_field_vals }, 17183 } }); 17184 const slice_ty = (try pt.ptrTypeSema(.{ 17185 .child = enum_field_ty.toIntern(), 17186 .flags = .{ 17187 .size = .slice, 17188 .is_const = true, 17189 }, 17190 })).toIntern(); 17191 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17192 break :v try pt.intern(.{ .slice = .{ 17193 .ty = slice_ty, 17194 .ptr = try pt.intern(.{ .ptr = .{ 17195 .ty = manyptr_ty, 17196 .base_addr = .{ .uav = .{ 17197 .val = new_decl_val, 17198 .orig_ty = manyptr_ty, 17199 } }, 17200 .byte_offset = 0, 17201 } }), 17202 .len = (try pt.intValue(.usize, enum_field_vals.len)).toIntern(), 17203 } }); 17204 }; 17205 17206 const decls_val = try sema.typeInfoDecls(src, ip.loadEnumType(ty.toIntern()).namespace.toOptional()); 17207 17208 const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum"); 17209 17210 const field_values = .{ 17211 // tag_type: type, 17212 ip.loadEnumType(ty.toIntern()).tag_ty, 17213 // fields: []const EnumField, 17214 fields_val, 17215 // decls: []const Declaration, 17216 decls_val, 17217 // is_exhaustive: bool, 17218 is_exhaustive.toIntern(), 17219 }; 17220 return Air.internedToRef((try pt.internUnion(.{ 17221 .ty = type_info_ty.toIntern(), 17222 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"enum"))).toIntern(), 17223 .val = try pt.intern(.{ .aggregate = .{ 17224 .ty = type_enum_ty.toIntern(), 17225 .storage = .{ .elems = &field_values }, 17226 } }), 17227 }))); 17228 }, 17229 .@"union" => { 17230 const type_union_ty = try sema.getBuiltinType(src, .@"Type.Union"); 17231 const union_field_ty = try sema.getBuiltinType(src, .@"Type.UnionField"); 17232 17233 try ty.resolveLayout(pt); // Getting alignment requires type layout 17234 const union_obj = zcu.typeToUnion(ty).?; 17235 const tag_type = union_obj.loadTagType(ip); 17236 const layout = union_obj.flagsUnordered(ip).layout; 17237 17238 const union_field_vals = try gpa.alloc(InternPool.Index, tag_type.names.len); 17239 defer gpa.free(union_field_vals); 17240 17241 for (union_field_vals, 0..) |*field_val, field_index| { 17242 const name_val = v: { 17243 const field_name = tag_type.names.get(ip)[field_index]; 17244 const field_name_len = field_name.length(ip); 17245 const new_decl_ty = try pt.arrayType(.{ 17246 .len = field_name_len, 17247 .sentinel = .zero_u8, 17248 .child = .u8_type, 17249 }); 17250 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17251 .ty = new_decl_ty.toIntern(), 17252 .storage = .{ .bytes = field_name.toString() }, 17253 } }); 17254 break :v try pt.intern(.{ .slice = .{ 17255 .ty = .slice_const_u8_sentinel_0_type, 17256 .ptr = try pt.intern(.{ .ptr = .{ 17257 .ty = .manyptr_const_u8_sentinel_0_type, 17258 .base_addr = .{ .uav = .{ 17259 .val = new_decl_val, 17260 .orig_ty = .slice_const_u8_sentinel_0_type, 17261 } }, 17262 .byte_offset = 0, 17263 } }), 17264 .len = (try pt.intValue(.usize, field_name_len)).toIntern(), 17265 } }); 17266 }; 17267 17268 const alignment = switch (layout) { 17269 .auto, .@"extern" => try ty.fieldAlignmentSema(field_index, pt), 17270 .@"packed" => .none, 17271 }; 17272 17273 const field_ty = union_obj.field_types.get(ip)[field_index]; 17274 const union_field_fields = .{ 17275 // name: [:0]const u8, 17276 name_val, 17277 // type: type, 17278 field_ty, 17279 // alignment: comptime_int, 17280 (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(), 17281 }; 17282 field_val.* = try pt.intern(.{ .aggregate = .{ 17283 .ty = union_field_ty.toIntern(), 17284 .storage = .{ .elems = &union_field_fields }, 17285 } }); 17286 } 17287 17288 const fields_val = v: { 17289 const array_fields_ty = try pt.arrayType(.{ 17290 .len = union_field_vals.len, 17291 .child = union_field_ty.toIntern(), 17292 }); 17293 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17294 .ty = array_fields_ty.toIntern(), 17295 .storage = .{ .elems = union_field_vals }, 17296 } }); 17297 const slice_ty = (try pt.ptrTypeSema(.{ 17298 .child = union_field_ty.toIntern(), 17299 .flags = .{ 17300 .size = .slice, 17301 .is_const = true, 17302 }, 17303 })).toIntern(); 17304 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17305 break :v try pt.intern(.{ .slice = .{ 17306 .ty = slice_ty, 17307 .ptr = try pt.intern(.{ .ptr = .{ 17308 .ty = manyptr_ty, 17309 .base_addr = .{ .uav = .{ 17310 .orig_ty = manyptr_ty, 17311 .val = new_decl_val, 17312 } }, 17313 .byte_offset = 0, 17314 } }), 17315 .len = (try pt.intValue(.usize, union_field_vals.len)).toIntern(), 17316 } }); 17317 }; 17318 17319 const decls_val = try sema.typeInfoDecls(src, ty.getNamespaceIndex(zcu).toOptional()); 17320 17321 const enum_tag_ty_val = try pt.intern(.{ .opt = .{ 17322 .ty = (try pt.optionalType(.type_type)).toIntern(), 17323 .val = if (ty.unionTagType(zcu)) |tag_ty| tag_ty.toIntern() else .none, 17324 } }); 17325 17326 const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout"); 17327 17328 const field_values = .{ 17329 // layout: ContainerLayout, 17330 (try pt.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17331 17332 // tag_type: ?type, 17333 enum_tag_ty_val, 17334 // fields: []const UnionField, 17335 fields_val, 17336 // decls: []const Declaration, 17337 decls_val, 17338 }; 17339 return Air.internedToRef((try pt.internUnion(.{ 17340 .ty = type_info_ty.toIntern(), 17341 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"union"))).toIntern(), 17342 .val = try pt.intern(.{ .aggregate = .{ 17343 .ty = type_union_ty.toIntern(), 17344 .storage = .{ .elems = &field_values }, 17345 } }), 17346 }))); 17347 }, 17348 .@"struct" => { 17349 const type_struct_ty = try sema.getBuiltinType(src, .@"Type.Struct"); 17350 const struct_field_ty = try sema.getBuiltinType(src, .@"Type.StructField"); 17351 17352 try ty.resolveLayout(pt); // Getting alignment requires type layout 17353 17354 var struct_field_vals: []InternPool.Index = &.{}; 17355 defer gpa.free(struct_field_vals); 17356 fv: { 17357 const struct_type = switch (ip.indexToKey(ty.toIntern())) { 17358 .tuple_type => |tuple_type| { 17359 struct_field_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len); 17360 for (struct_field_vals, 0..) |*struct_field_val, field_index| { 17361 const field_ty = tuple_type.types.get(ip)[field_index]; 17362 const field_val = tuple_type.values.get(ip)[field_index]; 17363 const name_val = v: { 17364 const field_name = try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); 17365 const field_name_len = field_name.length(ip); 17366 const new_decl_ty = try pt.arrayType(.{ 17367 .len = field_name_len, 17368 .sentinel = .zero_u8, 17369 .child = .u8_type, 17370 }); 17371 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17372 .ty = new_decl_ty.toIntern(), 17373 .storage = .{ .bytes = field_name.toString() }, 17374 } }); 17375 break :v try pt.intern(.{ .slice = .{ 17376 .ty = .slice_const_u8_sentinel_0_type, 17377 .ptr = try pt.intern(.{ .ptr = .{ 17378 .ty = .manyptr_const_u8_sentinel_0_type, 17379 .base_addr = .{ .uav = .{ 17380 .val = new_decl_val, 17381 .orig_ty = .slice_const_u8_sentinel_0_type, 17382 } }, 17383 .byte_offset = 0, 17384 } }), 17385 .len = (try pt.intValue(.usize, field_name_len)).toIntern(), 17386 } }); 17387 }; 17388 17389 try Type.fromInterned(field_ty).resolveLayout(pt); 17390 17391 const is_comptime = field_val != .none; 17392 const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null; 17393 const default_val_ptr = try sema.optRefValue(opt_default_val); 17394 const struct_field_fields = .{ 17395 // name: [:0]const u8, 17396 name_val, 17397 // type: type, 17398 field_ty, 17399 // default_value: ?*const anyopaque, 17400 default_val_ptr.toIntern(), 17401 // is_comptime: bool, 17402 Value.makeBool(is_comptime).toIntern(), 17403 // alignment: comptime_int, 17404 (try pt.intValue(.comptime_int, Type.fromInterned(field_ty).abiAlignment(zcu).toByteUnits() orelse 0)).toIntern(), 17405 }; 17406 struct_field_val.* = try pt.intern(.{ .aggregate = .{ 17407 .ty = struct_field_ty.toIntern(), 17408 .storage = .{ .elems = &struct_field_fields }, 17409 } }); 17410 } 17411 break :fv; 17412 }, 17413 .struct_type => ip.loadStructType(ty.toIntern()), 17414 else => unreachable, 17415 }; 17416 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len); 17417 17418 try ty.resolveStructFieldInits(pt); 17419 17420 for (struct_field_vals, 0..) |*field_val, field_index| { 17421 const field_name = if (struct_type.fieldName(ip, field_index).unwrap()) |field_name| 17422 field_name 17423 else 17424 try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); 17425 const field_name_len = field_name.length(ip); 17426 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); 17427 const field_init = struct_type.fieldInit(ip, field_index); 17428 const field_is_comptime = struct_type.fieldIsComptime(ip, field_index); 17429 const name_val = v: { 17430 const new_decl_ty = try pt.arrayType(.{ 17431 .len = field_name_len, 17432 .sentinel = .zero_u8, 17433 .child = .u8_type, 17434 }); 17435 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17436 .ty = new_decl_ty.toIntern(), 17437 .storage = .{ .bytes = field_name.toString() }, 17438 } }); 17439 break :v try pt.intern(.{ .slice = .{ 17440 .ty = .slice_const_u8_sentinel_0_type, 17441 .ptr = try pt.intern(.{ .ptr = .{ 17442 .ty = .manyptr_const_u8_sentinel_0_type, 17443 .base_addr = .{ .uav = .{ 17444 .val = new_decl_val, 17445 .orig_ty = .slice_const_u8_sentinel_0_type, 17446 } }, 17447 .byte_offset = 0, 17448 } }), 17449 .len = (try pt.intValue(.usize, field_name_len)).toIntern(), 17450 } }); 17451 }; 17452 17453 const opt_default_val = if (field_init == .none) null else Value.fromInterned(field_init); 17454 const default_val_ptr = try sema.optRefValue(opt_default_val); 17455 const alignment = switch (struct_type.layout) { 17456 .@"packed" => .none, 17457 else => try field_ty.structFieldAlignmentSema( 17458 struct_type.fieldAlign(ip, field_index), 17459 struct_type.layout, 17460 pt, 17461 ), 17462 }; 17463 17464 const struct_field_fields = .{ 17465 // name: [:0]const u8, 17466 name_val, 17467 // type: type, 17468 field_ty.toIntern(), 17469 // default_value: ?*const anyopaque, 17470 default_val_ptr.toIntern(), 17471 // is_comptime: bool, 17472 Value.makeBool(field_is_comptime).toIntern(), 17473 // alignment: comptime_int, 17474 (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(), 17475 }; 17476 field_val.* = try pt.intern(.{ .aggregate = .{ 17477 .ty = struct_field_ty.toIntern(), 17478 .storage = .{ .elems = &struct_field_fields }, 17479 } }); 17480 } 17481 } 17482 17483 const fields_val = v: { 17484 const array_fields_ty = try pt.arrayType(.{ 17485 .len = struct_field_vals.len, 17486 .child = struct_field_ty.toIntern(), 17487 }); 17488 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17489 .ty = array_fields_ty.toIntern(), 17490 .storage = .{ .elems = struct_field_vals }, 17491 } }); 17492 const slice_ty = (try pt.ptrTypeSema(.{ 17493 .child = struct_field_ty.toIntern(), 17494 .flags = .{ 17495 .size = .slice, 17496 .is_const = true, 17497 }, 17498 })).toIntern(); 17499 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17500 break :v try pt.intern(.{ .slice = .{ 17501 .ty = slice_ty, 17502 .ptr = try pt.intern(.{ .ptr = .{ 17503 .ty = manyptr_ty, 17504 .base_addr = .{ .uav = .{ 17505 .orig_ty = manyptr_ty, 17506 .val = new_decl_val, 17507 } }, 17508 .byte_offset = 0, 17509 } }), 17510 .len = (try pt.intValue(.usize, struct_field_vals.len)).toIntern(), 17511 } }); 17512 }; 17513 17514 const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu)); 17515 17516 const backing_integer_val = try pt.intern(.{ .opt = .{ 17517 .ty = (try pt.optionalType(.type_type)).toIntern(), 17518 .val = if (zcu.typeToPackedStruct(ty)) |packed_struct| val: { 17519 assert(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)).isInt(zcu)); 17520 break :val packed_struct.backingIntTypeUnordered(ip); 17521 } else .none, 17522 } }); 17523 17524 const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout"); 17525 17526 const layout = ty.containerLayout(zcu); 17527 17528 const field_values = [_]InternPool.Index{ 17529 // layout: ContainerLayout, 17530 (try pt.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17531 // backing_integer: ?type, 17532 backing_integer_val, 17533 // fields: []const StructField, 17534 fields_val, 17535 // decls: []const Declaration, 17536 decls_val, 17537 // is_tuple: bool, 17538 Value.makeBool(ty.isTuple(zcu)).toIntern(), 17539 }; 17540 return Air.internedToRef((try pt.internUnion(.{ 17541 .ty = type_info_ty.toIntern(), 17542 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"struct"))).toIntern(), 17543 .val = try pt.intern(.{ .aggregate = .{ 17544 .ty = type_struct_ty.toIntern(), 17545 .storage = .{ .elems = &field_values }, 17546 } }), 17547 }))); 17548 }, 17549 .@"opaque" => { 17550 const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque"); 17551 17552 try ty.resolveFields(pt); 17553 const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu)); 17554 17555 const field_values = .{ 17556 // decls: []const Declaration, 17557 decls_val, 17558 }; 17559 return Air.internedToRef((try pt.internUnion(.{ 17560 .ty = type_info_ty.toIntern(), 17561 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"opaque"))).toIntern(), 17562 .val = try pt.intern(.{ .aggregate = .{ 17563 .ty = type_opaque_ty.toIntern(), 17564 .storage = .{ .elems = &field_values }, 17565 } }), 17566 }))); 17567 }, 17568 .frame => return sema.failWithUseOfAsync(block, src), 17569 .@"anyframe" => return sema.failWithUseOfAsync(block, src), 17570 } 17571 } 17572 17573 fn typeInfoDecls( 17574 sema: *Sema, 17575 src: LazySrcLoc, 17576 opt_namespace: InternPool.OptionalNamespaceIndex, 17577 ) CompileError!InternPool.Index { 17578 const pt = sema.pt; 17579 const zcu = pt.zcu; 17580 const gpa = sema.gpa; 17581 17582 const declaration_ty = try sema.getBuiltinType(src, .@"Type.Declaration"); 17583 17584 var decl_vals = std.ArrayList(InternPool.Index).init(gpa); 17585 defer decl_vals.deinit(); 17586 17587 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); 17588 defer seen_namespaces.deinit(); 17589 17590 try sema.typeInfoNamespaceDecls(opt_namespace, declaration_ty, &decl_vals, &seen_namespaces); 17591 17592 const array_decl_ty = try pt.arrayType(.{ 17593 .len = decl_vals.items.len, 17594 .child = declaration_ty.toIntern(), 17595 }); 17596 const new_decl_val = try pt.intern(.{ .aggregate = .{ 17597 .ty = array_decl_ty.toIntern(), 17598 .storage = .{ .elems = decl_vals.items }, 17599 } }); 17600 const slice_ty = (try pt.ptrTypeSema(.{ 17601 .child = declaration_ty.toIntern(), 17602 .flags = .{ 17603 .size = .slice, 17604 .is_const = true, 17605 }, 17606 })).toIntern(); 17607 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); 17608 return try pt.intern(.{ .slice = .{ 17609 .ty = slice_ty, 17610 .ptr = try pt.intern(.{ .ptr = .{ 17611 .ty = manyptr_ty, 17612 .base_addr = .{ .uav = .{ 17613 .orig_ty = manyptr_ty, 17614 .val = new_decl_val, 17615 } }, 17616 .byte_offset = 0, 17617 } }), 17618 .len = (try pt.intValue(.usize, decl_vals.items.len)).toIntern(), 17619 } }); 17620 } 17621 17622 fn typeInfoNamespaceDecls( 17623 sema: *Sema, 17624 opt_namespace_index: InternPool.OptionalNamespaceIndex, 17625 declaration_ty: Type, 17626 decl_vals: *std.ArrayList(InternPool.Index), 17627 seen_namespaces: *std.AutoHashMap(*Namespace, void), 17628 ) !void { 17629 const pt = sema.pt; 17630 const zcu = pt.zcu; 17631 const ip = &zcu.intern_pool; 17632 17633 const namespace_index = opt_namespace_index.unwrap() orelse return; 17634 try pt.ensureNamespaceUpToDate(namespace_index); 17635 const namespace = zcu.namespacePtr(namespace_index); 17636 17637 const gop = try seen_namespaces.getOrPut(namespace); 17638 if (gop.found_existing) return; 17639 17640 for (namespace.pub_decls.keys()) |nav| { 17641 const name = ip.getNav(nav).name; 17642 const name_val = name_val: { 17643 const name_len = name.length(ip); 17644 const array_ty = try pt.arrayType(.{ 17645 .len = name_len, 17646 .sentinel = .zero_u8, 17647 .child = .u8_type, 17648 }); 17649 const array_val = try pt.intern(.{ .aggregate = .{ 17650 .ty = array_ty.toIntern(), 17651 .storage = .{ .bytes = name.toString() }, 17652 } }); 17653 break :name_val try pt.intern(.{ 17654 .slice = .{ 17655 .ty = .slice_const_u8_sentinel_0_type, // [:0]const u8 17656 .ptr = try pt.intern(.{ 17657 .ptr = .{ 17658 .ty = .manyptr_const_u8_sentinel_0_type, // [*:0]const u8 17659 .base_addr = .{ .uav = .{ 17660 .orig_ty = .slice_const_u8_sentinel_0_type, 17661 .val = array_val, 17662 } }, 17663 .byte_offset = 0, 17664 }, 17665 }), 17666 .len = (try pt.intValue(.usize, name_len)).toIntern(), 17667 }, 17668 }); 17669 }; 17670 const fields = [_]InternPool.Index{ 17671 // name: [:0]const u8, 17672 name_val, 17673 }; 17674 try decl_vals.append(try pt.intern(.{ .aggregate = .{ 17675 .ty = declaration_ty.toIntern(), 17676 .storage = .{ .elems = &fields }, 17677 } })); 17678 } 17679 } 17680 17681 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17682 _ = block; 17683 const zir_datas = sema.code.instructions.items(.data); 17684 const inst_data = zir_datas[@intFromEnum(inst)].un_node; 17685 const operand = try sema.resolveInst(inst_data.operand); 17686 const operand_ty = sema.typeOf(operand); 17687 return Air.internedToRef(operand_ty.toIntern()); 17688 } 17689 17690 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17691 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 17692 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 17693 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 17694 17695 var child_block: Block = .{ 17696 .parent = block, 17697 .sema = sema, 17698 .namespace = block.namespace, 17699 .instructions = .{}, 17700 .inlining = block.inlining, 17701 .comptime_reason = null, 17702 .is_typeof = true, 17703 .want_safety = false, 17704 .error_return_trace_index = block.error_return_trace_index, 17705 .src_base_inst = block.src_base_inst, 17706 .type_name_ctx = block.type_name_ctx, 17707 }; 17708 defer child_block.instructions.deinit(sema.gpa); 17709 17710 const operand = try sema.resolveInlineBody(&child_block, body, inst); 17711 return Air.internedToRef(sema.typeOf(operand).toIntern()); 17712 } 17713 17714 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17715 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 17716 const src = block.nodeOffset(inst_data.src_node); 17717 const operand = try sema.resolveInst(inst_data.operand); 17718 const operand_ty = sema.typeOf(operand); 17719 const res_ty = try sema.log2IntType(block, operand_ty, src); 17720 return Air.internedToRef(res_ty.toIntern()); 17721 } 17722 17723 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type { 17724 const pt = sema.pt; 17725 const zcu = pt.zcu; 17726 switch (operand.zigTypeTag(zcu)) { 17727 .comptime_int => return .comptime_int, 17728 .int => { 17729 const bits = operand.bitSize(zcu); 17730 const count = if (bits == 0) 17731 0 17732 else blk: { 17733 var count: u16 = 0; 17734 var s = bits - 1; 17735 while (s != 0) : (s >>= 1) { 17736 count += 1; 17737 } 17738 break :blk count; 17739 }; 17740 return pt.intType(.unsigned, count); 17741 }, 17742 .vector => { 17743 const elem_ty = operand.elemType2(zcu); 17744 const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); 17745 return pt.vectorType(.{ 17746 .len = operand.vectorLen(zcu), 17747 .child = log2_elem_ty.toIntern(), 17748 }); 17749 }, 17750 else => {}, 17751 } 17752 return sema.fail( 17753 block, 17754 src, 17755 "bit shifting operation expected integer type, found '{f}'", 17756 .{operand.fmt(pt)}, 17757 ); 17758 } 17759 17760 fn zirTypeofPeer( 17761 sema: *Sema, 17762 block: *Block, 17763 extended: Zir.Inst.Extended.InstData, 17764 inst: Zir.Inst.Index, 17765 ) CompileError!Air.Inst.Ref { 17766 const tracy = trace(@src()); 17767 defer tracy.end(); 17768 17769 const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); 17770 const src = block.nodeOffset(extra.data.src_node); 17771 const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len); 17772 17773 var child_block: Block = .{ 17774 .parent = block, 17775 .sema = sema, 17776 .namespace = block.namespace, 17777 .instructions = .{}, 17778 .inlining = block.inlining, 17779 .comptime_reason = null, 17780 .is_typeof = true, 17781 .runtime_cond = block.runtime_cond, 17782 .runtime_loop = block.runtime_loop, 17783 .runtime_index = block.runtime_index, 17784 .src_base_inst = block.src_base_inst, 17785 .type_name_ctx = block.type_name_ctx, 17786 }; 17787 defer child_block.instructions.deinit(sema.gpa); 17788 // Ignore the result, we only care about the instructions in `args`. 17789 _ = try sema.analyzeInlineBody(&child_block, body, inst); 17790 17791 const args = sema.code.refSlice(extra.end, extended.small); 17792 17793 const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len); 17794 defer sema.gpa.free(inst_list); 17795 17796 for (args, 0..) |arg_ref, i| { 17797 inst_list[i] = try sema.resolveInst(arg_ref); 17798 } 17799 17800 const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); 17801 return Air.internedToRef(result_type.toIntern()); 17802 } 17803 17804 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17805 const pt = sema.pt; 17806 const zcu = pt.zcu; 17807 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 17808 const src = block.nodeOffset(inst_data.src_node); 17809 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); 17810 const uncasted_operand = try sema.resolveInst(inst_data.operand); 17811 const uncasted_ty = sema.typeOf(uncasted_operand); 17812 if (uncasted_ty.isVector(zcu)) { 17813 if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) { 17814 return sema.fail(block, operand_src, "boolean not operation on type '{f}'", .{ 17815 uncasted_ty.fmt(pt), 17816 }); 17817 } 17818 return analyzeBitNot(sema, block, uncasted_operand, src); 17819 } 17820 const operand = try sema.coerce(block, .bool, uncasted_operand, operand_src); 17821 if (try sema.resolveValue(operand)) |val| { 17822 return if (val.isUndef(zcu)) .undef_bool else if (val.toBool()) .bool_false else .bool_true; 17823 } 17824 try sema.requireRuntimeBlock(block, src, null); 17825 return block.addTyOp(.not, .bool, operand); 17826 } 17827 17828 fn zirBoolBr( 17829 sema: *Sema, 17830 parent_block: *Block, 17831 inst: Zir.Inst.Index, 17832 is_bool_or: bool, 17833 ) CompileError!Air.Inst.Ref { 17834 const tracy = trace(@src()); 17835 defer tracy.end(); 17836 17837 const pt = sema.pt; 17838 const zcu = pt.zcu; 17839 const gpa = sema.gpa; 17840 17841 const datas = sema.code.instructions.items(.data); 17842 const inst_data = datas[@intFromEnum(inst)].pl_node; 17843 const extra = sema.code.extraData(Zir.Inst.BoolBr, inst_data.payload_index); 17844 17845 const uncoerced_lhs = try sema.resolveInst(extra.data.lhs); 17846 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 17847 const lhs_src = parent_block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); 17848 const rhs_src = parent_block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); 17849 17850 const lhs = try sema.coerce(parent_block, .bool, uncoerced_lhs, lhs_src); 17851 17852 if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| { 17853 if (is_bool_or and lhs_val.toBool()) { 17854 return .bool_true; 17855 } else if (!is_bool_or and !lhs_val.toBool()) { 17856 return .bool_false; 17857 } 17858 // comptime-known left-hand side. No need for a block here; the result 17859 // is simply the rhs expression. Here we rely on there only being 1 17860 // break instruction (`break_inline`). 17861 const rhs_result = try sema.resolveInlineBody(parent_block, body, inst); 17862 if (sema.typeOf(rhs_result).isNoReturn(zcu)) { 17863 return rhs_result; 17864 } 17865 return sema.coerce(parent_block, .bool, rhs_result, rhs_src); 17866 } 17867 17868 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 17869 try sema.air_instructions.append(gpa, .{ 17870 .tag = .block, 17871 .data = .{ .ty_pl = .{ 17872 .ty = .bool_type, 17873 .payload = undefined, 17874 } }, 17875 }); 17876 17877 var child_block = parent_block.makeSubBlock(); 17878 child_block.runtime_loop = null; 17879 child_block.runtime_cond = lhs_src; 17880 child_block.runtime_index.increment(); 17881 defer child_block.instructions.deinit(gpa); 17882 17883 var then_block = child_block.makeSubBlock(); 17884 defer then_block.instructions.deinit(gpa); 17885 17886 var else_block = child_block.makeSubBlock(); 17887 defer else_block.instructions.deinit(gpa); 17888 17889 const lhs_block = if (is_bool_or) &then_block else &else_block; 17890 const rhs_block = if (is_bool_or) &else_block else &then_block; 17891 17892 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false; 17893 _ = try lhs_block.addBr(block_inst, lhs_result); 17894 17895 const parent_hint = sema.branch_hint; 17896 defer sema.branch_hint = parent_hint; 17897 sema.branch_hint = null; 17898 17899 const rhs_result = try sema.resolveInlineBody(rhs_block, body, inst); 17900 const rhs_noret = sema.typeOf(rhs_result).isNoReturn(zcu); 17901 const coerced_rhs_result = if (!rhs_noret) rhs: { 17902 const coerced_result = try sema.coerce(rhs_block, .bool, rhs_result, rhs_src); 17903 _ = try rhs_block.addBr(block_inst, coerced_result); 17904 break :rhs coerced_result; 17905 } else rhs_result; 17906 17907 const rhs_hint = sema.branch_hint orelse .none; 17908 17909 const result = try sema.finishCondBr( 17910 parent_block, 17911 &child_block, 17912 &then_block, 17913 &else_block, 17914 lhs, 17915 block_inst, 17916 if (is_bool_or) .{ 17917 .true = .none, 17918 .false = rhs_hint, 17919 .then_cov = .poi, 17920 .else_cov = .poi, 17921 } else .{ 17922 .true = rhs_hint, 17923 .false = .none, 17924 .then_cov = .poi, 17925 .else_cov = .poi, 17926 }, 17927 ); 17928 if (!rhs_noret) { 17929 if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| { 17930 if (is_bool_or and rhs_val.toBool()) { 17931 return .bool_true; 17932 } else if (!is_bool_or and !rhs_val.toBool()) { 17933 return .bool_false; 17934 } 17935 } 17936 } 17937 17938 return result; 17939 } 17940 17941 fn finishCondBr( 17942 sema: *Sema, 17943 parent_block: *Block, 17944 child_block: *Block, 17945 then_block: *Block, 17946 else_block: *Block, 17947 cond: Air.Inst.Ref, 17948 block_inst: Air.Inst.Index, 17949 branch_hints: Air.CondBr.BranchHints, 17950 ) !Air.Inst.Ref { 17951 const gpa = sema.gpa; 17952 17953 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 17954 then_block.instructions.items.len + else_block.instructions.items.len + 17955 @typeInfo(Air.Block).@"struct".fields.len + child_block.instructions.items.len + 1); 17956 17957 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 17958 .then_body_len = @intCast(then_block.instructions.items.len), 17959 .else_body_len = @intCast(else_block.instructions.items.len), 17960 .branch_hints = branch_hints, 17961 }); 17962 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 17963 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 17964 17965 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 17966 .operand = cond, 17967 .payload = cond_br_payload, 17968 } } }); 17969 17970 sema.air_instructions.items(.data)[@intFromEnum(block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity( 17971 Air.Block{ .body_len = @intCast(child_block.instructions.items.len) }, 17972 ); 17973 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 17974 17975 try parent_block.instructions.append(gpa, block_inst); 17976 return block_inst.toRef(); 17977 } 17978 17979 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 17980 const pt = sema.pt; 17981 const zcu = pt.zcu; 17982 switch (ty.zigTypeTag(zcu)) { 17983 .optional, .null, .undefined => return, 17984 .pointer => if (ty.isPtrLikeOptional(zcu)) return, 17985 else => {}, 17986 } 17987 return sema.failWithExpectedOptionalType(block, src, ty); 17988 } 17989 17990 fn checkSentinelType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 17991 const pt = sema.pt; 17992 const zcu = pt.zcu; 17993 if (!ty.isSelfComparable(zcu, true)) { 17994 return sema.fail(block, src, "non-scalar sentinel type '{f}'", .{ty.fmt(pt)}); 17995 } 17996 } 17997 17998 fn zirIsNonNull( 17999 sema: *Sema, 18000 block: *Block, 18001 inst: Zir.Inst.Index, 18002 ) CompileError!Air.Inst.Ref { 18003 const tracy = trace(@src()); 18004 defer tracy.end(); 18005 18006 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18007 const src = block.nodeOffset(inst_data.src_node); 18008 const operand = try sema.resolveInst(inst_data.operand); 18009 try sema.checkNullableType(block, src, sema.typeOf(operand)); 18010 return sema.analyzeIsNull(block, operand, true); 18011 } 18012 18013 fn zirIsNonNullPtr( 18014 sema: *Sema, 18015 block: *Block, 18016 inst: Zir.Inst.Index, 18017 ) CompileError!Air.Inst.Ref { 18018 const tracy = trace(@src()); 18019 defer tracy.end(); 18020 18021 const pt = sema.pt; 18022 const zcu = pt.zcu; 18023 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18024 const src = block.nodeOffset(inst_data.src_node); 18025 const ptr = try sema.resolveInst(inst_data.operand); 18026 const ptr_ty = sema.typeOf(ptr); 18027 try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(zcu)); 18028 if (try sema.resolveValue(ptr)) |ptr_val| { 18029 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |loaded_val| { 18030 return sema.analyzeIsNull(block, Air.internedToRef(loaded_val.toIntern()), true); 18031 } 18032 } 18033 if (ptr_ty.childType(zcu).isNullFromType(zcu)) |is_null| { 18034 return if (is_null) .bool_false else .bool_true; 18035 } 18036 return block.addUnOp(.is_non_null_ptr, ptr); 18037 } 18038 18039 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 18040 const pt = sema.pt; 18041 const zcu = pt.zcu; 18042 switch (ty.zigTypeTag(zcu)) { 18043 .error_set, .error_union, .undefined => return, 18044 else => return sema.fail(block, src, "expected error union type, found '{f}'", .{ 18045 ty.fmt(pt), 18046 }), 18047 } 18048 } 18049 18050 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18051 const tracy = trace(@src()); 18052 defer tracy.end(); 18053 18054 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18055 const src = block.nodeOffset(inst_data.src_node); 18056 const operand = try sema.resolveInst(inst_data.operand); 18057 try sema.checkErrorType(block, src, sema.typeOf(operand)); 18058 return sema.analyzeIsNonErr(block, src, operand); 18059 } 18060 18061 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18062 const tracy = trace(@src()); 18063 defer tracy.end(); 18064 18065 const pt = sema.pt; 18066 const zcu = pt.zcu; 18067 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18068 const src = block.nodeOffset(inst_data.src_node); 18069 const ptr = try sema.resolveInst(inst_data.operand); 18070 try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(zcu)); 18071 const loaded = try sema.analyzeLoad(block, src, ptr, src); 18072 return sema.analyzeIsNonErr(block, src, loaded); 18073 } 18074 18075 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18076 const tracy = trace(@src()); 18077 defer tracy.end(); 18078 18079 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18080 const src = block.nodeOffset(inst_data.src_node); 18081 const operand = try sema.resolveInst(inst_data.operand); 18082 return sema.analyzeIsNonErr(block, src, operand); 18083 } 18084 18085 fn zirCondbr( 18086 sema: *Sema, 18087 parent_block: *Block, 18088 inst: Zir.Inst.Index, 18089 ) CompileError!void { 18090 const tracy = trace(@src()); 18091 defer tracy.end(); 18092 18093 const pt = sema.pt; 18094 const zcu = pt.zcu; 18095 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18096 const cond_src = parent_block.src(.{ .node_offset_if_cond = inst_data.src_node }); 18097 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 18098 18099 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); 18100 const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len); 18101 18102 const uncasted_cond = try sema.resolveInst(extra.data.condition); 18103 const cond = try sema.coerce(parent_block, .bool, uncasted_cond, cond_src); 18104 18105 if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| { 18106 const body = if (cond_val.toBool()) then_body else else_body; 18107 18108 // We can propagate `.cold` hints from this branch since it's comptime-known 18109 // to be taken from the parent branch. 18110 const parent_hint = sema.branch_hint; 18111 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; 18112 18113 try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src); 18114 // We use `analyzeBodyInner` since we want to propagate any comptime control flow to the caller. 18115 return sema.analyzeBodyInner(parent_block, body); 18116 } 18117 18118 const gpa = sema.gpa; 18119 18120 // We'll re-use the sub block to save on memory bandwidth, and yank out the 18121 // instructions array in between using it for the then block and else block. 18122 var sub_block = parent_block.makeSubBlock(); 18123 sub_block.runtime_loop = null; 18124 sub_block.runtime_cond = cond_src; 18125 sub_block.runtime_index.increment(); 18126 sub_block.need_debug_scope = null; // this body is emitted regardless 18127 defer sub_block.instructions.deinit(gpa); 18128 18129 const true_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, then_body); 18130 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); 18131 defer gpa.free(true_instructions); 18132 18133 const err_cond = blk: { 18134 const index = extra.data.condition.toIndex() orelse break :blk null; 18135 if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) break :blk null; 18136 18137 const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node; 18138 const err_operand = try sema.resolveInst(err_inst_data.operand); 18139 const operand_ty = sema.typeOf(err_operand); 18140 assert(operand_ty.zigTypeTag(zcu) == .error_union); 18141 const result_ty = operand_ty.errorUnionSet(zcu); 18142 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); 18143 }; 18144 18145 const false_hint: std.builtin.BranchHint = if (err_cond != null and 18146 try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false)) 18147 h: { 18148 // nothing to do here. weight against error branch 18149 break :h .unlikely; 18150 } else try sema.analyzeBodyRuntimeBreak(&sub_block, else_body); 18151 18152 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 18153 true_instructions.len + sub_block.instructions.items.len); 18154 _ = try parent_block.addInst(.{ 18155 .tag = .cond_br, 18156 .data = .{ 18157 .pl_op = .{ 18158 .operand = cond, 18159 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18160 .then_body_len = @intCast(true_instructions.len), 18161 .else_body_len = @intCast(sub_block.instructions.items.len), 18162 .branch_hints = .{ 18163 .true = true_hint, 18164 .false = false_hint, 18165 // Code coverage is desired for error handling. 18166 .then_cov = .poi, 18167 .else_cov = .poi, 18168 }, 18169 }), 18170 }, 18171 }, 18172 }); 18173 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); 18174 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18175 } 18176 18177 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18178 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18179 const src = parent_block.nodeOffset(inst_data.src_node); 18180 const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node }); 18181 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18182 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18183 const err_union = try sema.resolveInst(extra.data.operand); 18184 const err_union_ty = sema.typeOf(err_union); 18185 const pt = sema.pt; 18186 const zcu = pt.zcu; 18187 if (err_union_ty.zigTypeTag(zcu) != .error_union) { 18188 return sema.failWithOwnedErrorMsg(parent_block, msg: { 18189 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); 18190 errdefer msg.destroy(sema.gpa); 18191 try sema.addDeclaredHereNote(msg, err_union_ty); 18192 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); 18193 break :msg msg; 18194 }); 18195 } 18196 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18197 if (is_non_err != .none) { 18198 // We can propagate `.cold` hints from this branch since it's comptime-known 18199 // to be taken from the parent branch. 18200 const parent_hint = sema.branch_hint; 18201 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; 18202 18203 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18204 if (is_non_err_val.toBool()) { 18205 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); 18206 } 18207 // We can analyze the body directly in the parent block because we know there are 18208 // no breaks from the body possible, and that the body is noreturn. 18209 try sema.analyzeBodyInner(parent_block, body); 18210 return .unreachable_value; 18211 } 18212 18213 var sub_block = parent_block.makeSubBlock(); 18214 defer sub_block.instructions.deinit(sema.gpa); 18215 18216 const parent_hint = sema.branch_hint; 18217 defer sema.branch_hint = parent_hint; 18218 18219 // This body is guaranteed to end with noreturn and has no breaks. 18220 try sema.analyzeBodyInner(&sub_block, body); 18221 18222 // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`. 18223 const is_cold = sema.branch_hint == .cold; 18224 18225 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).@"struct".fields.len + 18226 sub_block.instructions.items.len); 18227 const try_inst = try parent_block.addInst(.{ 18228 .tag = if (is_cold) .try_cold else .@"try", 18229 .data = .{ .pl_op = .{ 18230 .operand = err_union, 18231 .payload = sema.addExtraAssumeCapacity(Air.Try{ 18232 .body_len = @intCast(sub_block.instructions.items.len), 18233 }), 18234 } }, 18235 }); 18236 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18237 return try_inst; 18238 } 18239 18240 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18241 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18242 const src = parent_block.nodeOffset(inst_data.src_node); 18243 const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node }); 18244 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18245 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18246 const operand = try sema.resolveInst(extra.data.operand); 18247 const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); 18248 const err_union_ty = sema.typeOf(err_union); 18249 const pt = sema.pt; 18250 const zcu = pt.zcu; 18251 if (err_union_ty.zigTypeTag(zcu) != .error_union) { 18252 return sema.failWithOwnedErrorMsg(parent_block, msg: { 18253 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); 18254 errdefer msg.destroy(sema.gpa); 18255 try sema.addDeclaredHereNote(msg, err_union_ty); 18256 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); 18257 break :msg msg; 18258 }); 18259 } 18260 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18261 if (is_non_err != .none) { 18262 // We can propagate `.cold` hints from this branch since it's comptime-known 18263 // to be taken from the parent branch. 18264 const parent_hint = sema.branch_hint; 18265 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; 18266 18267 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18268 if (is_non_err_val.toBool()) { 18269 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); 18270 } 18271 // We can analyze the body directly in the parent block because we know there are 18272 // no breaks from the body possible, and that the body is noreturn. 18273 try sema.analyzeBodyInner(parent_block, body); 18274 return .unreachable_value; 18275 } 18276 18277 var sub_block = parent_block.makeSubBlock(); 18278 defer sub_block.instructions.deinit(sema.gpa); 18279 18280 const parent_hint = sema.branch_hint; 18281 defer sema.branch_hint = parent_hint; 18282 18283 // This body is guaranteed to end with noreturn and has no breaks. 18284 try sema.analyzeBodyInner(&sub_block, body); 18285 18286 // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`. 18287 const is_cold = sema.branch_hint == .cold; 18288 18289 const operand_ty = sema.typeOf(operand); 18290 const ptr_info = operand_ty.ptrInfo(zcu); 18291 const res_ty = try pt.ptrTypeSema(.{ 18292 .child = err_union_ty.errorUnionPayload(zcu).toIntern(), 18293 .flags = .{ 18294 .is_const = ptr_info.flags.is_const, 18295 .is_volatile = ptr_info.flags.is_volatile, 18296 .is_allowzero = ptr_info.flags.is_allowzero, 18297 .address_space = ptr_info.flags.address_space, 18298 }, 18299 }); 18300 const res_ty_ref = Air.internedToRef(res_ty.toIntern()); 18301 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).@"struct".fields.len + 18302 sub_block.instructions.items.len); 18303 const try_inst = try parent_block.addInst(.{ 18304 .tag = if (is_cold) .try_ptr_cold else .try_ptr, 18305 .data = .{ .ty_pl = .{ 18306 .ty = res_ty_ref, 18307 .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ 18308 .ptr = operand, 18309 .body_len = @intCast(sub_block.instructions.items.len), 18310 }), 18311 } }, 18312 }); 18313 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18314 return try_inst; 18315 } 18316 18317 fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*LabeledBlock { 18318 const gop = sema.inst_map.getOrPutAssumeCapacity(dest_block); 18319 if (gop.found_existing) existing: { 18320 // This may be a *result* from an earlier iteration of an inline loop. 18321 // In this case, there will not be a post-hoc block entry, and we can 18322 // continue with the logic below. 18323 const new_block_inst = gop.value_ptr.*.toIndex() orelse break :existing; 18324 return sema.post_hoc_blocks.get(new_block_inst) orelse break :existing; 18325 } 18326 18327 try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); 18328 18329 const new_block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 18330 gop.value_ptr.* = new_block_inst.toRef(); 18331 try sema.air_instructions.append(sema.gpa, .{ 18332 .tag = .block, 18333 .data = undefined, 18334 }); 18335 const labeled_block = try sema.gpa.create(LabeledBlock); 18336 labeled_block.* = .{ 18337 .label = .{ 18338 .zir_block = dest_block, 18339 .merges = .{ 18340 .src_locs = .{}, 18341 .results = .{}, 18342 .br_list = .{}, 18343 .block_inst = new_block_inst, 18344 }, 18345 }, 18346 .block = .{ 18347 .parent = block, 18348 .sema = sema, 18349 .namespace = block.namespace, 18350 .instructions = .{}, 18351 .label = &labeled_block.label, 18352 .inlining = block.inlining, 18353 .comptime_reason = block.comptime_reason, 18354 .src_base_inst = block.src_base_inst, 18355 .type_name_ctx = block.type_name_ctx, 18356 }, 18357 }; 18358 sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); 18359 return labeled_block; 18360 } 18361 18362 /// A `break` statement is inside a runtime condition, but trying to 18363 /// break from an inline loop. In such case we must convert it to 18364 /// a runtime break. 18365 fn addRuntimeBreak(sema: *Sema, child_block: *Block, block_inst: Zir.Inst.Index, break_operand: Zir.Inst.Ref) !void { 18366 const labeled_block = try sema.ensurePostHoc(child_block, block_inst); 18367 18368 const operand = try sema.resolveInst(break_operand); 18369 const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); 18370 18371 try labeled_block.label.merges.results.append(sema.gpa, operand); 18372 try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); 18373 try labeled_block.label.merges.src_locs.append(sema.gpa, null); 18374 18375 labeled_block.block.runtime_index.increment(); 18376 if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { 18377 labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; 18378 labeled_block.block.runtime_loop = child_block.runtime_loop; 18379 } 18380 } 18381 18382 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18383 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable"; 18384 const src = block.nodeOffset(inst_data.src_node); 18385 18386 if (block.isComptime()) { 18387 return sema.fail(block, src, "reached unreachable code", .{}); 18388 } 18389 // TODO Add compile error for @optimizeFor occurring too late in a scope. 18390 sema.analyzeUnreachable(block, src, true) catch |err| switch (err) { 18391 error.AnalysisFail => { 18392 const msg = sema.err orelse return err; 18393 if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err; 18394 try sema.errNote(src, msg, "the end of a naked function is implicitly unreachable", .{}); 18395 return err; 18396 }, 18397 else => |e| return e, 18398 }; 18399 } 18400 18401 fn zirRetErrValue( 18402 sema: *Sema, 18403 block: *Block, 18404 inst: Zir.Inst.Index, 18405 ) CompileError!void { 18406 const pt = sema.pt; 18407 const zcu = pt.zcu; 18408 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 18409 const src = block.tokenOffset(inst_data.src_tok); 18410 const err_name = try zcu.intern_pool.getOrPutString( 18411 sema.gpa, 18412 pt.tid, 18413 inst_data.get(sema.code), 18414 .no_embedded_nulls, 18415 ); 18416 _ = try pt.getErrorValue(err_name); 18417 // Return the error code from the function. 18418 const error_set_type = try pt.singleErrorSetType(err_name); 18419 const result_inst = Air.internedToRef((try pt.intern(.{ .err = .{ 18420 .ty = error_set_type.toIntern(), 18421 .name = err_name, 18422 } }))); 18423 return sema.analyzeRet(block, result_inst, src, src); 18424 } 18425 18426 fn zirRetImplicit( 18427 sema: *Sema, 18428 block: *Block, 18429 inst: Zir.Inst.Index, 18430 ) CompileError!void { 18431 const tracy = trace(@src()); 18432 defer tracy.end(); 18433 18434 const pt = sema.pt; 18435 const zcu = pt.zcu; 18436 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 18437 const r_brace_src = block.tokenOffset(inst_data.src_tok); 18438 if (block.inlining == null and sema.func_is_naked) { 18439 assert(!block.isComptime()); 18440 if (block.wantSafety()) { 18441 // Calling a safety function from a naked function would not be legal. 18442 _ = try block.addNoOp(.trap); 18443 } else { 18444 try sema.analyzeUnreachable(block, r_brace_src, false); 18445 } 18446 return; 18447 } 18448 18449 const operand = try sema.resolveInst(inst_data.operand); 18450 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = .zero }); 18451 const base_tag = sema.fn_ret_ty.baseZigTypeTag(zcu); 18452 if (base_tag == .noreturn) { 18453 const msg = msg: { 18454 const msg = try sema.errMsg(ret_ty_src, "function declared '{f}' implicitly returns", .{ 18455 sema.fn_ret_ty.fmt(pt), 18456 }); 18457 errdefer msg.destroy(sema.gpa); 18458 try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{}); 18459 break :msg msg; 18460 }; 18461 return sema.failWithOwnedErrorMsg(block, msg); 18462 } else if (base_tag != .void) { 18463 const msg = msg: { 18464 const msg = try sema.errMsg(ret_ty_src, "function with non-void return type '{f}' implicitly returns", .{ 18465 sema.fn_ret_ty.fmt(pt), 18466 }); 18467 errdefer msg.destroy(sema.gpa); 18468 try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{}); 18469 break :msg msg; 18470 }; 18471 return sema.failWithOwnedErrorMsg(block, msg); 18472 } 18473 18474 return sema.analyzeRet(block, operand, r_brace_src, r_brace_src); 18475 } 18476 18477 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18478 const tracy = trace(@src()); 18479 defer tracy.end(); 18480 18481 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18482 const operand = try sema.resolveInst(inst_data.operand); 18483 const src = block.nodeOffset(inst_data.src_node); 18484 18485 return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node })); 18486 } 18487 18488 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18489 const tracy = trace(@src()); 18490 defer tracy.end(); 18491 18492 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18493 const src = block.nodeOffset(inst_data.src_node); 18494 const ret_ptr = try sema.resolveInst(inst_data.operand); 18495 18496 if (block.isComptime() or block.inlining != null or sema.func_is_naked) { 18497 const operand = try sema.analyzeLoad(block, src, ret_ptr, src); 18498 return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node })); 18499 } 18500 18501 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18502 const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); 18503 return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr); 18504 } 18505 18506 _ = try block.addUnOp(.ret_load, ret_ptr); 18507 } 18508 18509 fn retWithErrTracing( 18510 sema: *Sema, 18511 block: *Block, 18512 src: LazySrcLoc, 18513 is_non_err: Air.Inst.Ref, 18514 ret_tag: Air.Inst.Tag, 18515 operand: Air.Inst.Ref, 18516 ) CompileError!void { 18517 const pt = sema.pt; 18518 const need_check = switch (is_non_err) { 18519 .bool_true => { 18520 _ = try block.addUnOp(ret_tag, operand); 18521 return; 18522 }, 18523 .bool_false => false, 18524 else => true, 18525 }; 18526 18527 // This means we're returning something that might be an error! 18528 // This should only be possible with the `auto` cc, so we definitely have an error trace. 18529 assert(pt.zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace); 18530 18531 const gpa = sema.gpa; 18532 const return_err_fn = Air.internedToRef(try sema.getBuiltin(src, .returnError)); 18533 18534 if (!need_check) { 18535 try sema.callBuiltin(block, src, return_err_fn, .never_tail, &.{}, .@"error return"); 18536 _ = try block.addUnOp(ret_tag, operand); 18537 return; 18538 } 18539 18540 var then_block = block.makeSubBlock(); 18541 defer then_block.instructions.deinit(gpa); 18542 _ = try then_block.addUnOp(ret_tag, operand); 18543 18544 var else_block = block.makeSubBlock(); 18545 defer else_block.instructions.deinit(gpa); 18546 try sema.callBuiltin(&else_block, src, return_err_fn, .never_tail, &.{}, .@"error return"); 18547 _ = try else_block.addUnOp(ret_tag, operand); 18548 18549 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len + 18550 then_block.instructions.items.len + else_block.instructions.items.len + 18551 @typeInfo(Air.Block).@"struct".fields.len + 1); 18552 18553 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18554 .then_body_len = @intCast(then_block.instructions.items.len), 18555 .else_body_len = @intCast(else_block.instructions.items.len), 18556 .branch_hints = .{ 18557 // Weight against error branch. 18558 .true = .likely, 18559 .false = .unlikely, 18560 // Code coverage is not valuable on either branch. 18561 .then_cov = .none, 18562 .else_cov = .none, 18563 }, 18564 }); 18565 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 18566 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 18567 18568 _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 18569 .operand = is_non_err, 18570 .payload = cond_br_payload, 18571 } } }); 18572 } 18573 18574 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { 18575 const pt = sema.pt; 18576 const zcu = pt.zcu; 18577 return fn_ret_ty.isError(zcu) and zcu.comp.config.any_error_tracing; 18578 } 18579 18580 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18581 const pt = sema.pt; 18582 const zcu = pt.zcu; 18583 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index; 18584 18585 if (!block.ownerModule().error_tracing) return; 18586 18587 // This is only relevant at runtime. 18588 if (block.isComptime() or block.is_typeof) return; 18589 18590 const save_index = inst_data.operand == .none or b: { 18591 const operand = try sema.resolveInst(inst_data.operand); 18592 const operand_ty = sema.typeOf(operand); 18593 break :b operand_ty.isError(zcu); 18594 }; 18595 18596 if (save_index) 18597 block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block); 18598 } 18599 18600 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 18601 const extra = sema.code.extraData(Zir.Inst.RestoreErrRetIndex, extended.operand).data; 18602 return sema.restoreErrRetIndex(start_block, start_block.nodeOffset(extra.src_node), extra.block, extra.operand); 18603 } 18604 18605 /// If `operand` is non-error (or is `none`), restores the error return trace to 18606 /// its state at the point `block` was reached (or, if `block` is `none`, the 18607 /// point this function began execution). 18608 fn restoreErrRetIndex(sema: *Sema, start_block: *Block, src: LazySrcLoc, target_block: Zir.Inst.Ref, operand_zir: Zir.Inst.Ref) CompileError!void { 18609 const tracy = trace(@src()); 18610 defer tracy.end(); 18611 18612 const pt = sema.pt; 18613 const zcu = pt.zcu; 18614 18615 const saved_index = if (target_block.toIndexAllowNone()) |zir_block| b: { 18616 var block = start_block; 18617 while (true) { 18618 if (block.label) |label| { 18619 if (label.zir_block == zir_block) { 18620 const target_trace_index = if (block.parent) |parent_block| tgt: { 18621 break :tgt parent_block.error_return_trace_index; 18622 } else sema.error_return_trace_index_on_fn_entry; 18623 18624 if (start_block.error_return_trace_index != target_trace_index) 18625 break :b target_trace_index; 18626 18627 return; // No need to restore 18628 } 18629 } 18630 block = block.parent.?; 18631 } 18632 } else b: { 18633 if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry) 18634 break :b sema.error_return_trace_index_on_fn_entry; 18635 18636 return; // No need to restore 18637 }; 18638 18639 const operand = try sema.resolveInstAllowNone(operand_zir); 18640 18641 if (start_block.isComptime() or start_block.is_typeof) { 18642 const is_non_error = if (operand != .none) blk: { 18643 const is_non_error_inst = try sema.analyzeIsNonErr(start_block, src, operand); 18644 const cond_val = try sema.resolveDefinedValue(start_block, src, is_non_error_inst); 18645 break :blk cond_val.?.toBool(); 18646 } else true; // no operand means pop unconditionally 18647 18648 if (is_non_error) return; 18649 18650 const saved_index_val = try sema.resolveDefinedValue(start_block, src, saved_index); 18651 const saved_index_int = saved_index_val.?.toUnsignedInt(zcu); 18652 assert(saved_index_int <= sema.comptime_err_ret_trace.items.len); 18653 sema.comptime_err_ret_trace.items.len = @intCast(saved_index_int); 18654 return; 18655 } 18656 18657 if (!zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace) return; 18658 if (!start_block.ownerModule().error_tracing) return; 18659 18660 assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere 18661 18662 return sema.popErrorReturnTrace(start_block, src, operand, saved_index); 18663 } 18664 18665 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { 18666 const pt = sema.pt; 18667 const zcu = pt.zcu; 18668 const ip = &zcu.intern_pool; 18669 assert(sema.fn_ret_ty.zigTypeTag(zcu) == .error_union); 18670 const err_set_ty = sema.fn_ret_ty.errorUnionSet(zcu).toIntern(); 18671 switch (err_set_ty) { 18672 .adhoc_inferred_error_set_type => { 18673 const ies = sema.fn_ret_ty_ies.?; 18674 assert(ies.func == .none); 18675 try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand)); 18676 }, 18677 else => if (ip.isInferredErrorSetType(err_set_ty)) { 18678 const ies = sema.fn_ret_ty_ies.?; 18679 assert(ies.func == sema.owner.unwrap().func); 18680 try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand)); 18681 }, 18682 } 18683 } 18684 18685 fn addToInferredErrorSetPtr(sema: *Sema, ies: *InferredErrorSet, op_ty: Type) !void { 18686 const arena = sema.arena; 18687 const pt = sema.pt; 18688 const zcu = pt.zcu; 18689 const ip = &zcu.intern_pool; 18690 switch (op_ty.zigTypeTag(zcu)) { 18691 .error_set => try ies.addErrorSet(op_ty, ip, arena), 18692 .error_union => try ies.addErrorSet(op_ty.errorUnionSet(zcu), ip, arena), 18693 else => {}, 18694 } 18695 } 18696 18697 fn analyzeRet( 18698 sema: *Sema, 18699 block: *Block, 18700 uncasted_operand: Air.Inst.Ref, 18701 src: LazySrcLoc, 18702 operand_src: LazySrcLoc, 18703 ) CompileError!void { 18704 // Special case for returning an error to an inferred error set; we need to 18705 // add the error tag to the inferred error set of the in-scope function, so 18706 // that the coercion below works correctly. 18707 const pt = sema.pt; 18708 const zcu = pt.zcu; 18709 if (sema.fn_ret_ty_ies != null and sema.fn_ret_ty.zigTypeTag(zcu) == .error_union) { 18710 try sema.addToInferredErrorSet(uncasted_operand); 18711 } 18712 const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, operand_src, .{ .is_ret = true }) catch |err| switch (err) { 18713 error.NotCoercible => unreachable, 18714 else => |e| return e, 18715 }; 18716 18717 if (block.inlining) |inlining| { 18718 assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr 18719 if (block.isComptime()) { 18720 const ret_val = try sema.resolveConstValue(block, operand_src, operand, null); 18721 inlining.comptime_result = operand; 18722 18723 if (sema.fn_ret_ty.isError(zcu) and ret_val.getErrorName(zcu) != .none) { 18724 try sema.comptime_err_ret_trace.append(src); 18725 } 18726 return error.ComptimeReturn; 18727 } 18728 // We are inlining a function call; rewrite the `ret` as a `break`. 18729 const br_inst = try block.addBr(inlining.merges.block_inst, operand); 18730 try inlining.merges.results.append(sema.gpa, operand); 18731 try inlining.merges.br_list.append(sema.gpa, br_inst.toIndex().?); 18732 try inlining.merges.src_locs.append(sema.gpa, operand_src); 18733 return; 18734 } else if (block.isComptime()) { 18735 return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{}); 18736 } else if (sema.func_is_naked) { 18737 const msg = msg: { 18738 const msg = try sema.errMsg(src, "cannot return from naked function", .{}); 18739 errdefer msg.destroy(sema.gpa); 18740 18741 try sema.errNote(src, msg, "can only return using assembly", .{}); 18742 break :msg msg; 18743 }; 18744 return sema.failWithOwnedErrorMsg(block, msg); 18745 } 18746 18747 try sema.fn_ret_ty.resolveLayout(pt); 18748 18749 try sema.validateRuntimeValue(block, operand_src, operand); 18750 18751 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret; 18752 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18753 // Avoid adding a frame to the error return trace in case the value is comptime-known 18754 // to be not an error. 18755 const is_non_err = try sema.analyzeIsNonErr(block, operand_src, operand); 18756 return sema.retWithErrTracing(block, src, is_non_err, air_tag, operand); 18757 } 18758 18759 _ = try block.addUnOp(air_tag, operand); 18760 } 18761 18762 fn floatOpAllowed(tag: Zir.Inst.Tag) bool { 18763 // extend this swich as additional operators are implemented 18764 return switch (tag) { 18765 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true, 18766 else => false, 18767 }; 18768 } 18769 18770 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18771 const tracy = trace(@src()); 18772 defer tracy.end(); 18773 18774 const pt = sema.pt; 18775 const zcu = pt.zcu; 18776 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type; 18777 const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); 18778 const elem_ty_src = block.src(.{ .node_offset_ptr_elem = extra.data.src_node }); 18779 const sentinel_src = block.src(.{ .node_offset_ptr_sentinel = extra.data.src_node }); 18780 const align_src = block.src(.{ .node_offset_ptr_align = extra.data.src_node }); 18781 const addrspace_src = block.src(.{ .node_offset_ptr_addrspace = extra.data.src_node }); 18782 const bitoffset_src = block.src(.{ .node_offset_ptr_bitoffset = extra.data.src_node }); 18783 const hostsize_src = block.src(.{ .node_offset_ptr_hostsize = extra.data.src_node }); 18784 18785 const elem_ty = blk: { 18786 const air_inst = try sema.resolveInst(extra.data.elem_type); 18787 const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| { 18788 if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(zcu)) { 18789 try sema.errNote(elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{}); 18790 } 18791 return err; 18792 }; 18793 assert(!ty.isGenericPoison()); 18794 break :blk ty; 18795 }; 18796 18797 if (elem_ty.zigTypeTag(zcu) == .noreturn) 18798 return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); 18799 18800 const target = zcu.getTarget(); 18801 18802 var extra_i = extra.end; 18803 18804 const sentinel = if (inst_data.flags.has_sentinel) blk: { 18805 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 18806 extra_i += 1; 18807 const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src); 18808 const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ .simple = .pointer_sentinel }); 18809 try checkSentinelType(sema, block, sentinel_src, elem_ty); 18810 break :blk val.toIntern(); 18811 } else .none; 18812 18813 const abi_align: Alignment = if (inst_data.flags.has_align) blk: { 18814 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 18815 extra_i += 1; 18816 const coerced = try sema.coerce(block, align_ty, try sema.resolveInst(ref), align_src); 18817 const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" }); 18818 // Check if this happens to be the lazy alignment of our element type, in 18819 // which case we can make this 0 without resolving it. 18820 switch (zcu.intern_pool.indexToKey(val.toIntern())) { 18821 .int => |int| switch (int.storage) { 18822 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none, 18823 else => {}, 18824 }, 18825 else => {}, 18826 } 18827 const align_bytes = (try val.getUnsignedIntSema(pt)).?; 18828 break :blk try sema.validateAlign(block, align_src, align_bytes); 18829 } else .none; 18830 18831 const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: { 18832 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 18833 extra_i += 1; 18834 break :blk try sema.resolveAddressSpace(block, addrspace_src, ref, .pointer); 18835 } else if (elem_ty.zigTypeTag(zcu) == .@"fn" and target.cpu.arch == .avr) .flash else .generic; 18836 18837 const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: { 18838 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 18839 extra_i += 1; 18840 const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, .u16, .{ .simple = .type }); 18841 break :blk @intCast(bit_offset); 18842 } else 0; 18843 18844 const host_size: u16 = if (inst_data.flags.has_bit_range) blk: { 18845 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 18846 extra_i += 1; 18847 const host_size = try sema.resolveInt(block, hostsize_src, ref, .u16, .{ .simple = .type }); 18848 break :blk @intCast(host_size); 18849 } else 0; 18850 18851 if (host_size != 0) { 18852 if (bit_offset >= host_size * 8) { 18853 return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} starts {d} bits after the end of a {d} byte host integer", .{ 18854 elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size, 18855 }); 18856 } 18857 const elem_bit_size = try elem_ty.bitSizeSema(pt); 18858 if (elem_bit_size > host_size * 8 - bit_offset) { 18859 return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{ 18860 elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size, 18861 }); 18862 } 18863 } 18864 18865 if (elem_ty.zigTypeTag(zcu) == .@"fn") { 18866 if (inst_data.size != .one) { 18867 return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); 18868 } 18869 } else if (inst_data.size == .many and elem_ty.zigTypeTag(zcu) == .@"opaque") { 18870 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); 18871 } else if (inst_data.size == .c) { 18872 if (!try sema.validateExternType(elem_ty, .other)) { 18873 const msg = msg: { 18874 const msg = try sema.errMsg(elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)}); 18875 errdefer msg.destroy(sema.gpa); 18876 18877 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other); 18878 18879 try sema.addDeclaredHereNote(msg, elem_ty); 18880 break :msg msg; 18881 }; 18882 return sema.failWithOwnedErrorMsg(block, msg); 18883 } 18884 if (elem_ty.zigTypeTag(zcu) == .@"opaque") { 18885 return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{}); 18886 } 18887 } 18888 18889 if (host_size != 0 and !try sema.validatePackedType(elem_ty)) { 18890 return sema.failWithOwnedErrorMsg(block, msg: { 18891 const msg = try sema.errMsg(elem_ty_src, "bit-pointer cannot refer to value of type '{f}'", .{elem_ty.fmt(pt)}); 18892 errdefer msg.destroy(sema.gpa); 18893 try sema.explainWhyTypeIsNotPacked(msg, elem_ty_src, elem_ty); 18894 break :msg msg; 18895 }); 18896 } 18897 18898 const ty = try pt.ptrTypeSema(.{ 18899 .child = elem_ty.toIntern(), 18900 .sentinel = sentinel, 18901 .flags = .{ 18902 .alignment = abi_align, 18903 .address_space = address_space, 18904 .is_const = !inst_data.flags.is_mutable, 18905 .is_allowzero = inst_data.flags.is_allowzero, 18906 .is_volatile = inst_data.flags.is_volatile, 18907 .size = inst_data.size, 18908 }, 18909 .packed_offset = .{ 18910 .bit_offset = bit_offset, 18911 .host_size = host_size, 18912 }, 18913 }); 18914 return Air.internedToRef(ty.toIntern()); 18915 } 18916 18917 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18918 const tracy = trace(@src()); 18919 defer tracy.end(); 18920 18921 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18922 const src = block.nodeOffset(inst_data.src_node); 18923 const ty_src = block.src(.{ .node_offset_init_ty = inst_data.src_node }); 18924 const obj_ty = try sema.resolveType(block, ty_src, inst_data.operand); 18925 const pt = sema.pt; 18926 const zcu = pt.zcu; 18927 18928 switch (obj_ty.zigTypeTag(zcu)) { 18929 .@"struct" => return sema.structInitEmpty(block, obj_ty, src, src), 18930 .array, .vector => return sema.arrayInitEmpty(block, src, obj_ty), 18931 .void => return Air.internedToRef(Value.void.toIntern()), 18932 .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}), 18933 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), 18934 } 18935 } 18936 18937 fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref { 18938 const tracy = trace(@src()); 18939 defer tracy.end(); 18940 18941 const pt = sema.pt; 18942 const zcu = pt.zcu; 18943 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18944 const src = block.nodeOffset(inst_data.src_node); 18945 18946 // Generic poison means this is an untyped anonymous empty struct/array init 18947 const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse { 18948 if (is_byref) { 18949 return sema.uavRef(.empty_tuple); 18950 } else { 18951 return .empty_tuple; 18952 } 18953 }; 18954 18955 const init_ty = if (is_byref) ty: { 18956 const ptr_ty = ty_operand.optEuBaseType(zcu); 18957 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction 18958 switch (ptr_ty.ptrSize(zcu)) { 18959 // Use a zero-length array for a slice or many-ptr result 18960 .slice, .many => break :ty try pt.arrayType(.{ 18961 .len = 0, 18962 .child = ptr_ty.childType(zcu).toIntern(), 18963 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 18964 }), 18965 // Just use the child type for a single-pointer or C-pointer result 18966 .one, .c => { 18967 const child = ptr_ty.childType(zcu); 18968 if (child.toIntern() == .anyopaque_type) { 18969 // ...unless that child is anyopaque, in which case this is equivalent to an untyped init. 18970 // `.{}` is an empty tuple. 18971 if (is_byref) { 18972 return sema.uavRef(.empty_tuple); 18973 } else { 18974 return .empty_tuple; 18975 } 18976 } 18977 break :ty child; 18978 }, 18979 } 18980 if (!ptr_ty.isSlice(zcu)) { 18981 break :ty ptr_ty.childType(zcu); 18982 } 18983 // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`. 18984 break :ty try pt.arrayType(.{ 18985 .len = 0, 18986 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 18987 .child = ptr_ty.childType(zcu).toIntern(), 18988 }); 18989 } else ty_operand; 18990 const obj_ty = init_ty.optEuBaseType(zcu); 18991 18992 const empty_ref = switch (obj_ty.zigTypeTag(zcu)) { 18993 .@"struct" => try sema.structInitEmpty(block, obj_ty, src, src), 18994 .array, .vector => try sema.arrayInitEmpty(block, src, obj_ty), 18995 .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}), 18996 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), 18997 }; 18998 const init_ref = try sema.coerce(block, init_ty, empty_ref, src); 18999 19000 if (is_byref) { 19001 const init_val = (try sema.resolveValue(init_ref)).?; 19002 return sema.uavRef(init_val.toIntern()); 19003 } else { 19004 return init_ref; 19005 } 19006 } 19007 19008 fn structInitEmpty( 19009 sema: *Sema, 19010 block: *Block, 19011 struct_ty: Type, 19012 dest_src: LazySrcLoc, 19013 init_src: LazySrcLoc, 19014 ) CompileError!Air.Inst.Ref { 19015 const pt = sema.pt; 19016 const zcu = pt.zcu; 19017 const gpa = sema.gpa; 19018 // This logic must be synchronized with that in `zirStructInit`. 19019 try struct_ty.resolveFields(pt); 19020 19021 // The init values to use for the struct instance. 19022 const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(zcu)); 19023 defer gpa.free(field_inits); 19024 @memset(field_inits, .none); 19025 19026 // Maps field index in the struct declaration to the field index in the initialization expression. 19027 const field_assign_idxs = try gpa.alloc(?usize, struct_ty.structFieldCount(zcu)); 19028 defer gpa.free(field_assign_idxs); 19029 @memset(field_assign_idxs, null); 19030 19031 return sema.finishStructInit(block, init_src, dest_src, field_inits, field_assign_idxs, struct_ty, struct_ty, false); 19032 } 19033 19034 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { 19035 const pt = sema.pt; 19036 const zcu = pt.zcu; 19037 const arr_len = obj_ty.arrayLen(zcu); 19038 if (arr_len != 0) { 19039 if (obj_ty.zigTypeTag(zcu) == .array) { 19040 return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len}); 19041 } else { 19042 return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len}); 19043 } 19044 } 19045 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 19046 .ty = obj_ty.toIntern(), 19047 .storage = .{ .elems = &.{} }, 19048 } }))); 19049 } 19050 19051 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19052 const pt = sema.pt; 19053 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19054 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 19055 const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); 19056 const init_src = block.builtinCallArgSrc(inst_data.src_node, 2); 19057 const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; 19058 const union_ty = try sema.resolveType(block, ty_src, extra.union_type); 19059 if (union_ty.zigTypeTag(pt.zcu) != .@"union") { 19060 return sema.fail(block, ty_src, "expected union type, found '{f}'", .{union_ty.fmt(pt)}); 19061 } 19062 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_name }); 19063 const init = try sema.resolveInst(extra.init); 19064 return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); 19065 } 19066 19067 fn unionInit( 19068 sema: *Sema, 19069 block: *Block, 19070 uncasted_init: Air.Inst.Ref, 19071 init_src: LazySrcLoc, 19072 union_ty: Type, 19073 union_ty_src: LazySrcLoc, 19074 field_name: InternPool.NullTerminatedString, 19075 field_src: LazySrcLoc, 19076 ) CompileError!Air.Inst.Ref { 19077 const pt = sema.pt; 19078 const zcu = pt.zcu; 19079 const ip = &zcu.intern_pool; 19080 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); 19081 const field_ty: Type = .fromInterned(zcu.typeToUnion(union_ty).?.field_types.get(ip)[field_index]); 19082 const init = try sema.coerce(block, field_ty, uncasted_init, init_src); 19083 _ = union_ty_src; 19084 return unionInitFromEnumTag(sema, block, init_src, union_ty, field_index, init); 19085 } 19086 19087 fn unionInitFromEnumTag( 19088 sema: *Sema, 19089 block: *Block, 19090 init_src: LazySrcLoc, 19091 union_ty: Type, 19092 field_index: u32, 19093 init: Air.Inst.Ref, 19094 ) !Air.Inst.Ref { 19095 const pt = sema.pt; 19096 const zcu = pt.zcu; 19097 19098 if (try sema.resolveValue(init)) |init_val| { 19099 const tag_ty = union_ty.unionTagTypeHypothetical(zcu); 19100 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); 19101 return Air.internedToRef((try pt.internUnion(.{ 19102 .ty = union_ty.toIntern(), 19103 .tag = tag_val.toIntern(), 19104 .val = init_val.toIntern(), 19105 }))); 19106 } 19107 19108 try sema.requireRuntimeBlock(block, init_src, null); 19109 return block.addUnionInit(union_ty, field_index, init); 19110 } 19111 19112 fn zirStructInit( 19113 sema: *Sema, 19114 block: *Block, 19115 inst: Zir.Inst.Index, 19116 is_ref: bool, 19117 ) CompileError!Air.Inst.Ref { 19118 const gpa = sema.gpa; 19119 const zir_datas = sema.code.instructions.items(.data); 19120 const inst_data = zir_datas[@intFromEnum(inst)].pl_node; 19121 const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); 19122 const src = block.nodeOffset(inst_data.src_node); 19123 19124 const pt = sema.pt; 19125 const zcu = pt.zcu; 19126 const ip = &zcu.intern_pool; 19127 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; 19128 const first_field_type_data = zir_datas[@intFromEnum(first_item.field_type)].pl_node; 19129 const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; 19130 const result_ty = try sema.resolveTypeOrPoison(block, src, first_field_type_extra.container_type) orelse { 19131 // The type wasn't actually known, so treat this as an anon struct init. 19132 return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref); 19133 }; 19134 const resolved_ty = result_ty.optEuBaseType(zcu); 19135 try resolved_ty.resolveLayout(pt); 19136 19137 if (resolved_ty.zigTypeTag(zcu) == .@"struct") { 19138 // This logic must be synchronized with that in `zirStructInitEmpty`. 19139 19140 // Maps field index to field_type index of where it was already initialized. 19141 // For making sure all fields are accounted for and no fields are duplicated. 19142 const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(zcu)); 19143 defer gpa.free(found_fields); 19144 19145 // The init values to use for the struct instance. 19146 const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(zcu)); 19147 defer gpa.free(field_inits); 19148 @memset(field_inits, .none); 19149 19150 // Maps field index in the struct declaration to the field index in the initialization expression. 19151 const field_assign_idxs = try gpa.alloc(?usize, resolved_ty.structFieldCount(zcu)); 19152 defer gpa.free(field_assign_idxs); 19153 @memset(field_assign_idxs, null); 19154 19155 var field_i: u32 = 0; 19156 var extra_index = extra.end; 19157 19158 const is_packed = resolved_ty.containerLayout(zcu) == .@"packed"; 19159 while (field_i < extra.data.fields_len) : (field_i += 1) { 19160 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); 19161 extra_index = item.end; 19162 19163 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19164 const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node }); 19165 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 19166 const field_name = try ip.getOrPutString( 19167 gpa, 19168 pt.tid, 19169 sema.code.nullTerminatedString(field_type_extra.name_start), 19170 .no_embedded_nulls, 19171 ); 19172 const field_index = if (resolved_ty.isTuple(zcu)) 19173 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) 19174 else 19175 try sema.structFieldIndex(block, resolved_ty, field_name, field_src); 19176 assert(field_inits[field_index] == .none); 19177 field_assign_idxs[field_index] = field_i; 19178 found_fields[field_index] = item.data.field_type; 19179 const uncoerced_init = try sema.resolveInst(item.data.init); 19180 const field_ty = resolved_ty.fieldType(field_index, zcu); 19181 field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src); 19182 if (!is_packed) { 19183 try resolved_ty.resolveStructFieldInits(pt); 19184 if (try resolved_ty.structFieldValueComptime(pt, field_index)) |default_value| { 19185 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse { 19186 return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); 19187 }; 19188 19189 if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) { 19190 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); 19191 } 19192 } 19193 } 19194 } 19195 19196 return sema.finishStructInit(block, src, src, field_inits, field_assign_idxs, resolved_ty, result_ty, is_ref); 19197 } else if (resolved_ty.zigTypeTag(zcu) == .@"union") { 19198 if (extra.data.fields_len != 1) { 19199 return sema.fail(block, src, "union initialization expects exactly one field", .{}); 19200 } 19201 19202 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end); 19203 19204 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19205 const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node }); 19206 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 19207 const field_name = try ip.getOrPutString( 19208 gpa, 19209 pt.tid, 19210 sema.code.nullTerminatedString(field_type_extra.name_start), 19211 .no_embedded_nulls, 19212 ); 19213 const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); 19214 const tag_ty = resolved_ty.unionTagTypeHypothetical(zcu); 19215 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); 19216 const field_ty: Type = .fromInterned(zcu.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]); 19217 19218 if (field_ty.zigTypeTag(zcu) == .noreturn) { 19219 return sema.failWithOwnedErrorMsg(block, msg: { 19220 const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{}); 19221 errdefer msg.destroy(sema.gpa); 19222 19223 try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{f}' declared here", .{ 19224 field_name.fmt(ip), 19225 }); 19226 try sema.addDeclaredHereNote(msg, resolved_ty); 19227 break :msg msg; 19228 }); 19229 } 19230 19231 const uncoerced_init_inst = try sema.resolveInst(item.data.init); 19232 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src); 19233 19234 if (try sema.resolveValue(init_inst)) |val| { 19235 const struct_val = Value.fromInterned(try pt.internUnion(.{ 19236 .ty = resolved_ty.toIntern(), 19237 .tag = tag_val.toIntern(), 19238 .val = val.toIntern(), 19239 })); 19240 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src); 19241 const final_val = (try sema.resolveValue(final_val_inst)).?; 19242 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref); 19243 } 19244 19245 if (try resolved_ty.comptimeOnlySema(pt)) { 19246 return sema.failWithNeededComptime(block, field_src, .{ .comptime_only = .{ 19247 .ty = resolved_ty, 19248 .msg = .union_init, 19249 } }); 19250 } 19251 19252 try sema.validateRuntimeValue(block, field_src, init_inst); 19253 19254 if (is_ref) { 19255 const target = zcu.getTarget(); 19256 const alloc_ty = try pt.ptrTypeSema(.{ 19257 .child = result_ty.toIntern(), 19258 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19259 }); 19260 const alloc = try block.addTy(.alloc, alloc_ty); 19261 const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); 19262 const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true); 19263 try sema.storePtr(block, src, field_ptr, init_inst); 19264 if ((try sema.typeHasOnePossibleValue(tag_ty)) == null) { 19265 const new_tag = Air.internedToRef(tag_val.toIntern()); 19266 _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag); 19267 } 19268 return sema.makePtrConst(block, alloc); 19269 } 19270 19271 try sema.requireRuntimeBlock(block, src, null); 19272 const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst); 19273 return sema.coerce(block, result_ty, union_val, src); 19274 } 19275 unreachable; 19276 } 19277 19278 fn finishStructInit( 19279 sema: *Sema, 19280 block: *Block, 19281 init_src: LazySrcLoc, 19282 dest_src: LazySrcLoc, 19283 field_inits: []Air.Inst.Ref, 19284 field_assign_idxs: []?usize, 19285 struct_ty: Type, 19286 result_ty: Type, 19287 is_ref: bool, 19288 ) CompileError!Air.Inst.Ref { 19289 const pt = sema.pt; 19290 const zcu = pt.zcu; 19291 const ip = &zcu.intern_pool; 19292 19293 var root_msg: ?*Zcu.ErrorMsg = null; 19294 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 19295 19296 switch (ip.indexToKey(struct_ty.toIntern())) { 19297 .tuple_type => |tuple| { 19298 // We can't get the slices, as the coercion may invalidate them. 19299 for (0..tuple.types.len) |i| { 19300 if (field_inits[i] != .none) { 19301 // Coerce the init value to the field type. 19302 const field_src = block.src(.{ .init_elem = .{ 19303 .init_node_offset = init_src.offset.node_offset.x, 19304 .elem_index = @intCast(i), 19305 } }); 19306 const field_ty: Type = .fromInterned(tuple.types.get(ip)[i]); 19307 field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src); 19308 continue; 19309 } 19310 19311 const default_val = tuple.values.get(ip)[i]; 19312 19313 if (default_val == .none) { 19314 const template = "missing tuple field with index {d}"; 19315 if (root_msg) |msg| { 19316 try sema.errNote(init_src, msg, template, .{i}); 19317 } else { 19318 root_msg = try sema.errMsg(init_src, template, .{i}); 19319 } 19320 } else { 19321 field_inits[i] = Air.internedToRef(default_val); 19322 } 19323 } 19324 }, 19325 .struct_type => { 19326 const struct_type = ip.loadStructType(struct_ty.toIntern()); 19327 for (0..struct_type.field_types.len) |i| { 19328 if (field_inits[i] != .none) { 19329 // Coerce the init value to the field type. 19330 const field_src = block.src(.{ .init_elem = .{ 19331 .init_node_offset = init_src.offset.node_offset.x, 19332 .elem_index = @intCast(i), 19333 } }); 19334 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 19335 field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src); 19336 continue; 19337 } 19338 19339 try struct_ty.resolveStructFieldInits(pt); 19340 19341 const field_init = struct_type.fieldInit(ip, i); 19342 if (field_init == .none) { 19343 const field_name = struct_type.field_names.get(ip)[i]; 19344 const template = "missing struct field: {f}"; 19345 const args = .{field_name.fmt(ip)}; 19346 if (root_msg) |msg| { 19347 try sema.errNote(init_src, msg, template, args); 19348 } else { 19349 root_msg = try sema.errMsg(init_src, template, args); 19350 } 19351 } else { 19352 field_inits[i] = Air.internedToRef(field_init); 19353 } 19354 } 19355 }, 19356 else => unreachable, 19357 } 19358 19359 if (root_msg) |msg| { 19360 try sema.addDeclaredHereNote(msg, struct_ty); 19361 root_msg = null; 19362 return sema.failWithOwnedErrorMsg(block, msg); 19363 } 19364 19365 // Find which field forces the expression to be runtime, if any. 19366 const opt_runtime_index = for (field_inits, field_assign_idxs) |field_init, field_assign| { 19367 if (!(try sema.isComptimeKnown(field_init))) { 19368 break field_assign; 19369 } 19370 } else null; 19371 19372 const runtime_index = opt_runtime_index orelse { 19373 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); 19374 for (elems, field_inits) |*elem, field_init| { 19375 elem.* = (sema.resolveValue(field_init) catch unreachable).?.toIntern(); 19376 } 19377 const struct_val = try pt.intern(.{ .aggregate = .{ 19378 .ty = struct_ty.toIntern(), 19379 .storage = .{ .elems = elems }, 19380 } }); 19381 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val), init_src); 19382 const final_val = (try sema.resolveValue(final_val_inst)).?; 19383 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref); 19384 }; 19385 19386 if (try struct_ty.comptimeOnlySema(pt)) { 19387 return sema.failWithNeededComptime(block, block.src(.{ .init_elem = .{ 19388 .init_node_offset = init_src.offset.node_offset.x, 19389 .elem_index = @intCast(runtime_index), 19390 } }), .{ .comptime_only = .{ 19391 .ty = struct_ty, 19392 .msg = .struct_init, 19393 } }); 19394 } 19395 19396 for (field_inits) |field_init| { 19397 try sema.validateRuntimeValue(block, dest_src, field_init); 19398 } 19399 19400 if (is_ref) { 19401 try struct_ty.resolveLayout(pt); 19402 const target = zcu.getTarget(); 19403 const alloc_ty = try pt.ptrTypeSema(.{ 19404 .child = result_ty.toIntern(), 19405 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19406 }); 19407 const alloc = try block.addTy(.alloc, alloc_ty); 19408 const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src); 19409 for (field_inits, 0..) |field_init, i_usize| { 19410 const i: u32 = @intCast(i_usize); 19411 const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, struct_ty); 19412 try sema.storePtr(block, dest_src, field_ptr, field_init); 19413 } 19414 19415 return sema.makePtrConst(block, alloc); 19416 } 19417 19418 try sema.requireRuntimeBlock(block, dest_src, block.src(.{ .init_elem = .{ 19419 .init_node_offset = init_src.offset.node_offset.x, 19420 .elem_index = @intCast(runtime_index), 19421 } })); 19422 try struct_ty.resolveStructFieldInits(pt); 19423 const struct_val = try block.addAggregateInit(struct_ty, field_inits); 19424 return sema.coerce(block, result_ty, struct_val, init_src); 19425 } 19426 19427 fn zirStructInitAnon( 19428 sema: *Sema, 19429 block: *Block, 19430 inst: Zir.Inst.Index, 19431 ) CompileError!Air.Inst.Ref { 19432 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19433 const src = block.nodeOffset(inst_data.src_node); 19434 const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); 19435 return sema.structInitAnon(block, src, inst, .anon_init, extra.data, extra.end, false); 19436 } 19437 19438 fn structInitAnon( 19439 sema: *Sema, 19440 block: *Block, 19441 src: LazySrcLoc, 19442 inst: Zir.Inst.Index, 19443 /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a 19444 /// generic poison type. In this case, we need to know to interpret the extra data differently. 19445 comptime kind: enum { anon_init, typed_init }, 19446 extra_data: switch (kind) { 19447 .anon_init => Zir.Inst.StructInitAnon, 19448 .typed_init => Zir.Inst.StructInit, 19449 }, 19450 extra_end: usize, 19451 is_ref: bool, 19452 ) CompileError!Air.Inst.Ref { 19453 const pt = sema.pt; 19454 const zcu = pt.zcu; 19455 const gpa = sema.gpa; 19456 const ip = &zcu.intern_pool; 19457 const zir_datas = sema.code.instructions.items(.data); 19458 19459 const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len); 19460 const values = try sema.arena.alloc(InternPool.Index, types.len); 19461 const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len); 19462 19463 var any_values = false; 19464 19465 // Find which field forces the expression to be runtime, if any. 19466 const opt_runtime_index = rs: { 19467 var runtime_index: ?usize = null; 19468 var extra_index = extra_end; 19469 for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| { 19470 const item = switch (kind) { 19471 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19472 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19473 }; 19474 extra_index = item.end; 19475 19476 const name = switch (kind) { 19477 .anon_init => sema.code.nullTerminatedString(item.data.field_name), 19478 .typed_init => name: { 19479 // `item.data.field_type` references a `field_type` instruction 19480 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19481 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index); 19482 break :name sema.code.nullTerminatedString(field_type_extra.data.name_start); 19483 }, 19484 }; 19485 19486 field_name.* = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); 19487 19488 const init = try sema.resolveInst(item.data.init); 19489 field_ty.* = sema.typeOf(init).toIntern(); 19490 if (Type.fromInterned(field_ty.*).zigTypeTag(zcu) == .@"opaque") { 19491 const msg = msg: { 19492 const field_src = block.src(.{ .init_elem = .{ 19493 .init_node_offset = src.offset.node_offset.x, 19494 .elem_index = @intCast(i_usize), 19495 } }); 19496 const msg = try sema.errMsg(field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19497 errdefer msg.destroy(sema.gpa); 19498 19499 try sema.addDeclaredHereNote(msg, .fromInterned(field_ty.*)); 19500 break :msg msg; 19501 }; 19502 return sema.failWithOwnedErrorMsg(block, msg); 19503 } 19504 if (try sema.resolveValue(init)) |init_val| { 19505 field_val.* = init_val.toIntern(); 19506 any_values = true; 19507 } else { 19508 field_val.* = .none; 19509 runtime_index = @intCast(i_usize); 19510 } 19511 } 19512 break :rs runtime_index; 19513 }; 19514 19515 // We treat anonymous struct types as reified types, because there are similarities: 19516 // * They use a form of structural equivalence, which we can easily model using a custom hash 19517 // * They do not have captures 19518 // * They immediately have their fields resolved 19519 // In general, other code should treat anon struct types and reified struct types identically, 19520 // so there's no point having a separate `InternPool.NamespaceType` field for them. 19521 const type_hash: u64 = hash: { 19522 var hasher = std.hash.Wyhash.init(0); 19523 hasher.update(std.mem.sliceAsBytes(types)); 19524 hasher.update(std.mem.sliceAsBytes(values)); 19525 hasher.update(std.mem.sliceAsBytes(names)); 19526 break :hash hasher.final(); 19527 }; 19528 const tracked_inst = try block.trackZir(inst); 19529 const struct_ty = switch (try ip.getStructType(gpa, pt.tid, .{ 19530 .layout = .auto, 19531 .fields_len = extra_data.fields_len, 19532 .known_non_opv = false, 19533 .requires_comptime = .unknown, 19534 .any_comptime_fields = any_values, 19535 .any_default_inits = any_values, 19536 .inits_resolved = true, 19537 .any_aligned_fields = false, 19538 .key = .{ .reified = .{ 19539 .zir_index = tracked_inst, 19540 .type_hash = type_hash, 19541 } }, 19542 }, false)) { 19543 .wip => |wip| ty: { 19544 errdefer wip.cancel(ip, pt.tid); 19545 const type_name = try sema.createTypeName(block, .anon, "struct", inst, wip.index); 19546 wip.setName(ip, type_name.name, type_name.nav); 19547 19548 const struct_type = ip.loadStructType(wip.index); 19549 19550 for (names, values, 0..) |name, init_val, field_idx| { 19551 assert(struct_type.addFieldName(ip, name) == null); 19552 if (init_val != .none) struct_type.setFieldComptime(ip, field_idx); 19553 } 19554 19555 @memcpy(struct_type.field_types.get(ip), types); 19556 if (any_values) { 19557 @memcpy(struct_type.field_inits.get(ip), values); 19558 } 19559 19560 const new_namespace_index = try pt.createNamespace(.{ 19561 .parent = block.namespace.toOptional(), 19562 .owner_type = wip.index, 19563 .file_scope = block.getFileScopeIndex(zcu), 19564 .generation = zcu.generation, 19565 }); 19566 try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index }); 19567 codegen_type: { 19568 if (zcu.comp.config.use_llvm) break :codegen_type; 19569 if (block.ownerModule().strip) break :codegen_type; 19570 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 19571 try zcu.comp.queueJob(.{ .link_type = wip.index }); 19572 } 19573 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); 19574 break :ty wip.finish(ip, new_namespace_index); 19575 }, 19576 .existing => |ty| ty, 19577 }; 19578 try sema.declareDependency(.{ .interned = struct_ty }); 19579 try sema.addTypeReferenceEntry(src, struct_ty); 19580 19581 _ = opt_runtime_index orelse { 19582 const struct_val = try pt.intern(.{ .aggregate = .{ 19583 .ty = struct_ty, 19584 .storage = .{ .elems = values }, 19585 } }); 19586 return sema.addConstantMaybeRef(struct_val, is_ref); 19587 }; 19588 19589 if (is_ref) { 19590 const target = zcu.getTarget(); 19591 const alloc_ty = try pt.ptrTypeSema(.{ 19592 .child = struct_ty, 19593 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19594 }); 19595 const alloc = try block.addTy(.alloc, alloc_ty); 19596 var extra_index = extra_end; 19597 for (types, 0..) |field_ty, i_usize| { 19598 const i: u32 = @intCast(i_usize); 19599 const item = switch (kind) { 19600 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19601 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19602 }; 19603 extra_index = item.end; 19604 19605 const field_ptr_ty = try pt.ptrTypeSema(.{ 19606 .child = field_ty, 19607 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19608 }); 19609 if (values[i] == .none) { 19610 const init = try sema.resolveInst(item.data.init); 19611 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19612 _ = try block.addBinOp(.store, field_ptr, init); 19613 } 19614 } 19615 19616 return sema.makePtrConst(block, alloc); 19617 } 19618 19619 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); 19620 var extra_index = extra_end; 19621 for (types, 0..) |_, i| { 19622 const item = switch (kind) { 19623 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19624 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19625 }; 19626 extra_index = item.end; 19627 element_refs[i] = try sema.resolveInst(item.data.init); 19628 } 19629 19630 return block.addAggregateInit(.fromInterned(struct_ty), element_refs); 19631 } 19632 19633 fn zirArrayInit( 19634 sema: *Sema, 19635 block: *Block, 19636 inst: Zir.Inst.Index, 19637 is_ref: bool, 19638 ) CompileError!Air.Inst.Ref { 19639 const pt = sema.pt; 19640 const zcu = pt.zcu; 19641 const gpa = sema.gpa; 19642 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19643 const src = block.nodeOffset(inst_data.src_node); 19644 19645 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19646 const args = sema.code.refSlice(extra.end, extra.data.operands_len); 19647 assert(args.len >= 2); // array_ty + at least one element 19648 19649 const result_ty = try sema.resolveTypeOrPoison(block, src, args[0]) orelse { 19650 // The type wasn't actually known, so treat this as an anon array init. 19651 return sema.arrayInitAnon(block, src, args[1..], is_ref); 19652 }; 19653 const array_ty = result_ty.optEuBaseType(zcu); 19654 const is_tuple = array_ty.zigTypeTag(zcu) == .@"struct"; 19655 const sentinel_val = array_ty.sentinel(zcu); 19656 19657 var root_msg: ?*Zcu.ErrorMsg = null; 19658 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 19659 19660 const final_len = try sema.usizeCast(block, src, array_ty.arrayLenIncludingSentinel(zcu)); 19661 const resolved_args = try gpa.alloc(Air.Inst.Ref, final_len); 19662 defer gpa.free(resolved_args); 19663 for (resolved_args, 0..) |*dest, i| { 19664 const elem_src = block.src(.{ .init_elem = .{ 19665 .init_node_offset = src.offset.node_offset.x, 19666 .elem_index = @intCast(i), 19667 } }); 19668 // Less inits than needed. 19669 if (i + 2 > args.len) if (is_tuple) { 19670 const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern(); 19671 if (default_val == .unreachable_value) { 19672 const template = "missing tuple field with index {d}"; 19673 if (root_msg) |msg| { 19674 try sema.errNote(src, msg, template, .{i}); 19675 } else { 19676 root_msg = try sema.errMsg(src, template, .{i}); 19677 } 19678 } else { 19679 dest.* = Air.internedToRef(default_val); 19680 } 19681 continue; 19682 } else { 19683 dest.* = Air.internedToRef(sentinel_val.?.toIntern()); 19684 break; 19685 }; 19686 19687 const arg = args[i + 1]; 19688 const resolved_arg = try sema.resolveInst(arg); 19689 const elem_ty = if (is_tuple) 19690 array_ty.fieldType(i, zcu) 19691 else 19692 array_ty.elemType2(zcu); 19693 dest.* = try sema.coerce(block, elem_ty, resolved_arg, elem_src); 19694 if (is_tuple) { 19695 if (array_ty.structFieldIsComptime(i, zcu)) 19696 try array_ty.resolveStructFieldInits(pt); 19697 if (try array_ty.structFieldValueComptime(pt, i)) |field_val| { 19698 const init_val = try sema.resolveConstValue(block, elem_src, dest.*, .{ .simple = .stored_to_comptime_field }); 19699 if (!field_val.eql(init_val, elem_ty, zcu)) { 19700 return sema.failWithInvalidComptimeFieldStore(block, elem_src, array_ty, i); 19701 } 19702 } 19703 } 19704 } 19705 19706 if (root_msg) |msg| { 19707 try sema.addDeclaredHereNote(msg, array_ty); 19708 root_msg = null; 19709 return sema.failWithOwnedErrorMsg(block, msg); 19710 } 19711 19712 const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| { 19713 const comptime_known = try sema.isComptimeKnown(arg); 19714 if (!comptime_known) break @intCast(i); 19715 } else null; 19716 19717 _ = opt_runtime_index orelse { 19718 const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len); 19719 for (elem_vals, resolved_args) |*val, arg| { 19720 // We checked that all args are comptime above. 19721 val.* = (sema.resolveValue(arg) catch unreachable).?.toIntern(); 19722 } 19723 const arr_val = try pt.intern(.{ .aggregate = .{ 19724 .ty = array_ty.toIntern(), 19725 .storage = .{ .elems = elem_vals }, 19726 } }); 19727 const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val), src); 19728 const result_val = (try sema.resolveValue(result_ref)).?; 19729 return sema.addConstantMaybeRef(result_val.toIntern(), is_ref); 19730 }; 19731 19732 if (is_ref) { 19733 const target = zcu.getTarget(); 19734 const alloc_ty = try pt.ptrTypeSema(.{ 19735 .child = result_ty.toIntern(), 19736 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19737 }); 19738 const alloc = try block.addTy(.alloc, alloc_ty); 19739 const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); 19740 19741 if (is_tuple) { 19742 for (resolved_args, 0..) |arg, i| { 19743 const elem_ptr_ty = try pt.ptrTypeSema(.{ 19744 .child = array_ty.fieldType(i, zcu).toIntern(), 19745 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19746 }); 19747 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 19748 19749 const index = try pt.intRef(.usize, i); 19750 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref); 19751 _ = try block.addBinOp(.store, elem_ptr, arg); 19752 } 19753 return sema.makePtrConst(block, alloc); 19754 } 19755 19756 const elem_ptr_ty = try pt.ptrTypeSema(.{ 19757 .child = array_ty.elemType2(zcu).toIntern(), 19758 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19759 }); 19760 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 19761 19762 for (resolved_args, 0..) |arg, i| { 19763 const index = try pt.intRef(.usize, i); 19764 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref); 19765 _ = try block.addBinOp(.store, elem_ptr, arg); 19766 } 19767 return sema.makePtrConst(block, alloc); 19768 } 19769 19770 const arr_ref = try block.addAggregateInit(array_ty, resolved_args); 19771 return sema.coerce(block, result_ty, arr_ref, src); 19772 } 19773 19774 fn zirArrayInitAnon( 19775 sema: *Sema, 19776 block: *Block, 19777 inst: Zir.Inst.Index, 19778 ) CompileError!Air.Inst.Ref { 19779 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19780 const src = block.nodeOffset(inst_data.src_node); 19781 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19782 const operands = sema.code.refSlice(extra.end, extra.data.operands_len); 19783 return sema.arrayInitAnon(block, src, operands, false); 19784 } 19785 19786 fn arrayInitAnon( 19787 sema: *Sema, 19788 block: *Block, 19789 src: LazySrcLoc, 19790 operands: []const Zir.Inst.Ref, 19791 is_ref: bool, 19792 ) CompileError!Air.Inst.Ref { 19793 const pt = sema.pt; 19794 const zcu = pt.zcu; 19795 const gpa = sema.gpa; 19796 const ip = &zcu.intern_pool; 19797 19798 const types = try sema.arena.alloc(InternPool.Index, operands.len); 19799 const values = try sema.arena.alloc(InternPool.Index, operands.len); 19800 19801 const opt_runtime_src = rs: { 19802 var runtime_src: ?LazySrcLoc = null; 19803 for (operands, 0..) |operand, i| { 19804 const operand_src = src; // TODO better source location 19805 const elem = try sema.resolveInst(operand); 19806 types[i] = sema.typeOf(elem).toIntern(); 19807 if (Type.fromInterned(types[i]).zigTypeTag(zcu) == .@"opaque") { 19808 const msg = msg: { 19809 const msg = try sema.errMsg(operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19810 errdefer msg.destroy(gpa); 19811 19812 try sema.addDeclaredHereNote(msg, .fromInterned(types[i])); 19813 break :msg msg; 19814 }; 19815 return sema.failWithOwnedErrorMsg(block, msg); 19816 } 19817 if (try sema.resolveValue(elem)) |val| { 19818 values[i] = val.toIntern(); 19819 } else { 19820 values[i] = .none; 19821 runtime_src = operand_src; 19822 } 19823 } 19824 break :rs runtime_src; 19825 }; 19826 19827 const tuple_ty = try ip.getTupleType(gpa, pt.tid, .{ 19828 .types = types, 19829 .values = values, 19830 }); 19831 19832 const runtime_src = opt_runtime_src orelse { 19833 const tuple_val = try pt.intern(.{ .aggregate = .{ 19834 .ty = tuple_ty, 19835 .storage = .{ .elems = values }, 19836 } }); 19837 return sema.addConstantMaybeRef(tuple_val, is_ref); 19838 }; 19839 19840 try sema.requireRuntimeBlock(block, src, runtime_src); 19841 19842 if (is_ref) { 19843 const target = sema.pt.zcu.getTarget(); 19844 const alloc_ty = try pt.ptrTypeSema(.{ 19845 .child = tuple_ty, 19846 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19847 }); 19848 const alloc = try block.addTy(.alloc, alloc_ty); 19849 for (operands, 0..) |operand, i_usize| { 19850 const i: u32 = @intCast(i_usize); 19851 const field_ptr_ty = try pt.ptrTypeSema(.{ 19852 .child = types[i], 19853 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19854 }); 19855 if (values[i] == .none) { 19856 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19857 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); 19858 } 19859 } 19860 19861 return sema.makePtrConst(block, alloc); 19862 } 19863 19864 const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 19865 for (operands, 0..) |operand, i| { 19866 element_refs[i] = try sema.resolveInst(operand); 19867 } 19868 19869 return block.addAggregateInit(.fromInterned(tuple_ty), element_refs); 19870 } 19871 19872 fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref { 19873 return if (is_ref) sema.uavRef(val) else Air.internedToRef(val); 19874 } 19875 19876 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19877 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19878 const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data; 19879 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 19880 const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); 19881 const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); 19882 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name }); 19883 return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); 19884 } 19885 19886 fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19887 const pt = sema.pt; 19888 const zcu = pt.zcu; 19889 const ip = &zcu.intern_pool; 19890 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19891 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; 19892 const ty_src = block.nodeOffset(inst_data.src_node); 19893 const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node }); 19894 const wrapped_aggregate_ty = try sema.resolveTypeOrPoison(block, ty_src, extra.container_type) orelse return .generic_poison_type; 19895 const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu); 19896 const zir_field_name = sema.code.nullTerminatedString(extra.name_start); 19897 const field_name = try ip.getOrPutString(sema.gpa, pt.tid, zir_field_name, .no_embedded_nulls); 19898 return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); 19899 } 19900 19901 fn fieldType( 19902 sema: *Sema, 19903 block: *Block, 19904 aggregate_ty: Type, 19905 field_name: InternPool.NullTerminatedString, 19906 field_src: LazySrcLoc, 19907 ty_src: LazySrcLoc, 19908 ) CompileError!Air.Inst.Ref { 19909 const pt = sema.pt; 19910 const zcu = pt.zcu; 19911 const ip = &zcu.intern_pool; 19912 var cur_ty = aggregate_ty; 19913 while (true) { 19914 try cur_ty.resolveFields(pt); 19915 switch (cur_ty.zigTypeTag(zcu)) { 19916 .@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) { 19917 .tuple_type => |tuple| { 19918 const field_index = try sema.tupleFieldIndex(block, cur_ty, field_name, field_src); 19919 return Air.internedToRef(tuple.types.get(ip)[field_index]); 19920 }, 19921 .struct_type => { 19922 const struct_type = ip.loadStructType(cur_ty.toIntern()); 19923 const field_index = struct_type.nameIndex(ip, field_name) orelse 19924 return sema.failWithBadStructFieldAccess(block, cur_ty, struct_type, field_src, field_name); 19925 const field_ty = struct_type.field_types.get(ip)[field_index]; 19926 return Air.internedToRef(field_ty); 19927 }, 19928 else => unreachable, 19929 }, 19930 .@"union" => { 19931 const union_obj = zcu.typeToUnion(cur_ty).?; 19932 const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse 19933 return sema.failWithBadUnionFieldAccess(block, cur_ty, union_obj, field_src, field_name); 19934 const field_ty = union_obj.field_types.get(ip)[field_index]; 19935 return Air.internedToRef(field_ty); 19936 }, 19937 .optional => { 19938 // Struct/array init through optional requires the child type to not be a pointer. 19939 // If the child of .optional is a pointer it'll error on the next loop. 19940 cur_ty = .fromInterned(ip.indexToKey(cur_ty.toIntern()).opt_type); 19941 continue; 19942 }, 19943 .error_union => { 19944 cur_ty = cur_ty.errorUnionPayload(zcu); 19945 continue; 19946 }, 19947 else => {}, 19948 } 19949 return sema.fail(block, ty_src, "expected struct or union; found '{f}'", .{ 19950 cur_ty.fmt(pt), 19951 }); 19952 } 19953 } 19954 19955 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 19956 return sema.getErrorReturnTrace(block); 19957 } 19958 19959 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 19960 const pt = sema.pt; 19961 const zcu = pt.zcu; 19962 const ip = &zcu.intern_pool; 19963 const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace); 19964 try stack_trace_ty.resolveFields(pt); 19965 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); 19966 const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern()); 19967 19968 switch (sema.owner.unwrap()) { 19969 .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) { 19970 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); 19971 }, 19972 .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, 19973 } 19974 return Air.internedToRef(try pt.intern(.{ .opt = .{ 19975 .ty = opt_ptr_stack_trace_ty.toIntern(), 19976 .val = .none, 19977 } })); 19978 } 19979 19980 fn zirFrame( 19981 sema: *Sema, 19982 block: *Block, 19983 extended: Zir.Inst.Extended.InstData, 19984 ) CompileError!Air.Inst.Ref { 19985 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 19986 const src = block.nodeOffset(src_node); 19987 return sema.failWithUseOfAsync(block, src); 19988 } 19989 19990 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19991 const zcu = sema.pt.zcu; 19992 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 19993 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 19994 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 19995 if (ty.isNoReturn(zcu)) { 19996 return sema.fail(block, operand_src, "no align available for type '{f}'", .{ty.fmt(sema.pt)}); 19997 } 19998 const val = try ty.lazyAbiAlignment(sema.pt); 19999 return Air.internedToRef(val.toIntern()); 20000 } 20001 20002 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20003 const pt = sema.pt; 20004 const zcu = pt.zcu; 20005 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20006 const src = block.nodeOffset(inst_data.src_node); 20007 const operand = try sema.resolveInst(inst_data.operand); 20008 const operand_ty = sema.typeOf(operand); 20009 const is_vector = operand_ty.zigTypeTag(zcu) == .vector; 20010 const operand_scalar_ty = operand_ty.scalarType(zcu); 20011 if (operand_scalar_ty.toIntern() != .bool_type) { 20012 return sema.fail(block, src, "expected 'bool', found '{t}'", .{operand_scalar_ty.zigTypeTag(zcu)}); 20013 } 20014 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; 20015 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1; 20016 if (try sema.resolveValue(operand)) |val| { 20017 if (!is_vector) { 20018 return if (val.isUndef(zcu)) .undef_u1 else if (val.toBool()) .one_u1 else .zero_u1; 20019 } 20020 if (val.isUndef(zcu)) return pt.undefRef(dest_ty); 20021 const new_elems = try sema.arena.alloc(InternPool.Index, len); 20022 for (new_elems, 0..) |*new_elem, i| { 20023 const old_elem = try val.elemValue(pt, i); 20024 new_elem.* = if (old_elem.isUndef(zcu)) 20025 .undef_u1 20026 else if (old_elem.toBool()) 20027 .one_u1 20028 else 20029 .zero_u1; 20030 } 20031 return Air.internedToRef(try pt.intern(.{ .aggregate = .{ 20032 .ty = dest_ty.toIntern(), 20033 .storage = .{ .elems = new_elems }, 20034 } })); 20035 } 20036 return block.addBitCast(dest_ty, operand); 20037 } 20038 20039 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20040 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20041 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20042 const uncoerced_operand = try sema.resolveInst(inst_data.operand); 20043 const operand = try sema.coerce(block, .anyerror, uncoerced_operand, operand_src); 20044 20045 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 20046 const err_name = sema.pt.zcu.intern_pool.indexToKey(val.toIntern()).err.name; 20047 return sema.addNullTerminatedStrLit(err_name); 20048 } 20049 20050 // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass 20051 // might be able to resolve the result at compile time. 20052 return block.addUnOp(.error_name, operand); 20053 } 20054 20055 fn zirAbs( 20056 sema: *Sema, 20057 block: *Block, 20058 inst: Zir.Inst.Index, 20059 ) CompileError!Air.Inst.Ref { 20060 const pt = sema.pt; 20061 const zcu = pt.zcu; 20062 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20063 const operand = try sema.resolveInst(inst_data.operand); 20064 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20065 const operand_ty = sema.typeOf(operand); 20066 const scalar_ty = operand_ty.scalarType(zcu); 20067 20068 const result_ty = switch (scalar_ty.zigTypeTag(zcu)) { 20069 .comptime_float, .float, .comptime_int => operand_ty, 20070 .int => if (scalar_ty.isSignedInt(zcu)) try operand_ty.toUnsigned(pt) else return operand, 20071 else => return sema.fail( 20072 block, 20073 operand_src, 20074 "expected integer, float, or vector of either integers or floats, found '{f}'", 20075 .{operand_ty.fmt(pt)}, 20076 ), 20077 }; 20078 20079 return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse { 20080 try sema.requireRuntimeBlock(block, operand_src, null); 20081 return block.addTyOp(.abs, result_ty, operand); 20082 }; 20083 } 20084 20085 fn maybeConstantUnaryMath( 20086 sema: *Sema, 20087 operand: Air.Inst.Ref, 20088 result_ty: Type, 20089 comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, 20090 ) CompileError!?Air.Inst.Ref { 20091 const pt = sema.pt; 20092 const zcu = pt.zcu; 20093 switch (result_ty.zigTypeTag(zcu)) { 20094 .vector => if (try sema.resolveValue(operand)) |val| { 20095 const scalar_ty = result_ty.scalarType(zcu); 20096 const vec_len = result_ty.vectorLen(zcu); 20097 if (val.isUndef(zcu)) 20098 return try pt.undefRef(result_ty); 20099 20100 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 20101 for (elems, 0..) |*elem, i| { 20102 const elem_val = try val.elemValue(pt, i); 20103 elem.* = (try eval(elem_val, scalar_ty, sema.arena, pt)).toIntern(); 20104 } 20105 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 20106 .ty = result_ty.toIntern(), 20107 .storage = .{ .elems = elems }, 20108 } }))); 20109 }, 20110 else => if (try sema.resolveValue(operand)) |operand_val| { 20111 if (operand_val.isUndef(zcu)) 20112 return try pt.undefRef(result_ty); 20113 const result_val = try eval(operand_val, result_ty, sema.arena, pt); 20114 return Air.internedToRef(result_val.toIntern()); 20115 }, 20116 } 20117 return null; 20118 } 20119 20120 fn zirUnaryMath( 20121 sema: *Sema, 20122 block: *Block, 20123 inst: Zir.Inst.Index, 20124 air_tag: Air.Inst.Tag, 20125 comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, 20126 ) CompileError!Air.Inst.Ref { 20127 const tracy = trace(@src()); 20128 defer tracy.end(); 20129 20130 const pt = sema.pt; 20131 const zcu = pt.zcu; 20132 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20133 const operand = try sema.resolveInst(inst_data.operand); 20134 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20135 const operand_ty = sema.typeOf(operand); 20136 const scalar_ty = operand_ty.scalarType(zcu); 20137 20138 switch (scalar_ty.zigTypeTag(zcu)) { 20139 .comptime_float, .float => {}, 20140 else => return sema.fail( 20141 block, 20142 operand_src, 20143 "expected vector of floats or float type, found '{f}'", 20144 .{operand_ty.fmt(pt)}, 20145 ), 20146 } 20147 20148 return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse { 20149 try sema.requireRuntimeBlock(block, operand_src, null); 20150 return block.addUnOp(air_tag, operand); 20151 }; 20152 } 20153 20154 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20155 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20156 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 20157 const src = block.nodeOffset(inst_data.src_node); 20158 const operand = try sema.resolveInst(inst_data.operand); 20159 const operand_ty = sema.typeOf(operand); 20160 const pt = sema.pt; 20161 const zcu = pt.zcu; 20162 const ip = &zcu.intern_pool; 20163 try operand_ty.resolveLayout(pt); 20164 const enum_ty = switch (operand_ty.zigTypeTag(zcu)) { 20165 .enum_literal => { 20166 const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?; 20167 const tag_name = ip.indexToKey(val.toIntern()).enum_literal; 20168 return sema.addNullTerminatedStrLit(tag_name); 20169 }, 20170 .@"enum" => operand_ty, 20171 .@"union" => operand_ty.unionTagType(zcu) orelse 20172 return sema.fail(block, src, "union '{f}' is untagged", .{operand_ty.fmt(pt)}), 20173 else => return sema.fail(block, operand_src, "expected enum or union; found '{f}'", .{ 20174 operand_ty.fmt(pt), 20175 }), 20176 }; 20177 if (enum_ty.enumFieldCount(zcu) == 0) { 20178 // TODO I don't think this is the correct way to handle this but 20179 // it prevents a crash. 20180 // https://github.com/ziglang/zig/issues/15909 20181 return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{f}'", .{ 20182 enum_ty.fmt(pt), 20183 }); 20184 } 20185 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); 20186 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { 20187 const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse { 20188 const msg = msg: { 20189 const msg = try sema.errMsg(src, "no field with value '{f}' in enum '{f}'", .{ 20190 val.fmtValueSema(pt, sema), enum_ty.fmt(pt), 20191 }); 20192 errdefer msg.destroy(sema.gpa); 20193 try sema.errNote(enum_ty.srcLoc(zcu), msg, "declared here", .{}); 20194 break :msg msg; 20195 }; 20196 return sema.failWithOwnedErrorMsg(block, msg); 20197 }; 20198 // TODO: write something like getCoercedInts to avoid needing to dupe 20199 const field_name = enum_ty.enumFieldName(field_index, zcu); 20200 return sema.addNullTerminatedStrLit(field_name); 20201 } 20202 try sema.requireRuntimeBlock(block, src, operand_src); 20203 if (block.wantSafety() and zcu.backendSupportsFeature(.is_named_enum_value)) { 20204 const ok = try block.addUnOp(.is_named_enum_value, casted_operand); 20205 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); 20206 } 20207 // In case the value is runtime-known, we have an AIR instruction for this instead 20208 // of trying to lower it in Sema because an optimization pass may result in the operand 20209 // being comptime-known, which would let us elide the `tag_name` AIR instruction. 20210 return block.addUnOp(.tag_name, casted_operand); 20211 } 20212 20213 fn zirReify( 20214 sema: *Sema, 20215 block: *Block, 20216 extended: Zir.Inst.Extended.InstData, 20217 inst: Zir.Inst.Index, 20218 ) CompileError!Air.Inst.Ref { 20219 const pt = sema.pt; 20220 const zcu = pt.zcu; 20221 const gpa = sema.gpa; 20222 const ip = &zcu.intern_pool; 20223 const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small); 20224 const extra = sema.code.extraData(Zir.Inst.Reify, extended.operand).data; 20225 const tracked_inst = try block.trackZir(inst); 20226 const src: LazySrcLoc = .{ 20227 .base_node_inst = tracked_inst, 20228 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 20229 }; 20230 const operand_src: LazySrcLoc = .{ 20231 .base_node_inst = tracked_inst, 20232 .offset = .{ 20233 .node_offset_builtin_call_arg = .{ 20234 .builtin_call_node = .zero, // `tracked_inst` is precisely the `reify` instruction, so offset is 0 20235 .arg_index = 0, 20236 }, 20237 }, 20238 }; 20239 const type_info_ty = try sema.getBuiltinType(src, .Type); 20240 const uncasted_operand = try sema.resolveInst(extra.operand); 20241 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); 20242 const val = try sema.resolveConstDefinedValue(block, operand_src, type_info, .{ .simple = .operand_Type }); 20243 const union_val = ip.indexToKey(val.toIntern()).un; 20244 if (try sema.anyUndef(block, operand_src, Value.fromInterned(union_val.val))) { 20245 return sema.failWithUseOfUndef(block, operand_src); 20246 } 20247 const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), zcu).?; 20248 switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { 20249 .type => return .type_type, 20250 .void => return .void_type, 20251 .bool => return .bool_type, 20252 .noreturn => return .noreturn_type, 20253 .comptime_float => return .comptime_float_type, 20254 .comptime_int => return .comptime_int_type, 20255 .undefined => return .undefined_type, 20256 .null => return .null_type, 20257 .@"anyframe" => return sema.failWithUseOfAsync(block, src), 20258 .enum_literal => return .enum_literal_type, 20259 .int => { 20260 const int = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Int); 20261 const ty = try pt.intType(int.signedness, int.bits); 20262 return Air.internedToRef(ty.toIntern()); 20263 }, 20264 .vector => { 20265 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20266 const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20267 ip, 20268 try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), 20269 ).?); 20270 const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20271 ip, 20272 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), 20273 ).?); 20274 20275 const len: u32 = @intCast(try len_val.toUnsignedIntSema(pt)); 20276 const child_ty = child_val.toType(); 20277 20278 try sema.checkVectorElemType(block, src, child_ty); 20279 20280 const ty = try pt.vectorType(.{ 20281 .len = len, 20282 .child = child_ty.toIntern(), 20283 }); 20284 return Air.internedToRef(ty.toIntern()); 20285 }, 20286 .float => { 20287 const float = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Float); 20288 20289 const ty: Type = switch (float.bits) { 20290 16 => .f16, 20291 32 => .f32, 20292 64 => .f64, 20293 80 => .f80, 20294 128 => .f128, 20295 else => return sema.fail(block, src, "{d}-bit float unsupported", .{float.bits}), 20296 }; 20297 return Air.internedToRef(ty.toIntern()); 20298 }, 20299 .pointer => { 20300 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20301 const size_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20302 ip, 20303 try ip.getOrPutString(gpa, pt.tid, "size", .no_embedded_nulls), 20304 ).?); 20305 const is_const_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20306 ip, 20307 try ip.getOrPutString(gpa, pt.tid, "is_const", .no_embedded_nulls), 20308 ).?); 20309 const is_volatile_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20310 ip, 20311 try ip.getOrPutString(gpa, pt.tid, "is_volatile", .no_embedded_nulls), 20312 ).?); 20313 const alignment_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20314 ip, 20315 try ip.getOrPutString(gpa, pt.tid, "alignment", .no_embedded_nulls), 20316 ).?); 20317 const address_space_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20318 ip, 20319 try ip.getOrPutString(gpa, pt.tid, "address_space", .no_embedded_nulls), 20320 ).?); 20321 const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20322 ip, 20323 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), 20324 ).?); 20325 const is_allowzero_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20326 ip, 20327 try ip.getOrPutString(gpa, pt.tid, "is_allowzero", .no_embedded_nulls), 20328 ).?); 20329 const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20330 ip, 20331 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls), 20332 ).?); 20333 20334 if (!try sema.intFitsInType(alignment_val, align_ty, null)) { 20335 return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)}); 20336 } 20337 const alignment_val_int = try alignment_val.toUnsignedIntSema(pt); 20338 const abi_align = try sema.validateAlign(block, src, alignment_val_int); 20339 20340 const elem_ty = child_val.toType(); 20341 if (abi_align != .none) { 20342 try elem_ty.resolveLayout(pt); 20343 } 20344 20345 const ptr_size = try sema.interpretBuiltinType(block, operand_src, size_val, std.builtin.Type.Pointer.Size); 20346 20347 const actual_sentinel: InternPool.Index = s: { 20348 if (!sentinel_val.isNull(zcu)) { 20349 if (ptr_size == .one or ptr_size == .c) { 20350 return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); 20351 } 20352 const sentinel_ptr_val = sentinel_val.optionalValue(zcu).?; 20353 const ptr_ty = try pt.singleMutPtrType(elem_ty); 20354 const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; 20355 try sema.checkSentinelType(block, src, elem_ty); 20356 break :s sent_val.toIntern(); 20357 } 20358 break :s .none; 20359 }; 20360 20361 if (elem_ty.zigTypeTag(zcu) == .noreturn) { 20362 return sema.fail(block, src, "pointer to noreturn not allowed", .{}); 20363 } else if (elem_ty.zigTypeTag(zcu) == .@"fn") { 20364 if (ptr_size != .one) { 20365 return sema.fail(block, src, "function pointers must be single pointers", .{}); 20366 } 20367 } else if (ptr_size == .many and elem_ty.zigTypeTag(zcu) == .@"opaque") { 20368 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); 20369 } else if (ptr_size == .c) { 20370 if (!try sema.validateExternType(elem_ty, .other)) { 20371 const msg = msg: { 20372 const msg = try sema.errMsg(src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)}); 20373 errdefer msg.destroy(gpa); 20374 20375 try sema.explainWhyTypeIsNotExtern(msg, src, elem_ty, .other); 20376 20377 try sema.addDeclaredHereNote(msg, elem_ty); 20378 break :msg msg; 20379 }; 20380 return sema.failWithOwnedErrorMsg(block, msg); 20381 } 20382 if (elem_ty.zigTypeTag(zcu) == .@"opaque") { 20383 return sema.fail(block, src, "C pointers cannot point to opaque types", .{}); 20384 } 20385 } 20386 20387 const ty = try pt.ptrTypeSema(.{ 20388 .child = elem_ty.toIntern(), 20389 .sentinel = actual_sentinel, 20390 .flags = .{ 20391 .size = ptr_size, 20392 .is_const = is_const_val.toBool(), 20393 .is_volatile = is_volatile_val.toBool(), 20394 .alignment = abi_align, 20395 .address_space = try sema.interpretBuiltinType(block, operand_src, address_space_val, std.builtin.AddressSpace), 20396 .is_allowzero = is_allowzero_val.toBool(), 20397 }, 20398 }); 20399 return Air.internedToRef(ty.toIntern()); 20400 }, 20401 .array => { 20402 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20403 const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20404 ip, 20405 try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), 20406 ).?); 20407 const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20408 ip, 20409 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), 20410 ).?); 20411 const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20412 ip, 20413 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls), 20414 ).?); 20415 20416 const len = try len_val.toUnsignedIntSema(pt); 20417 const child_ty = child_val.toType(); 20418 const sentinel = if (sentinel_val.optionalValue(zcu)) |p| blk: { 20419 const ptr_ty = try pt.singleMutPtrType(child_ty); 20420 try sema.checkSentinelType(block, src, child_ty); 20421 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?; 20422 } else null; 20423 20424 const ty = try pt.arrayType(.{ 20425 .len = len, 20426 .sentinel = if (sentinel) |s| s.toIntern() else .none, 20427 .child = child_ty.toIntern(), 20428 }); 20429 return Air.internedToRef(ty.toIntern()); 20430 }, 20431 .optional => { 20432 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20433 const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20434 ip, 20435 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), 20436 ).?); 20437 20438 const child_ty = child_val.toType(); 20439 20440 const ty = try pt.optionalType(child_ty.toIntern()); 20441 return Air.internedToRef(ty.toIntern()); 20442 }, 20443 .error_union => { 20444 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20445 const error_set_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20446 ip, 20447 try ip.getOrPutString(gpa, pt.tid, "error_set", .no_embedded_nulls), 20448 ).?); 20449 const payload_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20450 ip, 20451 try ip.getOrPutString(gpa, pt.tid, "payload", .no_embedded_nulls), 20452 ).?); 20453 20454 const error_set_ty = error_set_val.toType(); 20455 const payload_ty = payload_val.toType(); 20456 20457 if (error_set_ty.zigTypeTag(zcu) != .error_set) { 20458 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{}); 20459 } 20460 20461 const ty = try pt.errorUnionType(error_set_ty, payload_ty); 20462 return Air.internedToRef(ty.toIntern()); 20463 }, 20464 .error_set => { 20465 const payload_val = Value.fromInterned(union_val.val).optionalValue(zcu) orelse 20466 return .anyerror_type; 20467 20468 const names_val = try sema.derefSliceAsArray(block, src, payload_val, .{ .simple = .error_set_contents }); 20469 20470 const len = try sema.usizeCast(block, src, names_val.typeOf(zcu).arrayLen(zcu)); 20471 var names: InferredErrorSet.NameMap = .{}; 20472 try names.ensureUnusedCapacity(sema.arena, len); 20473 for (0..len) |i| { 20474 const elem_val = try names_val.elemValue(pt, i); 20475 const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); 20476 const name_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( 20477 ip, 20478 try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), 20479 ).?); 20480 20481 const name = try sema.sliceToIpString(block, src, name_val, .{ .simple = .error_set_contents }); 20482 _ = try pt.getErrorValue(name); 20483 const gop = names.getOrPutAssumeCapacity(name); 20484 if (gop.found_existing) { 20485 return sema.fail(block, src, "duplicate error '{f}'", .{ 20486 name.fmt(ip), 20487 }); 20488 } 20489 } 20490 20491 const ty = try pt.errorSetFromUnsortedNames(names.keys()); 20492 return Air.internedToRef(ty.toIntern()); 20493 }, 20494 .@"struct" => { 20495 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20496 const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20497 ip, 20498 try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls), 20499 ).?); 20500 const backing_integer_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20501 ip, 20502 try ip.getOrPutString(gpa, pt.tid, "backing_integer", .no_embedded_nulls), 20503 ).?); 20504 const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20505 ip, 20506 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), 20507 ).?); 20508 const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20509 ip, 20510 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), 20511 ).?); 20512 const is_tuple_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20513 ip, 20514 try ip.getOrPutString(gpa, pt.tid, "is_tuple", .no_embedded_nulls), 20515 ).?); 20516 20517 const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout); 20518 20519 // Decls 20520 if (try decls_val.sliceLen(pt) > 0) { 20521 return sema.fail(block, src, "reified structs must have no decls", .{}); 20522 } 20523 20524 if (layout != .@"packed" and !backing_integer_val.isNull(zcu)) { 20525 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); 20526 } 20527 20528 const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .struct_fields }); 20529 20530 if (is_tuple_val.toBool()) { 20531 switch (layout) { 20532 .@"extern" => return sema.fail(block, src, "extern tuples are not supported", .{}), 20533 .@"packed" => return sema.fail(block, src, "packed tuples are not supported", .{}), 20534 .auto => {}, 20535 } 20536 return sema.reifyTuple(block, src, fields_arr); 20537 } else { 20538 return sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_arr, name_strategy); 20539 } 20540 }, 20541 .@"enum" => { 20542 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20543 const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20544 ip, 20545 try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls), 20546 ).?); 20547 const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20548 ip, 20549 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), 20550 ).?); 20551 const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20552 ip, 20553 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), 20554 ).?); 20555 const is_exhaustive_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20556 ip, 20557 try ip.getOrPutString(gpa, pt.tid, "is_exhaustive", .no_embedded_nulls), 20558 ).?); 20559 20560 if (try decls_val.sliceLen(pt) > 0) { 20561 return sema.fail(block, src, "reified enums must have no decls", .{}); 20562 } 20563 20564 const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .enum_fields }); 20565 20566 return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_arr, name_strategy); 20567 }, 20568 .@"opaque" => { 20569 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20570 const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20571 ip, 20572 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), 20573 ).?); 20574 20575 // Decls 20576 if (try decls_val.sliceLen(pt) > 0) { 20577 return sema.fail(block, src, "reified opaque must have no decls", .{}); 20578 } 20579 20580 const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, .{ 20581 .key = .{ .reified = .{ 20582 .zir_index = try block.trackZir(inst), 20583 } }, 20584 })) { 20585 .existing => |ty| { 20586 try sema.addTypeReferenceEntry(src, ty); 20587 return Air.internedToRef(ty); 20588 }, 20589 .wip => |wip| wip, 20590 }; 20591 errdefer wip_ty.cancel(ip, pt.tid); 20592 20593 const type_name = try sema.createTypeName( 20594 block, 20595 name_strategy, 20596 "opaque", 20597 inst, 20598 wip_ty.index, 20599 ); 20600 wip_ty.setName(ip, type_name.name, type_name.nav); 20601 20602 const new_namespace_index = try pt.createNamespace(.{ 20603 .parent = block.namespace.toOptional(), 20604 .owner_type = wip_ty.index, 20605 .file_scope = block.getFileScopeIndex(zcu), 20606 .generation = zcu.generation, 20607 }); 20608 20609 try sema.addTypeReferenceEntry(src, wip_ty.index); 20610 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 20611 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 20612 }, 20613 .@"union" => { 20614 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20615 const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20616 ip, 20617 try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls), 20618 ).?); 20619 const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20620 ip, 20621 try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls), 20622 ).?); 20623 const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20624 ip, 20625 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), 20626 ).?); 20627 const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20628 ip, 20629 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), 20630 ).?); 20631 20632 if (try decls_val.sliceLen(pt) > 0) { 20633 return sema.fail(block, src, "reified unions must have no decls", .{}); 20634 } 20635 const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout); 20636 20637 const has_tag = tag_type_val.optionalValue(zcu) != null; 20638 20639 if (has_tag) { 20640 switch (layout) { 20641 .@"extern" => return sema.fail(block, src, "extern union does not support enum tag type", .{}), 20642 .@"packed" => return sema.fail(block, src, "packed union does not support enum tag type", .{}), 20643 .auto => {}, 20644 } 20645 } 20646 20647 const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .union_fields }); 20648 20649 return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_arr, name_strategy); 20650 }, 20651 .@"fn" => { 20652 const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); 20653 const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20654 ip, 20655 try ip.getOrPutString(gpa, pt.tid, "calling_convention", .no_embedded_nulls), 20656 ).?); 20657 const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20658 ip, 20659 try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls), 20660 ).?); 20661 const is_var_args_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20662 ip, 20663 try ip.getOrPutString(gpa, pt.tid, "is_var_args", .no_embedded_nulls), 20664 ).?); 20665 const return_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20666 ip, 20667 try ip.getOrPutString(gpa, pt.tid, "return_type", .no_embedded_nulls), 20668 ).?); 20669 const params_slice_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( 20670 ip, 20671 try ip.getOrPutString(gpa, pt.tid, "params", .no_embedded_nulls), 20672 ).?); 20673 20674 const is_generic = is_generic_val.toBool(); 20675 if (is_generic) { 20676 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{}); 20677 } 20678 20679 const is_var_args = is_var_args_val.toBool(); 20680 const cc = try sema.analyzeValueAsCallconv(block, src, calling_convention_val); 20681 if (is_var_args) { 20682 try sema.checkCallConvSupportsVarArgs(block, src, cc); 20683 } 20684 20685 const return_type = return_type_val.optionalValue(zcu) orelse 20686 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); 20687 20688 const params_val = try sema.derefSliceAsArray(block, operand_src, params_slice_val, .{ .simple = .function_parameters }); 20689 20690 const args_len = try sema.usizeCast(block, src, params_val.typeOf(zcu).arrayLen(zcu)); 20691 const param_types = try sema.arena.alloc(InternPool.Index, args_len); 20692 20693 var noalias_bits: u32 = 0; 20694 for (param_types, 0..) |*param_type, i| { 20695 const elem_val = try params_val.elemValue(pt, i); 20696 const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); 20697 const param_is_generic_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( 20698 ip, 20699 try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls), 20700 ).?); 20701 const param_is_noalias_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( 20702 ip, 20703 try ip.getOrPutString(gpa, pt.tid, "is_noalias", .no_embedded_nulls), 20704 ).?); 20705 const opt_param_type_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( 20706 ip, 20707 try ip.getOrPutString(gpa, pt.tid, "type", .no_embedded_nulls), 20708 ).?); 20709 20710 if (param_is_generic_val.toBool()) { 20711 return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{}); 20712 } 20713 20714 const param_type_val = opt_param_type_val.optionalValue(zcu) orelse 20715 return sema.fail(block, src, "Type.Fn.Param.type must be non-null for @Type", .{}); 20716 param_type.* = param_type_val.toIntern(); 20717 20718 if (param_is_noalias_val.toBool()) { 20719 if (!Type.fromInterned(param_type.*).isPtrAtRuntime(zcu)) { 20720 return sema.fail(block, src, "non-pointer parameter declared noalias", .{}); 20721 } 20722 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse 20723 return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 20724 } 20725 } 20726 20727 const ty = try pt.funcType(.{ 20728 .param_types = param_types, 20729 .noalias_bits = noalias_bits, 20730 .return_type = return_type.toIntern(), 20731 .cc = cc, 20732 .is_var_args = is_var_args, 20733 }); 20734 return Air.internedToRef(ty.toIntern()); 20735 }, 20736 .frame => return sema.failWithUseOfAsync(block, src), 20737 } 20738 } 20739 20740 fn reifyEnum( 20741 sema: *Sema, 20742 block: *Block, 20743 inst: Zir.Inst.Index, 20744 src: LazySrcLoc, 20745 tag_ty: Type, 20746 is_exhaustive: bool, 20747 fields_val: Value, 20748 name_strategy: Zir.Inst.NameStrategy, 20749 ) CompileError!Air.Inst.Ref { 20750 const pt = sema.pt; 20751 const zcu = pt.zcu; 20752 const gpa = sema.gpa; 20753 const ip = &zcu.intern_pool; 20754 20755 // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`. 20756 20757 const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); 20758 20759 // The validation work here is non-trivial, and it's possible the type already exists. 20760 // So in this first pass, let's just construct a hash to optimize for this case. If the 20761 // inputs turn out to be invalid, we can cancel the WIP type later. 20762 20763 // For deduplication purposes, we must create a hash including all details of this type. 20764 // TODO: use a longer hash! 20765 var hasher = std.hash.Wyhash.init(0); 20766 std.hash.autoHash(&hasher, tag_ty.toIntern()); 20767 std.hash.autoHash(&hasher, is_exhaustive); 20768 std.hash.autoHash(&hasher, fields_len); 20769 20770 for (0..fields_len) |field_idx| { 20771 const field_info = try fields_val.elemValue(pt, field_idx); 20772 20773 const field_name_val = try field_info.fieldValue(pt, 0); 20774 const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1)); 20775 20776 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .enum_field_name }); 20777 20778 std.hash.autoHash(&hasher, .{ 20779 field_name, 20780 field_value_val.toIntern(), 20781 }); 20782 } 20783 20784 const tracked_inst = try block.trackZir(inst); 20785 20786 const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{ 20787 .has_values = true, 20788 .tag_mode = if (is_exhaustive) .explicit else .nonexhaustive, 20789 .fields_len = fields_len, 20790 .key = .{ .reified = .{ 20791 .zir_index = tracked_inst, 20792 .type_hash = hasher.final(), 20793 } }, 20794 }, false)) { 20795 .wip => |wip| wip, 20796 .existing => |ty| { 20797 try sema.declareDependency(.{ .interned = ty }); 20798 try sema.addTypeReferenceEntry(src, ty); 20799 return Air.internedToRef(ty); 20800 }, 20801 }; 20802 var done = false; 20803 errdefer if (!done) wip_ty.cancel(ip, pt.tid); 20804 20805 if (tag_ty.zigTypeTag(zcu) != .int) { 20806 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); 20807 } 20808 20809 const type_name = try sema.createTypeName( 20810 block, 20811 name_strategy, 20812 "enum", 20813 inst, 20814 wip_ty.index, 20815 ); 20816 wip_ty.setName(ip, type_name.name, type_name.nav); 20817 20818 const new_namespace_index = try pt.createNamespace(.{ 20819 .parent = block.namespace.toOptional(), 20820 .owner_type = wip_ty.index, 20821 .file_scope = block.getFileScopeIndex(zcu), 20822 .generation = zcu.generation, 20823 }); 20824 20825 try sema.declareDependency(.{ .interned = wip_ty.index }); 20826 try sema.addTypeReferenceEntry(src, wip_ty.index); 20827 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 20828 wip_ty.prepare(ip, new_namespace_index); 20829 wip_ty.setTagTy(ip, tag_ty.toIntern()); 20830 done = true; 20831 20832 for (0..fields_len) |field_idx| { 20833 const field_info = try fields_val.elemValue(pt, field_idx); 20834 20835 const field_name_val = try field_info.fieldValue(pt, 0); 20836 const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1)); 20837 20838 // Don't pass a reason; first loop acts as an assertion that this is valid. 20839 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); 20840 20841 if (!try sema.intFitsInType(field_value_val, tag_ty, null)) { 20842 // TODO: better source location 20843 return sema.fail(block, src, "field '{f}' with enumeration value '{f}' is too large for backing int type '{f}'", .{ 20844 field_name.fmt(ip), 20845 field_value_val.fmtValueSema(pt, sema), 20846 tag_ty.fmt(pt), 20847 }); 20848 } 20849 20850 const coerced_field_val = try pt.getCoerced(field_value_val, tag_ty); 20851 if (wip_ty.nextField(ip, field_name, coerced_field_val.toIntern())) |conflict| { 20852 return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) { 20853 .name => msg: { 20854 const msg = try sema.errMsg(src, "duplicate enum field '{f}'", .{field_name.fmt(ip)}); 20855 errdefer msg.destroy(gpa); 20856 _ = conflict.prev_field_idx; // TODO: this note is incorrect 20857 try sema.errNote(src, msg, "other field here", .{}); 20858 break :msg msg; 20859 }, 20860 .value => msg: { 20861 const msg = try sema.errMsg(src, "enum tag value {f} already taken", .{field_value_val.fmtValueSema(pt, sema)}); 20862 errdefer msg.destroy(gpa); 20863 _ = conflict.prev_field_idx; // TODO: this note is incorrect 20864 try sema.errNote(src, msg, "other enum tag value here", .{}); 20865 break :msg msg; 20866 }, 20867 }); 20868 } 20869 } 20870 20871 if (!is_exhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(zcu)) { 20872 return sema.fail(block, src, "non-exhaustive enum specified every value", .{}); 20873 } 20874 20875 codegen_type: { 20876 if (zcu.comp.config.use_llvm) break :codegen_type; 20877 if (block.ownerModule().strip) break :codegen_type; 20878 // This job depends on any resolve_type_fully jobs queued up before it. 20879 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 20880 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 20881 } 20882 return Air.internedToRef(wip_ty.index); 20883 } 20884 20885 fn reifyUnion( 20886 sema: *Sema, 20887 block: *Block, 20888 inst: Zir.Inst.Index, 20889 src: LazySrcLoc, 20890 layout: std.builtin.Type.ContainerLayout, 20891 opt_tag_type_val: Value, 20892 fields_val: Value, 20893 name_strategy: Zir.Inst.NameStrategy, 20894 ) CompileError!Air.Inst.Ref { 20895 const pt = sema.pt; 20896 const zcu = pt.zcu; 20897 const gpa = sema.gpa; 20898 const ip = &zcu.intern_pool; 20899 20900 // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`. 20901 20902 const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); 20903 20904 // The validation work here is non-trivial, and it's possible the type already exists. 20905 // So in this first pass, let's just construct a hash to optimize for this case. If the 20906 // inputs turn out to be invalid, we can cancel the WIP type later. 20907 20908 // For deduplication purposes, we must create a hash including all details of this type. 20909 // TODO: use a longer hash! 20910 var hasher = std.hash.Wyhash.init(0); 20911 std.hash.autoHash(&hasher, layout); 20912 std.hash.autoHash(&hasher, opt_tag_type_val.toIntern()); 20913 std.hash.autoHash(&hasher, fields_len); 20914 20915 for (0..fields_len) |field_idx| { 20916 const field_info = try fields_val.elemValue(pt, field_idx); 20917 20918 const field_name_val = try field_info.fieldValue(pt, 0); 20919 const field_type_val = try field_info.fieldValue(pt, 1); 20920 const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 2)); 20921 20922 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .union_field_name }); 20923 std.hash.autoHash(&hasher, .{ 20924 field_name, 20925 field_type_val.toIntern(), 20926 field_align_val.toIntern(), 20927 }); 20928 } 20929 20930 const tracked_inst = try block.trackZir(inst); 20931 20932 const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{ 20933 .flags = .{ 20934 .layout = layout, 20935 .status = .none, 20936 .runtime_tag = if (opt_tag_type_val.optionalValue(zcu) != null) 20937 .tagged 20938 else if (layout != .auto) 20939 .none 20940 else switch (block.wantSafeTypes()) { 20941 true => .safety, 20942 false => .none, 20943 }, 20944 .any_aligned_fields = layout != .@"packed", 20945 .requires_comptime = .unknown, 20946 .assumed_runtime_bits = false, 20947 .assumed_pointer_aligned = false, 20948 .alignment = .none, 20949 }, 20950 .fields_len = fields_len, 20951 .enum_tag_ty = .none, // set later because not yet validated 20952 .field_types = &.{}, // set later 20953 .field_aligns = &.{}, // set later 20954 .key = .{ .reified = .{ 20955 .zir_index = tracked_inst, 20956 .type_hash = hasher.final(), 20957 } }, 20958 }, false)) { 20959 .wip => |wip| wip, 20960 .existing => |ty| { 20961 try sema.declareDependency(.{ .interned = ty }); 20962 try sema.addTypeReferenceEntry(src, ty); 20963 return Air.internedToRef(ty); 20964 }, 20965 }; 20966 errdefer wip_ty.cancel(ip, pt.tid); 20967 20968 const type_name = try sema.createTypeName( 20969 block, 20970 name_strategy, 20971 "union", 20972 inst, 20973 wip_ty.index, 20974 ); 20975 wip_ty.setName(ip, type_name.name, type_name.nav); 20976 20977 const loaded_union = ip.loadUnionType(wip_ty.index); 20978 20979 const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(zcu)) |tag_type_val| tag_ty: { 20980 switch (ip.indexToKey(tag_type_val.toIntern())) { 20981 .enum_type => {}, 20982 else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), 20983 } 20984 const enum_tag_ty = tag_type_val.toType(); 20985 20986 // We simply track which fields of the tag type have been seen. 20987 const tag_ty_fields_len = enum_tag_ty.enumFieldCount(zcu); 20988 var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len); 20989 20990 for (0..fields_len) |field_idx| { 20991 const field_info = try fields_val.elemValue(pt, field_idx); 20992 20993 const field_name_val = try field_info.fieldValue(pt, 0); 20994 const field_type_val = try field_info.fieldValue(pt, 1); 20995 const field_alignment_val = try field_info.fieldValue(pt, 2); 20996 20997 // Don't pass a reason; first loop acts as an assertion that this is valid. 20998 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); 20999 21000 const enum_index = enum_tag_ty.enumFieldIndex(field_name, zcu) orelse { 21001 // TODO: better source location 21002 return sema.fail(block, src, "no field named '{f}' in enum '{f}'", .{ 21003 field_name.fmt(ip), enum_tag_ty.fmt(pt), 21004 }); 21005 }; 21006 if (seen_tags.isSet(enum_index)) { 21007 // TODO: better source location 21008 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)}); 21009 } 21010 seen_tags.set(enum_index); 21011 21012 loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern(); 21013 const byte_align = try field_alignment_val.toUnsignedIntSema(pt); 21014 if (layout == .@"packed") { 21015 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{}); 21016 } else { 21017 loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); 21018 } 21019 } 21020 21021 if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: { 21022 const msg = try sema.errMsg(src, "enum fields missing in union", .{}); 21023 errdefer msg.destroy(gpa); 21024 var it = seen_tags.iterator(.{ .kind = .unset }); 21025 while (it.next()) |enum_index| { 21026 const field_name = enum_tag_ty.enumFieldName(enum_index, zcu); 21027 try sema.addFieldErrNote(enum_tag_ty, enum_index, msg, "field '{f}' missing, declared here", .{ 21028 field_name.fmt(ip), 21029 }); 21030 } 21031 try sema.addDeclaredHereNote(msg, enum_tag_ty); 21032 break :msg msg; 21033 }); 21034 21035 break :tag_ty .{ enum_tag_ty.toIntern(), true }; 21036 } else tag_ty: { 21037 // We must track field names and set up the tag type ourselves. 21038 var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty; 21039 try field_names.ensureTotalCapacity(sema.arena, fields_len); 21040 21041 for (0..fields_len) |field_idx| { 21042 const field_info = try fields_val.elemValue(pt, field_idx); 21043 21044 const field_name_val = try field_info.fieldValue(pt, 0); 21045 const field_type_val = try field_info.fieldValue(pt, 1); 21046 const field_alignment_val = try field_info.fieldValue(pt, 2); 21047 21048 // Don't pass a reason; first loop acts as an assertion that this is valid. 21049 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); 21050 const gop = field_names.getOrPutAssumeCapacity(field_name); 21051 if (gop.found_existing) { 21052 // TODO: better source location 21053 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)}); 21054 } 21055 21056 loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern(); 21057 const byte_align = try field_alignment_val.toUnsignedIntSema(pt); 21058 if (layout == .@"packed") { 21059 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{}); 21060 } else { 21061 loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); 21062 } 21063 } 21064 21065 const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name.name); 21066 break :tag_ty .{ enum_tag_ty, false }; 21067 }; 21068 errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error 21069 21070 for (loaded_union.field_types.get(ip)) |field_ty_ip| { 21071 const field_ty: Type = .fromInterned(field_ty_ip); 21072 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 21073 return sema.failWithOwnedErrorMsg(block, msg: { 21074 const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 21075 errdefer msg.destroy(gpa); 21076 21077 try sema.addDeclaredHereNote(msg, field_ty); 21078 break :msg msg; 21079 }); 21080 } 21081 if (layout == .@"extern" and !try sema.validateExternType(field_ty, .union_field)) { 21082 return sema.failWithOwnedErrorMsg(block, msg: { 21083 const msg = try sema.errMsg(src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 21084 errdefer msg.destroy(gpa); 21085 21086 try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .union_field); 21087 21088 try sema.addDeclaredHereNote(msg, field_ty); 21089 break :msg msg; 21090 }); 21091 } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { 21092 return sema.failWithOwnedErrorMsg(block, msg: { 21093 const msg = try sema.errMsg(src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 21094 errdefer msg.destroy(gpa); 21095 21096 try sema.explainWhyTypeIsNotPacked(msg, src, field_ty); 21097 21098 try sema.addDeclaredHereNote(msg, field_ty); 21099 break :msg msg; 21100 }); 21101 } 21102 } 21103 21104 loaded_union.setTagType(ip, enum_tag_ty); 21105 loaded_union.setStatus(ip, .have_field_types); 21106 21107 const new_namespace_index = try pt.createNamespace(.{ 21108 .parent = block.namespace.toOptional(), 21109 .owner_type = wip_ty.index, 21110 .file_scope = block.getFileScopeIndex(zcu), 21111 .generation = zcu.generation, 21112 }); 21113 21114 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 21115 codegen_type: { 21116 if (zcu.comp.config.use_llvm) break :codegen_type; 21117 if (block.ownerModule().strip) break :codegen_type; 21118 // This job depends on any resolve_type_fully jobs queued up before it. 21119 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 21120 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 21121 } 21122 try sema.declareDependency(.{ .interned = wip_ty.index }); 21123 try sema.addTypeReferenceEntry(src, wip_ty.index); 21124 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 21125 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 21126 } 21127 21128 fn reifyTuple( 21129 sema: *Sema, 21130 block: *Block, 21131 src: LazySrcLoc, 21132 fields_val: Value, 21133 ) CompileError!Air.Inst.Ref { 21134 const pt = sema.pt; 21135 const zcu = pt.zcu; 21136 const gpa = sema.gpa; 21137 const ip = &zcu.intern_pool; 21138 21139 const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); 21140 21141 const types = try sema.arena.alloc(InternPool.Index, fields_len); 21142 const inits = try sema.arena.alloc(InternPool.Index, fields_len); 21143 21144 for (types, inits, 0..) |*field_ty, *field_init, field_idx| { 21145 const field_info = try fields_val.elemValue(pt, field_idx); 21146 21147 const field_name_val = try field_info.fieldValue(pt, 0); 21148 const field_type_val = try field_info.fieldValue(pt, 1); 21149 const field_default_value_val = try field_info.fieldValue(pt, 2); 21150 const field_is_comptime_val = try field_info.fieldValue(pt, 3); 21151 const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4)); 21152 21153 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .tuple_field_name }); 21154 const field_type = field_type_val.toType(); 21155 const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: { 21156 const ptr_ty = try pt.singleConstPtrType(field_type_val.toType()); 21157 // We need to do this deref here, so we won't check for this error case later on. 21158 const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( 21159 block, 21160 src, 21161 .{ .simple = .tuple_field_default_value }, 21162 ); 21163 // Resolve the value so that lazy values do not create distinct types. 21164 break :d (try sema.resolveLazyValue(val)).toIntern(); 21165 } else .none; 21166 21167 const field_name_index = field_name.toUnsigned(ip) orelse return sema.fail( 21168 block, 21169 src, 21170 "tuple cannot have non-numeric field '{f}'", 21171 .{field_name.fmt(ip)}, 21172 ); 21173 if (field_name_index != field_idx) { 21174 return sema.fail( 21175 block, 21176 src, 21177 "tuple field name '{d}' does not match field index {d}", 21178 .{ field_name_index, field_idx }, 21179 ); 21180 } 21181 21182 try sema.validateTupleFieldType(block, field_type, src); 21183 21184 { 21185 const alignment_ok = ok: { 21186 if (field_alignment_val.toIntern() == .zero) break :ok true; 21187 const given_align = try field_alignment_val.getUnsignedIntSema(pt) orelse break :ok false; 21188 const abi_align = (try field_type.abiAlignmentSema(pt)).toByteUnits() orelse 0; 21189 break :ok abi_align == given_align; 21190 }; 21191 if (!alignment_ok) { 21192 return sema.fail(block, src, "tuple fields cannot specify alignment", .{}); 21193 } 21194 } 21195 21196 if (field_is_comptime_val.toBool() and field_default_value == .none) { 21197 return sema.fail(block, src, "comptime field without default initialization value", .{}); 21198 } 21199 21200 if (!field_is_comptime_val.toBool() and field_default_value != .none) { 21201 return sema.fail(block, src, "non-comptime tuple fields cannot specify default initialization value", .{}); 21202 } 21203 21204 const default_or_opv: InternPool.Index = default: { 21205 if (field_default_value != .none) { 21206 break :default field_default_value; 21207 } 21208 if (try sema.typeHasOnePossibleValue(field_type)) |opv| { 21209 break :default opv.toIntern(); 21210 } 21211 break :default .none; 21212 }; 21213 21214 field_ty.* = field_type.toIntern(); 21215 field_init.* = default_or_opv; 21216 } 21217 21218 return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{ 21219 .types = types, 21220 .values = inits, 21221 })); 21222 } 21223 21224 fn reifyStruct( 21225 sema: *Sema, 21226 block: *Block, 21227 inst: Zir.Inst.Index, 21228 src: LazySrcLoc, 21229 layout: std.builtin.Type.ContainerLayout, 21230 opt_backing_int_val: Value, 21231 fields_val: Value, 21232 name_strategy: Zir.Inst.NameStrategy, 21233 ) CompileError!Air.Inst.Ref { 21234 const pt = sema.pt; 21235 const zcu = pt.zcu; 21236 const gpa = sema.gpa; 21237 const ip = &zcu.intern_pool; 21238 21239 // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`. 21240 21241 const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); 21242 21243 // The validation work here is non-trivial, and it's possible the type already exists. 21244 // So in this first pass, let's just construct a hash to optimize for this case. If the 21245 // inputs turn out to be invalid, we can cancel the WIP type later. 21246 21247 // For deduplication purposes, we must create a hash including all details of this type. 21248 // TODO: use a longer hash! 21249 var hasher = std.hash.Wyhash.init(0); 21250 std.hash.autoHash(&hasher, layout); 21251 std.hash.autoHash(&hasher, opt_backing_int_val.toIntern()); 21252 std.hash.autoHash(&hasher, fields_len); 21253 21254 var any_comptime_fields = false; 21255 var any_default_inits = false; 21256 21257 for (0..fields_len) |field_idx| { 21258 const field_info = try fields_val.elemValue(pt, field_idx); 21259 21260 const field_name_val = try field_info.fieldValue(pt, 0); 21261 const field_type_val = try field_info.fieldValue(pt, 1); 21262 const field_default_value_val = try field_info.fieldValue(pt, 2); 21263 const field_is_comptime_val = try field_info.fieldValue(pt, 3); 21264 const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4)); 21265 21266 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .struct_field_name }); 21267 const field_is_comptime = field_is_comptime_val.toBool(); 21268 const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: { 21269 const ptr_ty = try pt.singleConstPtrType(field_type_val.toType()); 21270 // We need to do this deref here, so we won't check for this error case later on. 21271 const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( 21272 block, 21273 src, 21274 .{ .simple = .struct_field_default_value }, 21275 ); 21276 // Resolve the value so that lazy values do not create distinct types. 21277 break :d (try sema.resolveLazyValue(val)).toIntern(); 21278 } else .none; 21279 21280 std.hash.autoHash(&hasher, .{ 21281 field_name, 21282 field_type_val.toIntern(), 21283 field_default_value, 21284 field_is_comptime, 21285 field_alignment_val.toIntern(), 21286 }); 21287 21288 if (field_is_comptime) any_comptime_fields = true; 21289 if (field_default_value != .none) any_default_inits = true; 21290 } 21291 21292 const tracked_inst = try block.trackZir(inst); 21293 21294 const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{ 21295 .layout = layout, 21296 .fields_len = fields_len, 21297 .known_non_opv = false, 21298 .requires_comptime = .unknown, 21299 .any_comptime_fields = any_comptime_fields, 21300 .any_default_inits = any_default_inits, 21301 .any_aligned_fields = layout != .@"packed", 21302 .inits_resolved = true, 21303 .key = .{ .reified = .{ 21304 .zir_index = tracked_inst, 21305 .type_hash = hasher.final(), 21306 } }, 21307 }, false)) { 21308 .wip => |wip| wip, 21309 .existing => |ty| { 21310 try sema.declareDependency(.{ .interned = ty }); 21311 try sema.addTypeReferenceEntry(src, ty); 21312 return Air.internedToRef(ty); 21313 }, 21314 }; 21315 errdefer wip_ty.cancel(ip, pt.tid); 21316 21317 const type_name = try sema.createTypeName( 21318 block, 21319 name_strategy, 21320 "struct", 21321 inst, 21322 wip_ty.index, 21323 ); 21324 wip_ty.setName(ip, type_name.name, type_name.nav); 21325 21326 const struct_type = ip.loadStructType(wip_ty.index); 21327 21328 for (0..fields_len) |field_idx| { 21329 const field_info = try fields_val.elemValue(pt, field_idx); 21330 21331 const field_name_val = try field_info.fieldValue(pt, 0); 21332 const field_type_val = try field_info.fieldValue(pt, 1); 21333 const field_default_value_val = try field_info.fieldValue(pt, 2); 21334 const field_is_comptime_val = try field_info.fieldValue(pt, 3); 21335 const field_alignment_val = try field_info.fieldValue(pt, 4); 21336 21337 const field_ty = field_type_val.toType(); 21338 // Don't pass a reason; first loop acts as an assertion that this is valid. 21339 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); 21340 if (struct_type.addFieldName(ip, field_name)) |prev_index| { 21341 _ = prev_index; // TODO: better source location 21342 return sema.fail(block, src, "duplicate struct field name {f}", .{field_name.fmt(ip)}); 21343 } 21344 21345 if (!try sema.intFitsInType(field_alignment_val, align_ty, null)) { 21346 return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)}); 21347 } 21348 const byte_align = try field_alignment_val.toUnsignedIntSema(pt); 21349 if (layout == .@"packed") { 21350 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed struct field must be set to 0", .{}); 21351 } else { 21352 struct_type.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); 21353 } 21354 21355 const field_is_comptime = field_is_comptime_val.toBool(); 21356 if (field_is_comptime) { 21357 assert(any_comptime_fields); 21358 switch (layout) { 21359 .@"extern" => return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}), 21360 .@"packed" => return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}), 21361 .auto => struct_type.setFieldComptime(ip, field_idx), 21362 } 21363 } 21364 21365 const field_default: InternPool.Index = d: { 21366 if (!any_default_inits) break :d .none; 21367 const ptr_val = field_default_value_val.optionalValue(zcu) orelse break :d .none; 21368 const ptr_ty = try pt.singleConstPtrType(field_ty); 21369 // Asserted comptime-dereferencable above. 21370 const val = (try sema.pointerDeref(block, src, ptr_val, ptr_ty)).?; 21371 // We already resolved this for deduplication, so we may as well do it now. 21372 break :d (try sema.resolveLazyValue(val)).toIntern(); 21373 }; 21374 21375 if (field_is_comptime and field_default == .none) { 21376 return sema.fail(block, src, "comptime field without default initialization value", .{}); 21377 } 21378 21379 struct_type.field_types.get(ip)[field_idx] = field_type_val.toIntern(); 21380 if (field_default != .none) { 21381 struct_type.field_inits.get(ip)[field_idx] = field_default; 21382 } 21383 21384 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 21385 return sema.failWithOwnedErrorMsg(block, msg: { 21386 const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 21387 errdefer msg.destroy(gpa); 21388 21389 try sema.addDeclaredHereNote(msg, field_ty); 21390 break :msg msg; 21391 }); 21392 } 21393 if (field_ty.zigTypeTag(zcu) == .noreturn) { 21394 return sema.failWithOwnedErrorMsg(block, msg: { 21395 const msg = try sema.errMsg(src, "struct fields cannot be 'noreturn'", .{}); 21396 errdefer msg.destroy(gpa); 21397 21398 try sema.addDeclaredHereNote(msg, field_ty); 21399 break :msg msg; 21400 }); 21401 } 21402 if (layout == .@"extern" and !try sema.validateExternType(field_ty, .struct_field)) { 21403 return sema.failWithOwnedErrorMsg(block, msg: { 21404 const msg = try sema.errMsg(src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 21405 errdefer msg.destroy(gpa); 21406 21407 try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .struct_field); 21408 21409 try sema.addDeclaredHereNote(msg, field_ty); 21410 break :msg msg; 21411 }); 21412 } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { 21413 return sema.failWithOwnedErrorMsg(block, msg: { 21414 const msg = try sema.errMsg(src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 21415 errdefer msg.destroy(gpa); 21416 21417 try sema.explainWhyTypeIsNotPacked(msg, src, field_ty); 21418 21419 try sema.addDeclaredHereNote(msg, field_ty); 21420 break :msg msg; 21421 }); 21422 } 21423 } 21424 21425 if (layout == .@"packed") { 21426 var fields_bit_sum: u64 = 0; 21427 for (0..struct_type.field_types.len) |field_idx| { 21428 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_idx]); 21429 field_ty.resolveLayout(pt) catch |err| switch (err) { 21430 error.AnalysisFail => { 21431 const msg = sema.err orelse return err; 21432 try sema.errNote(src, msg, "while checking a field of this struct", .{}); 21433 return err; 21434 }, 21435 else => return err, 21436 }; 21437 fields_bit_sum += field_ty.bitSize(zcu); 21438 } 21439 21440 if (opt_backing_int_val.optionalValue(zcu)) |backing_int_val| { 21441 const backing_int_ty = backing_int_val.toType(); 21442 try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); 21443 struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); 21444 } else { 21445 const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum)); 21446 struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); 21447 } 21448 } 21449 21450 const new_namespace_index = try pt.createNamespace(.{ 21451 .parent = block.namespace.toOptional(), 21452 .owner_type = wip_ty.index, 21453 .file_scope = block.getFileScopeIndex(zcu), 21454 .generation = zcu.generation, 21455 }); 21456 21457 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 21458 codegen_type: { 21459 if (zcu.comp.config.use_llvm) break :codegen_type; 21460 if (block.ownerModule().strip) break :codegen_type; 21461 // This job depends on any resolve_type_fully jobs queued up before it. 21462 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 21463 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 21464 } 21465 try sema.declareDependency(.{ .interned = wip_ty.index }); 21466 try sema.addTypeReferenceEntry(src, wip_ty.index); 21467 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 21468 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); 21469 } 21470 21471 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { 21472 const pt = sema.pt; 21473 const va_list_ty = try sema.getBuiltinType(src, .VaList); 21474 const va_list_ptr = try pt.singleMutPtrType(va_list_ty); 21475 21476 const inst = try sema.resolveInst(zir_ref); 21477 return sema.coerce(block, va_list_ptr, inst, src); 21478 } 21479 21480 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21481 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 21482 const src = block.nodeOffset(extra.node); 21483 const va_list_src = block.builtinCallArgSrc(extra.node, 0); 21484 const ty_src = block.builtinCallArgSrc(extra.node, 1); 21485 21486 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs); 21487 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs); 21488 21489 if (!try sema.validateExternType(arg_ty, .param_ty)) { 21490 const msg = msg: { 21491 const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)}); 21492 errdefer msg.destroy(sema.gpa); 21493 21494 try sema.explainWhyTypeIsNotExtern(msg, ty_src, arg_ty, .param_ty); 21495 21496 try sema.addDeclaredHereNote(msg, arg_ty); 21497 break :msg msg; 21498 }; 21499 return sema.failWithOwnedErrorMsg(block, msg); 21500 } 21501 21502 try sema.requireRuntimeBlock(block, src, null); 21503 return block.addTyOp(.c_va_arg, arg_ty, va_list_ref); 21504 } 21505 21506 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21507 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 21508 const src = block.nodeOffset(extra.node); 21509 const va_list_src = block.builtinCallArgSrc(extra.node, 0); 21510 21511 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 21512 const va_list_ty = try sema.getBuiltinType(src, .VaList); 21513 21514 try sema.requireRuntimeBlock(block, src, null); 21515 return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref); 21516 } 21517 21518 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21519 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 21520 const src = block.nodeOffset(extra.node); 21521 const va_list_src = block.builtinCallArgSrc(extra.node, 0); 21522 21523 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 21524 21525 try sema.requireRuntimeBlock(block, src, null); 21526 return block.addUnOp(.c_va_end, va_list_ref); 21527 } 21528 21529 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21530 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 21531 const src = block.nodeOffset(src_node); 21532 21533 const va_list_ty = try sema.getBuiltinType(src, .VaList); 21534 try sema.requireRuntimeBlock(block, src, null); 21535 return block.addInst(.{ 21536 .tag = .c_va_start, 21537 .data = .{ .ty = va_list_ty }, 21538 }); 21539 } 21540 21541 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21542 const pt = sema.pt; 21543 const zcu = pt.zcu; 21544 const ip = &zcu.intern_pool; 21545 21546 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 21547 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21548 const ty = try sema.resolveType(block, ty_src, inst_data.operand); 21549 21550 const type_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{f}", .{ty.fmt(pt)}, .no_embedded_nulls); 21551 return sema.addNullTerminatedStrLit(type_name); 21552 } 21553 21554 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21555 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 21556 const src = block.nodeOffset(inst_data.src_node); 21557 return sema.failWithUseOfAsync(block, src); 21558 } 21559 21560 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21561 const pt = sema.pt; 21562 const zcu = pt.zcu; 21563 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21564 const src = block.nodeOffset(inst_data.src_node); 21565 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21566 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21567 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat"); 21568 const operand = try sema.resolveInst(extra.rhs); 21569 const operand_ty = sema.typeOf(operand); 21570 21571 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 21572 const is_vector = dest_ty.zigTypeTag(zcu) == .vector; 21573 21574 const dest_scalar_ty = dest_ty.scalarType(zcu); 21575 const operand_scalar_ty = operand_ty.scalarType(zcu); 21576 21577 _ = try sema.checkIntType(block, src, dest_scalar_ty); 21578 try sema.checkFloatType(block, operand_src, operand_scalar_ty); 21579 21580 if (try sema.resolveValue(operand)) |operand_val| { 21581 const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate); 21582 return Air.internedToRef(result_val.toIntern()); 21583 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { 21584 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int }); 21585 } 21586 21587 try sema.requireRuntimeBlock(block, src, operand_src); 21588 if (dest_scalar_ty.intInfo(zcu).bits == 0) { 21589 if (block.wantSafety()) { 21590 // Emit an explicit safety check. We can do this one like `abs(x) < 1`. 21591 const abs_ref = try block.addTyOp(.abs, operand_ty, operand); 21592 const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref; 21593 const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern()); 21594 const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref); 21595 try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds); 21596 } 21597 const scalar_val = try pt.intValue(dest_scalar_ty, 0); 21598 if (!is_vector) return Air.internedToRef(scalar_val.toIntern()); 21599 return Air.internedToRef(try pt.intern(.{ .aggregate = .{ 21600 .ty = dest_ty.toIntern(), 21601 .storage = .{ .repeated_elem = scalar_val.toIntern() }, 21602 } })); 21603 } 21604 if (block.wantSafety()) { 21605 try sema.preparePanicId(src, .integer_part_out_of_bounds); 21606 return block.addTyOp(switch (block.float_mode) { 21607 .optimized => .int_from_float_optimized_safe, 21608 .strict => .int_from_float_safe, 21609 }, dest_ty, operand); 21610 } 21611 return block.addTyOp(switch (block.float_mode) { 21612 .optimized => .int_from_float_optimized, 21613 .strict => .int_from_float, 21614 }, dest_ty, operand); 21615 } 21616 21617 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21618 const pt = sema.pt; 21619 const zcu = pt.zcu; 21620 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21621 const src = block.nodeOffset(inst_data.src_node); 21622 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21623 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21624 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt"); 21625 const operand = try sema.resolveInst(extra.rhs); 21626 const operand_ty = sema.typeOf(operand); 21627 21628 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 21629 21630 const dest_scalar_ty = dest_ty.scalarType(zcu); 21631 const operand_scalar_ty = operand_ty.scalarType(zcu); 21632 21633 try sema.checkFloatType(block, src, dest_scalar_ty); 21634 _ = try sema.checkIntType(block, operand_src, operand_scalar_ty); 21635 21636 if (try sema.resolveValue(operand)) |operand_val| { 21637 const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, pt, .sema); 21638 return Air.internedToRef(result_val.toIntern()); 21639 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) { 21640 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_float }); 21641 } 21642 21643 try sema.requireRuntimeBlock(block, src, operand_src); 21644 return block.addTyOp(.float_from_int, dest_ty, operand); 21645 } 21646 21647 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21648 const pt = sema.pt; 21649 const zcu = pt.zcu; 21650 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21651 const src = block.nodeOffset(inst_data.src_node); 21652 21653 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21654 21655 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21656 const operand_res = try sema.resolveInst(extra.rhs); 21657 21658 const uncoerced_operand_ty = sema.typeOf(operand_res); 21659 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt"); 21660 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src); 21661 21662 const is_vector = dest_ty.zigTypeTag(zcu) == .vector; 21663 const operand_ty: Type = if (is_vector) operand_ty: { 21664 const len = dest_ty.vectorLen(zcu); 21665 break :operand_ty try pt.vectorType(.{ .child = .usize_type, .len = len }); 21666 } else .usize; 21667 21668 const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src); 21669 21670 const ptr_ty = dest_ty.scalarType(zcu); 21671 try sema.checkPtrType(block, src, ptr_ty, true); 21672 21673 const elem_ty = ptr_ty.elemType2(zcu); 21674 const ptr_align = try ptr_ty.ptrAlignmentSema(pt); 21675 21676 if (ptr_ty.isSlice(zcu)) { 21677 const msg = msg: { 21678 const msg = try sema.errMsg(src, "integer cannot be converted to slice type '{f}'", .{ptr_ty.fmt(pt)}); 21679 errdefer msg.destroy(sema.gpa); 21680 try sema.errNote(src, msg, "slice length cannot be inferred from address", .{}); 21681 break :msg msg; 21682 }; 21683 return sema.failWithOwnedErrorMsg(block, msg); 21684 } 21685 21686 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { 21687 if (!is_vector) { 21688 const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align); 21689 return Air.internedToRef(ptr_val.toIntern()); 21690 } 21691 const len = dest_ty.vectorLen(zcu); 21692 const new_elems = try sema.arena.alloc(InternPool.Index, len); 21693 for (new_elems, 0..) |*new_elem, i| { 21694 const elem = try val.elemValue(pt, i); 21695 const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align); 21696 new_elem.* = ptr_val.toIntern(); 21697 } 21698 return Air.internedToRef(try pt.intern(.{ .aggregate = .{ 21699 .ty = dest_ty.toIntern(), 21700 .storage = .{ .elems = new_elems }, 21701 } })); 21702 } 21703 if (try ptr_ty.comptimeOnlySema(pt)) { 21704 return sema.failWithOwnedErrorMsg(block, msg: { 21705 const msg = try sema.errMsg(src, "pointer to comptime-only type '{f}' must be comptime-known, but operand is runtime-known", .{ptr_ty.fmt(pt)}); 21706 errdefer msg.destroy(sema.gpa); 21707 21708 try sema.explainWhyTypeIsComptime(msg, src, ptr_ty); 21709 break :msg msg; 21710 }); 21711 } 21712 try sema.requireRuntimeBlock(block, src, operand_src); 21713 try sema.checkLogicalPtrOperation(block, src, ptr_ty); 21714 if (block.wantSafety() and (try elem_ty.hasRuntimeBitsSema(pt) or elem_ty.zigTypeTag(zcu) == .@"fn")) { 21715 if (!ptr_ty.isAllowzeroPtr(zcu)) { 21716 const is_non_zero = if (is_vector) all_non_zero: { 21717 const zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern()); 21718 const is_non_zero = try block.addCmpVector(operand_coerced, zero_usize, .neq); 21719 break :all_non_zero try block.addReduce(is_non_zero, .And); 21720 } else try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); 21721 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null); 21722 } 21723 if (ptr_align.compare(.gt, .@"1")) { 21724 const align_bytes_minus_1 = ptr_align.toByteUnits().? - 1; 21725 const align_mask = Air.internedToRef((try sema.splat(operand_ty, try pt.intValue( 21726 .usize, 21727 if (elem_ty.fnPtrMaskOrNull(zcu)) |mask| 21728 align_bytes_minus_1 & mask 21729 else 21730 align_bytes_minus_1, 21731 ))).toIntern()); 21732 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_mask); 21733 const is_aligned = if (is_vector) all_aligned: { 21734 const splat_zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern()); 21735 const is_aligned = try block.addCmpVector(remainder, splat_zero_usize, .eq); 21736 break :all_aligned try block.addReduce(is_aligned, .And); 21737 } else try block.addBinOp(.cmp_eq, remainder, .zero_usize); 21738 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment); 21739 } 21740 } 21741 return block.addBitCast(dest_ty, operand_coerced); 21742 } 21743 21744 fn ptrFromIntVal( 21745 sema: *Sema, 21746 block: *Block, 21747 operand_src: LazySrcLoc, 21748 operand_val: Value, 21749 ptr_ty: Type, 21750 ptr_align: Alignment, 21751 ) !Value { 21752 const pt = sema.pt; 21753 const zcu = pt.zcu; 21754 if (operand_val.isUndef(zcu)) { 21755 if (ptr_ty.isAllowzeroPtr(zcu) and ptr_align == .@"1") { 21756 return pt.undefValue(ptr_ty); 21757 } 21758 return sema.failWithUseOfUndef(block, operand_src); 21759 } 21760 const addr = try operand_val.toUnsignedIntSema(pt); 21761 if (!ptr_ty.isAllowzeroPtr(zcu) and addr == 0) 21762 return sema.fail(block, operand_src, "pointer type '{f}' does not allow address zero", .{ptr_ty.fmt(pt)}); 21763 if (addr != 0 and ptr_align != .none) { 21764 const masked_addr = if (ptr_ty.childType(zcu).fnPtrMaskOrNull(zcu)) |mask| 21765 addr & mask 21766 else 21767 addr; 21768 21769 if (!ptr_align.check(masked_addr)) { 21770 return sema.fail(block, operand_src, "pointer type '{f}' requires aligned address", .{ptr_ty.fmt(pt)}); 21771 } 21772 } 21773 21774 return switch (ptr_ty.zigTypeTag(zcu)) { 21775 .optional => Value.fromInterned(try pt.intern(.{ .opt = .{ 21776 .ty = ptr_ty.toIntern(), 21777 .val = if (addr == 0) .none else (try pt.ptrIntValue(ptr_ty.childType(zcu), addr)).toIntern(), 21778 } })), 21779 .pointer => try pt.ptrIntValue(ptr_ty, addr), 21780 else => unreachable, 21781 }; 21782 } 21783 21784 fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21785 const pt = sema.pt; 21786 const zcu = pt.zcu; 21787 const ip = &zcu.intern_pool; 21788 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 21789 const src = block.nodeOffset(extra.node); 21790 const operand_src = block.builtinCallArgSrc(extra.node, 0); 21791 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); 21792 const operand = try sema.resolveInst(extra.rhs); 21793 const operand_ty = sema.typeOf(operand); 21794 21795 const dest_tag = dest_ty.zigTypeTag(zcu); 21796 const operand_tag = operand_ty.zigTypeTag(zcu); 21797 21798 if (dest_tag != .error_set and dest_tag != .error_union) { 21799 return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)}); 21800 } 21801 if (operand_tag != .error_set and operand_tag != .error_union) { 21802 return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(operand_tag)}); 21803 } 21804 if (dest_tag == .error_set and operand_tag == .error_union) { 21805 return sema.fail(block, src, "cannot cast an error union type to error set", .{}); 21806 } 21807 if (dest_tag == .error_union and operand_tag == .error_union and 21808 dest_ty.errorUnionPayload(zcu).toIntern() != operand_ty.errorUnionPayload(zcu).toIntern()) 21809 { 21810 return sema.failWithOwnedErrorMsg(block, msg: { 21811 const msg = try sema.errMsg(src, "payload types of error unions must match", .{}); 21812 errdefer msg.destroy(sema.gpa); 21813 const dest_payload_ty = dest_ty.errorUnionPayload(zcu); 21814 const operand_payload_ty = operand_ty.errorUnionPayload(zcu); 21815 try sema.errNote(src, msg, "destination payload is '{f}'", .{dest_payload_ty.fmt(pt)}); 21816 try sema.errNote(src, msg, "operand payload is '{f}'", .{operand_payload_ty.fmt(pt)}); 21817 try addDeclaredHereNote(sema, msg, dest_ty); 21818 try addDeclaredHereNote(sema, msg, operand_ty); 21819 break :msg msg; 21820 }); 21821 } 21822 const dest_err_ty = switch (dest_tag) { 21823 .error_union => dest_ty.errorUnionSet(zcu), 21824 .error_set => dest_ty, 21825 else => unreachable, 21826 }; 21827 const operand_err_ty = switch (operand_tag) { 21828 .error_union => operand_ty.errorUnionSet(zcu), 21829 .error_set => operand_ty, 21830 else => unreachable, 21831 }; 21832 21833 const disjoint = disjoint: { 21834 // Try avoiding resolving inferred error sets if we can 21835 if (!dest_err_ty.isAnyError(zcu) and dest_err_ty.errorSetIsEmpty(zcu)) break :disjoint true; 21836 if (!operand_err_ty.isAnyError(zcu) and operand_err_ty.errorSetIsEmpty(zcu)) break :disjoint true; 21837 if (dest_err_ty.isAnyError(zcu)) break :disjoint false; 21838 if (operand_err_ty.isAnyError(zcu)) break :disjoint false; 21839 const dest_err_names = dest_err_ty.errorSetNames(zcu); 21840 for (0..dest_err_names.len) |dest_err_index| { 21841 if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) 21842 break :disjoint false; 21843 } 21844 21845 if (!ip.isInferredErrorSetType(dest_err_ty.toIntern()) and 21846 !ip.isInferredErrorSetType(operand_err_ty.toIntern())) 21847 { 21848 break :disjoint true; 21849 } 21850 21851 _ = try sema.resolveInferredErrorSetTy(block, src, dest_err_ty.toIntern()); 21852 _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_err_ty.toIntern()); 21853 for (0..dest_err_names.len) |dest_err_index| { 21854 if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) 21855 break :disjoint false; 21856 } 21857 21858 break :disjoint true; 21859 }; 21860 if (disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) { 21861 return sema.fail(block, src, "error sets '{f}' and '{f}' have no common errors", .{ 21862 operand_err_ty.fmt(pt), dest_err_ty.fmt(pt), 21863 }); 21864 } 21865 21866 // operand must be defined since it can be an invalid error value 21867 if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| { 21868 const err_name: InternPool.NullTerminatedString = switch (operand_tag) { 21869 .error_set => ip.indexToKey(operand_val.toIntern()).err.name, 21870 .error_union => switch (ip.indexToKey(operand_val.toIntern()).error_union.val) { 21871 .err_name => |name| name, 21872 .payload => |payload_val| { 21873 assert(dest_tag == .error_union); // should be guaranteed from the type checks above 21874 return sema.coerce(block, dest_ty, Air.internedToRef(payload_val), operand_src); 21875 }, 21876 }, 21877 else => unreachable, 21878 }; 21879 21880 if (!dest_err_ty.isAnyError(zcu) and !Type.errorSetHasFieldIp(ip, dest_err_ty.toIntern(), err_name)) { 21881 return sema.fail(block, src, "'error.{f}' not a member of error set '{f}'", .{ 21882 err_name.fmt(ip), dest_err_ty.fmt(pt), 21883 }); 21884 } 21885 21886 return Air.internedToRef(try pt.intern(switch (dest_tag) { 21887 .error_set => .{ .err = .{ 21888 .ty = dest_ty.toIntern(), 21889 .name = err_name, 21890 } }, 21891 .error_union => .{ .error_union = .{ 21892 .ty = dest_ty.toIntern(), 21893 .val = .{ .err_name = err_name }, 21894 } }, 21895 else => unreachable, 21896 })); 21897 } 21898 21899 const err_int_ty = try pt.errorIntType(); 21900 if (block.wantSafety() and !dest_err_ty.isAnyError(zcu) and 21901 dest_err_ty.toIntern() != .adhoc_inferred_error_set_type and 21902 zcu.backendSupportsFeature(.error_set_has_value)) 21903 { 21904 const err_code_inst = switch (operand_tag) { 21905 .error_set => operand, 21906 .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand), 21907 else => unreachable, 21908 }; 21909 const err_int_inst = try block.addBitCast(err_int_ty, err_code_inst); 21910 21911 if (dest_tag == .error_union) { 21912 const zero_err = try pt.intRef(err_int_ty, 0); 21913 const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err); 21914 if (disjoint) { 21915 // Error must be zero. 21916 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code); 21917 } else { 21918 // Error must be in destination set or zero. 21919 const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst); 21920 const ok = try block.addBinOp(.bool_or, has_value, is_zero); 21921 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 21922 } 21923 } else { 21924 const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst); 21925 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 21926 } 21927 } 21928 21929 if (operand_tag == .error_set and dest_tag == .error_union) { 21930 const err_val = try block.addBitCast(dest_err_ty, operand); 21931 return block.addTyOp(.wrap_errunion_err, dest_ty, err_val); 21932 } else { 21933 return block.addBitCast(dest_ty, operand); 21934 } 21935 } 21936 21937 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21938 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 21939 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); 21940 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 21941 const src = block.nodeOffset(extra.node); 21942 const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node }); 21943 const operand = try sema.resolveInst(extra.rhs); 21944 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName()); 21945 return sema.ptrCastFull( 21946 block, 21947 flags, 21948 src, 21949 operand, 21950 operand_src, 21951 dest_ty, 21952 flags.needResultTypeBuiltinName(), 21953 ); 21954 } 21955 21956 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21957 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21958 const src = block.nodeOffset(inst_data.src_node); 21959 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 21960 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21961 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast"); 21962 const operand = try sema.resolveInst(extra.rhs); 21963 21964 return sema.ptrCastFull( 21965 block, 21966 .{ .ptr_cast = true }, 21967 src, 21968 operand, 21969 operand_src, 21970 dest_ty, 21971 "@ptrCast", 21972 ); 21973 } 21974 21975 fn ptrCastFull( 21976 sema: *Sema, 21977 block: *Block, 21978 flags: Zir.Inst.FullPtrCastFlags, 21979 src: LazySrcLoc, 21980 operand: Air.Inst.Ref, 21981 operand_src: LazySrcLoc, 21982 dest_ty: Type, 21983 operation: []const u8, 21984 ) CompileError!Air.Inst.Ref { 21985 const pt = sema.pt; 21986 const zcu = pt.zcu; 21987 const operand_ty = sema.typeOf(operand); 21988 21989 try sema.checkPtrType(block, src, dest_ty, true); 21990 try sema.checkPtrOperand(block, operand_src, operand_ty); 21991 21992 const src_info = operand_ty.ptrInfo(zcu); 21993 const dest_info = dest_ty.ptrInfo(zcu); 21994 21995 try Type.fromInterned(src_info.child).resolveLayout(pt); 21996 try Type.fromInterned(dest_info.child).resolveLayout(pt); 21997 21998 const DestSliceLen = union(enum) { 21999 undef, 22000 constant: u64, 22001 equal_runtime_src_slice, 22002 change_runtime_src_slice: struct { 22003 bytes_per_src: u64, 22004 bytes_per_dest: u64, 22005 }, 22006 }; 22007 // Populated iff the destination type is a slice. 22008 const dest_slice_len: ?DestSliceLen = len: { 22009 switch (dest_info.flags.size) { 22010 .slice => {}, 22011 .many, .c, .one => break :len null, 22012 } 22013 // A `null` length means the operand is a runtime-known slice (so the length is runtime-known). 22014 // `src_elem_type` is different from `src_info.child` if the latter is an array, to ensure we ignore sentinels. 22015 const src_elem_ty: Type, const opt_src_len: ?u64 = switch (src_info.flags.size) { 22016 .one => src: { 22017 const true_child: Type = .fromInterned(src_info.child); 22018 break :src switch (true_child.zigTypeTag(zcu)) { 22019 .array => .{ true_child.childType(zcu), true_child.arrayLen(zcu) }, 22020 else => .{ true_child, 1 }, 22021 }; 22022 }, 22023 .slice => src: { 22024 const operand_val = try sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null }; 22025 if (operand_val.isUndef(zcu)) break :len .undef; 22026 const slice_val = switch (operand_ty.zigTypeTag(zcu)) { 22027 .optional => operand_val.optionalValue(zcu) orelse break :len .undef, 22028 .pointer => operand_val, 22029 else => unreachable, 22030 }; 22031 const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern()))); 22032 if (slice_len_resolved.isUndef(zcu)) break :len .undef; 22033 break :src .{ .fromInterned(src_info.child), slice_len_resolved.toUnsignedInt(zcu) }; 22034 }, 22035 .many, .c => { 22036 return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)}); 22037 }, 22038 }; 22039 const dest_elem_ty: Type = .fromInterned(dest_info.child); 22040 if (dest_elem_ty.toIntern() == src_elem_ty.toIntern()) { 22041 break :len if (opt_src_len) |l| .{ .constant = l } else .equal_runtime_src_slice; 22042 } 22043 if (!src_elem_ty.comptimeOnly(zcu) and !dest_elem_ty.comptimeOnly(zcu)) { 22044 const src_elem_size = src_elem_ty.abiSize(zcu); 22045 const dest_elem_size = dest_elem_ty.abiSize(zcu); 22046 if (dest_elem_size == 0) { 22047 return sema.fail(block, src, "cannot infer length of slice of zero-bit '{f}' from '{f}'", .{ 22048 dest_elem_ty.fmt(pt), operand_ty.fmt(pt), 22049 }); 22050 } 22051 if (opt_src_len) |src_len| { 22052 const bytes = src_len * src_elem_size; 22053 const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch switch (src_info.flags.size) { 22054 .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), 22055 .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{Type.fromInterned(src_info.child).fmt(pt)}), 22056 else => unreachable, 22057 }; 22058 break :len .{ .constant = dest_len }; 22059 } 22060 assert(src_info.flags.size == .slice); 22061 break :len .{ .change_runtime_src_slice = .{ 22062 .bytes_per_src = src_elem_size, 22063 .bytes_per_dest = dest_elem_size, 22064 } }; 22065 } 22066 // We apply rules for comptime memory consistent with comptime loads/stores, where arrays of 22067 // comptime-only types can be "restructured". 22068 const dest_base_ty: Type, const dest_base_per_elem: u64 = dest_elem_ty.arrayBase(zcu); 22069 const src_base_ty: Type, const src_base_per_elem: u64 = src_elem_ty.arrayBase(zcu); 22070 // The source value has `src_len * src_base_per_elem` values of type `src_base_ty`. 22071 // The result value will have `dest_len * dest_base_per_elem` values of type `dest_base_ty`. 22072 if (dest_base_ty.toIntern() != src_base_ty.toIntern()) { 22073 return sema.fail(block, src, "cannot infer length of comptime-only '{f}' from incompatible '{f}'", .{ 22074 dest_ty.fmt(pt), operand_ty.fmt(pt), 22075 }); 22076 } 22077 // `src_base_ty` is comptime-only, so `src_elem_ty` is comptime-only, so `operand_ty` is 22078 // comptime-only, so `operand` is comptime-known, so `opt_src_len` is non-`null`. 22079 const src_len = opt_src_len.?; 22080 const base_len = src_len * src_base_per_elem; 22081 const dest_len = std.math.divExact(u64, base_len, dest_base_per_elem) catch switch (src_info.flags.size) { 22082 .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), 22083 .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}), 22084 else => unreachable, 22085 }; 22086 break :len .{ .constant = dest_len }; 22087 }; 22088 22089 // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs 22090 22091 if (!flags.ptr_cast) { 22092 const is_array_ptr_to_slice = b: { 22093 if (dest_info.flags.size != .slice) break :b false; 22094 if (src_info.flags.size != .one) break :b false; 22095 const src_pointer_child: Type = .fromInterned(src_info.child); 22096 if (src_pointer_child.zigTypeTag(zcu) != .array) break :b false; 22097 const src_elem = src_pointer_child.childType(zcu); 22098 break :b src_elem.toIntern() == dest_info.child; 22099 }; 22100 22101 check_size: { 22102 if (src_info.flags.size == dest_info.flags.size) break :check_size; 22103 if (is_array_ptr_to_slice) break :check_size; 22104 if (src_info.flags.size == .c) break :check_size; 22105 if (dest_info.flags.size == .c) break :check_size; 22106 return sema.failWithOwnedErrorMsg(block, msg: { 22107 const msg = try sema.errMsg(src, "cannot implicitly convert {s} to {s}", .{ 22108 pointerSizeString(src_info.flags.size), 22109 pointerSizeString(dest_info.flags.size), 22110 }); 22111 errdefer msg.destroy(sema.gpa); 22112 if (dest_info.flags.size == .many and 22113 (src_info.flags.size == .slice or 22114 (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array))) 22115 { 22116 try sema.errNote(src, msg, "use 'ptr' field to convert slice to many pointer", .{}); 22117 } else { 22118 try sema.errNote(src, msg, "use @ptrCast to change pointer size", .{}); 22119 } 22120 break :msg msg; 22121 }); 22122 } 22123 22124 check_child: { 22125 const src_child: Type = if (dest_info.flags.size == .slice and src_info.flags.size == .one) blk: { 22126 // *[n]T -> []T 22127 break :blk Type.fromInterned(src_info.child).childType(zcu); 22128 } else .fromInterned(src_info.child); 22129 22130 const dest_child: Type = .fromInterned(dest_info.child); 22131 22132 const imc_res = try sema.coerceInMemoryAllowed( 22133 block, 22134 dest_child, 22135 src_child, 22136 !dest_info.flags.is_const, 22137 zcu.getTarget(), 22138 src, 22139 operand_src, 22140 null, 22141 ); 22142 if (imc_res == .ok) break :check_child; 22143 return sema.failWithOwnedErrorMsg(block, msg: { 22144 const msg = try sema.errMsg(src, "pointer element type '{f}' cannot coerce into element type '{f}'", .{ 22145 src_child.fmt(pt), dest_child.fmt(pt), 22146 }); 22147 errdefer msg.destroy(sema.gpa); 22148 try imc_res.report(sema, src, msg); 22149 try sema.errNote(src, msg, "use @ptrCast to cast pointer element type", .{}); 22150 break :msg msg; 22151 }); 22152 } 22153 22154 check_sent: { 22155 if (dest_info.sentinel == .none) break :check_sent; 22156 if (src_info.flags.size == .c) break :check_sent; 22157 if (src_info.sentinel != .none) { 22158 const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_info.sentinel, dest_info.child); 22159 if (dest_info.sentinel == coerced_sent) break :check_sent; 22160 } 22161 if (is_array_ptr_to_slice) { 22162 // [*]nT -> []T 22163 const arr_ty: Type = .fromInterned(src_info.child); 22164 if (arr_ty.sentinel(zcu)) |src_sentinel| { 22165 const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_sentinel.toIntern(), dest_info.child); 22166 if (dest_info.sentinel == coerced_sent) break :check_sent; 22167 } 22168 } 22169 return sema.failWithOwnedErrorMsg(block, msg: { 22170 const msg = if (src_info.sentinel == .none) blk: { 22171 break :blk try sema.errMsg(src, "destination pointer requires '{f}' sentinel", .{ 22172 Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema), 22173 }); 22174 } else blk: { 22175 break :blk try sema.errMsg(src, "pointer sentinel '{f}' cannot coerce into pointer sentinel '{f}'", .{ 22176 Value.fromInterned(src_info.sentinel).fmtValueSema(pt, sema), 22177 Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema), 22178 }); 22179 }; 22180 errdefer msg.destroy(sema.gpa); 22181 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{}); 22182 break :msg msg; 22183 }); 22184 } 22185 22186 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) { 22187 return sema.failWithOwnedErrorMsg(block, msg: { 22188 const msg = try sema.errMsg(src, "pointer host size '{d}' cannot coerce into pointer host size '{d}'", .{ 22189 src_info.packed_offset.host_size, 22190 dest_info.packed_offset.host_size, 22191 }); 22192 errdefer msg.destroy(sema.gpa); 22193 try sema.errNote(src, msg, "use @ptrCast to cast pointer host size", .{}); 22194 break :msg msg; 22195 }); 22196 } 22197 22198 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) { 22199 return sema.failWithOwnedErrorMsg(block, msg: { 22200 const msg = try sema.errMsg(src, "pointer bit offset '{d}' cannot coerce into pointer bit offset '{d}'", .{ 22201 src_info.packed_offset.bit_offset, 22202 dest_info.packed_offset.bit_offset, 22203 }); 22204 errdefer msg.destroy(sema.gpa); 22205 try sema.errNote(src, msg, "use @ptrCast to cast pointer bit offset", .{}); 22206 break :msg msg; 22207 }); 22208 } 22209 22210 check_allowzero: { 22211 const src_allows_zero = operand_ty.ptrAllowsZero(zcu); 22212 const dest_allows_zero = dest_ty.ptrAllowsZero(zcu); 22213 if (!src_allows_zero) break :check_allowzero; 22214 if (dest_allows_zero) break :check_allowzero; 22215 22216 return sema.failWithOwnedErrorMsg(block, msg: { 22217 const msg = try sema.errMsg(src, "'{f}' could have null values which are illegal in type '{f}'", .{ 22218 operand_ty.fmt(pt), 22219 dest_ty.fmt(pt), 22220 }); 22221 errdefer msg.destroy(sema.gpa); 22222 try sema.errNote(src, msg, "use @ptrCast to assert the pointer is not null", .{}); 22223 break :msg msg; 22224 }); 22225 } 22226 22227 // TODO: vector index? 22228 } 22229 22230 const src_align = if (src_info.flags.alignment != .none) 22231 src_info.flags.alignment 22232 else 22233 Type.fromInterned(src_info.child).abiAlignment(zcu); 22234 22235 const dest_align = if (dest_info.flags.alignment != .none) 22236 dest_info.flags.alignment 22237 else 22238 Type.fromInterned(dest_info.child).abiAlignment(zcu); 22239 22240 if (!flags.align_cast) { 22241 if (dest_align.compare(.gt, src_align)) { 22242 return sema.failWithOwnedErrorMsg(block, msg: { 22243 const msg = try sema.errMsg(src, "{s} increases pointer alignment", .{operation}); 22244 errdefer msg.destroy(sema.gpa); 22245 try sema.errNote(operand_src, msg, "'{f}' has alignment '{d}'", .{ 22246 operand_ty.fmt(pt), src_align.toByteUnits() orelse 0, 22247 }); 22248 try sema.errNote(src, msg, "'{f}' has alignment '{d}'", .{ 22249 dest_ty.fmt(pt), dest_align.toByteUnits() orelse 0, 22250 }); 22251 try sema.errNote(src, msg, "use @alignCast to assert pointer alignment", .{}); 22252 break :msg msg; 22253 }); 22254 } 22255 } 22256 22257 if (!flags.addrspace_cast) { 22258 if (src_info.flags.address_space != dest_info.flags.address_space) { 22259 return sema.failWithOwnedErrorMsg(block, msg: { 22260 const msg = try sema.errMsg(src, "{s} changes pointer address space", .{operation}); 22261 errdefer msg.destroy(sema.gpa); 22262 try sema.errNote(operand_src, msg, "'{f}' has address space '{s}'", .{ 22263 operand_ty.fmt(pt), @tagName(src_info.flags.address_space), 22264 }); 22265 try sema.errNote(src, msg, "'{f}' has address space '{s}'", .{ 22266 dest_ty.fmt(pt), @tagName(dest_info.flags.address_space), 22267 }); 22268 try sema.errNote(src, msg, "use @addrSpaceCast to cast pointer address space", .{}); 22269 break :msg msg; 22270 }); 22271 } 22272 } else { 22273 // Some address space casts are always disallowed 22274 if (!target_util.addrSpaceCastIsValid(zcu.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) { 22275 return sema.failWithOwnedErrorMsg(block, msg: { 22276 const msg = try sema.errMsg(src, "invalid address space cast", .{}); 22277 errdefer msg.destroy(sema.gpa); 22278 try sema.errNote(operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{ 22279 @tagName(src_info.flags.address_space), 22280 @tagName(dest_info.flags.address_space), 22281 }); 22282 break :msg msg; 22283 }); 22284 } 22285 } 22286 22287 if (!flags.const_cast) { 22288 if (src_info.flags.is_const and !dest_info.flags.is_const) { 22289 return sema.failWithOwnedErrorMsg(block, msg: { 22290 const msg = try sema.errMsg(src, "{s} discards const qualifier", .{operation}); 22291 errdefer msg.destroy(sema.gpa); 22292 try sema.errNote(src, msg, "use @constCast to discard const qualifier", .{}); 22293 break :msg msg; 22294 }); 22295 } 22296 } 22297 22298 if (!flags.volatile_cast) { 22299 if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) { 22300 return sema.failWithOwnedErrorMsg(block, msg: { 22301 const msg = try sema.errMsg(src, "{s} discards volatile qualifier", .{operation}); 22302 errdefer msg.destroy(sema.gpa); 22303 try sema.errNote(src, msg, "use @volatileCast to discard volatile qualifier", .{}); 22304 break :msg msg; 22305 }); 22306 } 22307 } 22308 22309 // Type validation done -- this cast is okay. Let's do it! 22310 // 22311 // `operand` is a maybe-optional pointer or slice. 22312 // `dest_ty` is a maybe-optional pointer or slice. 22313 // 22314 // We have a few safety checks: 22315 // * if the destination does not allow zero, check the operand is not null / 0 22316 // * if the destination is more aligned than the operand, check the pointer alignment 22317 // * if `slice_needs_len_change`, check the element count divides neatly 22318 22319 ct: { 22320 if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime 22321 const operand_val = try sema.resolveValue(operand) orelse break :ct; 22322 22323 if (operand_val.isUndef(zcu)) { 22324 if (!dest_ty.ptrAllowsZero(zcu)) { 22325 return sema.failWithUseOfUndef(block, operand_src); 22326 } 22327 return pt.undefRef(dest_ty); 22328 } 22329 22330 if (operand_val.isNull(zcu)) { 22331 if (!dest_ty.ptrAllowsZero(zcu)) { 22332 return sema.fail(block, operand_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)}); 22333 } 22334 if (dest_ty.zigTypeTag(zcu) == .optional) { 22335 return Air.internedToRef((try pt.nullValue(dest_ty)).toIntern()); 22336 } else { 22337 return Air.internedToRef((try pt.ptrIntValue(dest_ty, 0)).toIntern()); 22338 } 22339 } 22340 22341 const ptr_val: Value = switch (src_info.flags.size) { 22342 .slice => .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.ptr), 22343 .one, .many, .c => operand_val, 22344 }; 22345 22346 if (dest_align.compare(.gt, src_align)) { 22347 if (try ptr_val.getUnsignedIntSema(pt)) |addr| { 22348 const masked_addr = if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask| 22349 addr & mask 22350 else 22351 addr; 22352 22353 if (!dest_align.check(masked_addr)) { 22354 return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ 22355 addr, 22356 dest_align.toByteUnits().?, 22357 }); 22358 } 22359 } 22360 } 22361 22362 if (dest_info.flags.size == .slice) { 22363 // Because the operand is comptime-known and not `null`, the slice length has already been computed: 22364 const len: Value = switch (dest_slice_len.?) { 22365 .undef => .undef_usize, 22366 .constant => |n| try pt.intValue(.usize, n), 22367 .equal_runtime_src_slice => unreachable, 22368 .change_runtime_src_slice => unreachable, 22369 }; 22370 return Air.internedToRef(try pt.intern(.{ .slice = .{ 22371 .ty = dest_ty.toIntern(), 22372 .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), 22373 .len = len.toIntern(), 22374 } })); 22375 } else { 22376 // Any to non-slice 22377 const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty); 22378 return Air.internedToRef(new_ptr_val.toIntern()); 22379 } 22380 } 22381 22382 try sema.validateRuntimeValue(block, operand_src, operand); 22383 22384 const can_cast_to_int = !target_util.arePointersLogical(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu)); 22385 const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); 22386 const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align); 22387 22388 const slice_needs_len_change = if (dest_slice_len) |l| switch (l) { 22389 .undef, .equal_runtime_src_slice => false, 22390 .constant, .change_runtime_src_slice => true, 22391 } else false; 22392 22393 // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer. 22394 const need_operand_ptr = src_info.flags.size != .slice or // we already have it 22395 dest_info.flags.size != .slice or // the result is a raw pointer 22396 need_null_check or // safety check happens on pointer 22397 need_align_check or // safety check happens on pointer 22398 flags.addrspace_cast or // AIR addrspace_cast acts on a pointer 22399 slice_needs_len_change; // to change the length, we reconstruct the slice 22400 22401 // This is not quite just the pointer part of `operand` -- it's also had the address space cast done already. 22402 const operand_ptr: Air.Inst.Ref = ptr: { 22403 if (!need_operand_ptr) break :ptr .none; 22404 // First, just get the pointer. 22405 const pre_addrspace_cast = inner: { 22406 if (src_info.flags.size != .slice) break :inner operand; 22407 if (operand_ty.zigTypeTag(zcu) == .optional) { 22408 break :inner try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty); 22409 } else { 22410 break :inner try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); 22411 } 22412 }; 22413 // Now, do an addrspace cast if necessary! 22414 if (!flags.addrspace_cast) break :ptr pre_addrspace_cast; 22415 22416 const intermediate_ptr_ty = try pt.ptrTypeSema(info: { 22417 var info = src_info; 22418 info.flags.address_space = dest_info.flags.address_space; 22419 break :info info; 22420 }); 22421 const intermediate_ty = if (operand_ty.zigTypeTag(zcu) == .optional) blk: { 22422 break :blk try pt.optionalType(intermediate_ptr_ty.toIntern()); 22423 } else intermediate_ptr_ty; 22424 break :ptr try block.addInst(.{ 22425 .tag = .addrspace_cast, 22426 .data = .{ .ty_op = .{ 22427 .ty = Air.internedToRef(intermediate_ty.toIntern()), 22428 .operand = pre_addrspace_cast, 22429 } }, 22430 }); 22431 }; 22432 22433 // Whether we need to know if the (slice) operand has `len == 0`. 22434 const need_operand_len_is_zero = src_info.flags.size == .slice and 22435 dest_info.flags.size == .slice and 22436 (need_null_check or need_align_check); 22437 // Whether we need to get the (slice) operand's `len`. 22438 const need_operand_len = need_len: { 22439 if (src_info.flags.size != .slice) break :need_len false; 22440 if (dest_info.flags.size != .slice) break :need_len false; 22441 if (need_operand_len_is_zero) break :need_len true; 22442 if (flags.addrspace_cast or slice_needs_len_change) break :need_len true; 22443 break :need_len false; 22444 }; 22445 // `.none` if `!need_operand_len`. 22446 const operand_len: Air.Inst.Ref = len: { 22447 if (!need_operand_len) break :len .none; 22448 break :len try block.addTyOp(.slice_len, .usize, operand); 22449 }; 22450 // `.none` if `!need_operand_len_is_zero`. 22451 const operand_len_is_zero: Air.Inst.Ref = zero: { 22452 if (!need_operand_len_is_zero) break :zero .none; 22453 assert(need_operand_len); 22454 break :zero try block.addBinOp(.cmp_eq, operand_len, .zero_usize); 22455 }; 22456 22457 // `operand_ptr` converted to an integer, for safety checks. 22458 const operand_ptr_int: Air.Inst.Ref = if (need_null_check or need_align_check) i: { 22459 assert(need_operand_ptr); 22460 break :i try block.addBitCast(.usize, operand_ptr); 22461 } else .none; 22462 22463 if (need_null_check) { 22464 assert(operand_ptr_int != .none); 22465 const ptr_is_non_zero = try block.addBinOp(.cmp_neq, operand_ptr_int, .zero_usize); 22466 const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { 22467 break :ok try block.addBinOp(.bool_or, operand_len_is_zero, ptr_is_non_zero); 22468 } else ptr_is_non_zero; 22469 try sema.addSafetyCheck(block, src, ok, .cast_to_null); 22470 } 22471 if (need_align_check) { 22472 assert(operand_ptr_int != .none); 22473 const align_mask = try pt.intRef(.usize, mask: { 22474 const target_ptr_mask = Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu) orelse ~@as(u64, 0); 22475 break :mask (dest_align.toByteUnits().? - 1) & target_ptr_mask; 22476 }); 22477 const ptr_masked = try block.addBinOp(.bit_and, operand_ptr_int, align_mask); 22478 const is_aligned = try block.addBinOp(.cmp_eq, ptr_masked, .zero_usize); 22479 const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { 22480 break :ok try block.addBinOp(.bool_or, operand_len_is_zero, is_aligned); 22481 } else is_aligned; 22482 try sema.addSafetyCheck(block, src, ok, .incorrect_alignment); 22483 } 22484 22485 if (dest_info.flags.size == .slice) { 22486 if (src_info.flags.size == .slice and !flags.addrspace_cast and !slice_needs_len_change) { 22487 // Fast path: just bitcast! 22488 return block.addBitCast(dest_ty, operand); 22489 } 22490 22491 // We need to deconstruct the slice (if applicable) and reconstruct it. 22492 assert(need_operand_ptr); 22493 22494 const result_len: Air.Inst.Ref = switch (dest_slice_len.?) { 22495 .undef => .undef_usize, 22496 .constant => |n| try pt.intRef(.usize, n), 22497 .equal_runtime_src_slice => len: { 22498 assert(need_operand_len); 22499 break :len operand_len; 22500 }, 22501 .change_runtime_src_slice => |change| len: { 22502 assert(need_operand_len); 22503 // If `mul / div` is a whole number, then just multiply the length by it. 22504 if (std.math.divExact(u64, change.bytes_per_src, change.bytes_per_dest)) |dest_per_src| { 22505 const multiplier = try pt.intRef(.usize, dest_per_src); 22506 break :len try block.addBinOp(.mul, operand_len, multiplier); 22507 } else |err| switch (err) { 22508 error.DivisionByZero => unreachable, 22509 error.UnexpectedRemainder => {}, // fall through to code below 22510 } 22511 // If `div / mul` is a whole number, then just divide the length by it. 22512 // This incurs a safety check. 22513 if (std.math.divExact(u64, change.bytes_per_dest, change.bytes_per_src)) |src_per_dest| { 22514 const divisor = try pt.intRef(.usize, src_per_dest); 22515 if (block.wantSafety()) { 22516 // Check that the element count divides neatly. 22517 const remainder = try block.addBinOp(.rem, operand_len, divisor); 22518 const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 22519 try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); 22520 } 22521 break :len try block.addBinOp(.div_exact, operand_len, divisor); 22522 } else |err| switch (err) { 22523 error.DivisionByZero => unreachable, 22524 error.UnexpectedRemainder => {}, // fall through to code below 22525 } 22526 // Fallback: the elements don't divide easily. We'll multiply *and* divide. This incurs a safety check. 22527 const total_bytes_ref = try block.addBinOp(.mul, operand_len, try pt.intRef(.usize, change.bytes_per_src)); 22528 const bytes_per_dest_ref = try pt.intRef(.usize, change.bytes_per_dest); 22529 if (block.wantSafety()) { 22530 // Check that `total_bytes_ref` divides neatly into `bytes_per_dest_ref`. 22531 const remainder = try block.addBinOp(.rem, total_bytes_ref, bytes_per_dest_ref); 22532 const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 22533 try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); 22534 } 22535 break :len try block.addBinOp(.div_exact, total_bytes_ref, bytes_per_dest_ref); 22536 }, 22537 }; 22538 22539 const operand_ptr_ty = sema.typeOf(operand_ptr); 22540 const want_ptr_ty = switch (dest_ty.zigTypeTag(zcu)) { 22541 .optional => try pt.optionalType(dest_ty.childType(zcu).slicePtrFieldType(zcu).toIntern()), 22542 .pointer => dest_ty.slicePtrFieldType(zcu), 22543 else => unreachable, 22544 }; 22545 const coerced_ptr = if (operand_ptr_ty.toIntern() != want_ptr_ty.toIntern()) ptr: { 22546 break :ptr try block.addBitCast(want_ptr_ty, operand_ptr); 22547 } else operand_ptr; 22548 22549 return block.addInst(.{ 22550 .tag = .slice, 22551 .data = .{ .ty_pl = .{ 22552 .ty = Air.internedToRef(dest_ty.toIntern()), 22553 .payload = try sema.addExtra(Air.Bin{ 22554 .lhs = coerced_ptr, 22555 .rhs = result_len, 22556 }), 22557 } }, 22558 }); 22559 } else { 22560 assert(need_operand_ptr); 22561 // We just need to bitcast the pointer, if necessary. 22562 // It might not be necessary, since we might have just needed the `addrspace_cast`. 22563 const result = if (sema.typeOf(operand_ptr).toIntern() == dest_ty.toIntern()) 22564 operand_ptr 22565 else 22566 try block.addBitCast(dest_ty, operand_ptr); 22567 22568 try sema.checkKnownAllocPtr(block, operand, result); 22569 return result; 22570 } 22571 } 22572 22573 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 22574 const pt = sema.pt; 22575 const zcu = pt.zcu; 22576 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 22577 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); 22578 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 22579 const src = block.nodeOffset(extra.node); 22580 const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node }); 22581 const operand = try sema.resolveInst(extra.operand); 22582 const operand_ty = sema.typeOf(operand); 22583 try sema.checkPtrOperand(block, operand_src, operand_ty); 22584 22585 var ptr_info = operand_ty.ptrInfo(zcu); 22586 if (flags.const_cast) ptr_info.flags.is_const = false; 22587 if (flags.volatile_cast) ptr_info.flags.is_volatile = false; 22588 22589 const dest_ty = blk: { 22590 const dest_ty = try pt.ptrTypeSema(ptr_info); 22591 if (operand_ty.zigTypeTag(zcu) == .optional) { 22592 break :blk try pt.optionalType(dest_ty.toIntern()); 22593 } 22594 break :blk dest_ty; 22595 }; 22596 22597 if (try sema.resolveValue(operand)) |operand_val| { 22598 return Air.internedToRef((try pt.getCoerced(operand_val, dest_ty)).toIntern()); 22599 } 22600 22601 try sema.requireRuntimeBlock(block, src, null); 22602 const new_ptr = try block.addBitCast(dest_ty, operand); 22603 try sema.checkKnownAllocPtr(block, operand, new_ptr); 22604 return new_ptr; 22605 } 22606 22607 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22608 const pt = sema.pt; 22609 const zcu = pt.zcu; 22610 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 22611 const src = block.nodeOffset(inst_data.src_node); 22612 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22613 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22614 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate"); 22615 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src); 22616 const operand = try sema.resolveInst(extra.rhs); 22617 const operand_ty = sema.typeOf(operand); 22618 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 22619 22620 const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector; 22621 const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector; 22622 if (operand_is_vector != dest_is_vector) { 22623 return sema.fail(block, operand_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), operand_ty.fmt(pt) }); 22624 } 22625 22626 if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { 22627 return sema.coerce(block, dest_ty, operand, operand_src); 22628 } 22629 22630 const dest_info = dest_scalar_ty.intInfo(zcu); 22631 22632 if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { 22633 return Air.internedToRef(val.toIntern()); 22634 } 22635 22636 if (operand_scalar_ty.zigTypeTag(zcu) != .comptime_int) { 22637 const operand_info = operand_ty.intInfo(zcu); 22638 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22639 return Air.internedToRef(val.toIntern()); 22640 } 22641 22642 if (operand_info.signedness != dest_info.signedness) { 22643 return sema.fail(block, operand_src, "expected {s} integer type, found '{f}'", .{ 22644 @tagName(dest_info.signedness), operand_ty.fmt(pt), 22645 }); 22646 } 22647 switch (std.math.order(dest_info.bits, operand_info.bits)) { 22648 .gt => { 22649 const msg = msg: { 22650 const msg = try sema.errMsg( 22651 src, 22652 "destination type '{f}' has more bits than source type '{f}'", 22653 .{ dest_ty.fmt(pt), operand_ty.fmt(pt) }, 22654 ); 22655 errdefer msg.destroy(sema.gpa); 22656 try sema.errNote(src, msg, "destination type has {d} bits", .{ 22657 dest_info.bits, 22658 }); 22659 try sema.errNote(operand_src, msg, "operand type has {d} bits", .{ 22660 operand_info.bits, 22661 }); 22662 break :msg msg; 22663 }; 22664 return sema.failWithOwnedErrorMsg(block, msg); 22665 }, 22666 .eq => return operand, 22667 .lt => {}, 22668 } 22669 } 22670 22671 if (try sema.resolveValueResolveLazy(operand)) |val| { 22672 if (val.isUndef(zcu)) return pt.undefRef(dest_ty); 22673 if (!dest_is_vector) { 22674 return Air.internedToRef((try pt.getCoerced( 22675 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, pt), 22676 dest_ty, 22677 )).toIntern()); 22678 } 22679 const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(zcu)); 22680 for (elems, 0..) |*elem, i| { 22681 const elem_val = try val.elemValue(pt, i); 22682 const uncoerced_elem = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, pt); 22683 elem.* = (try pt.getCoerced(uncoerced_elem, dest_scalar_ty)).toIntern(); 22684 } 22685 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 22686 .ty = dest_ty.toIntern(), 22687 .storage = .{ .elems = elems }, 22688 } }))); 22689 } 22690 22691 try sema.requireRuntimeBlock(block, src, operand_src); 22692 return block.addTyOp(.trunc, dest_ty, operand); 22693 } 22694 22695 fn zirBitCount( 22696 sema: *Sema, 22697 block: *Block, 22698 inst: Zir.Inst.Index, 22699 air_tag: Air.Inst.Tag, 22700 comptime comptimeOp: fn (val: Value, ty: Type, zcu: *Zcu) u64, 22701 ) CompileError!Air.Inst.Ref { 22702 const pt = sema.pt; 22703 const zcu = pt.zcu; 22704 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22705 const src = block.nodeOffset(inst_data.src_node); 22706 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22707 const operand = try sema.resolveInst(inst_data.operand); 22708 const operand_ty = sema.typeOf(operand); 22709 _ = try sema.checkIntOrVector(block, operand, operand_src); 22710 const bits = operand_ty.intInfo(zcu).bits; 22711 22712 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22713 return Air.internedToRef(val.toIntern()); 22714 } 22715 22716 const result_scalar_ty = try pt.smallestUnsignedInt(bits); 22717 switch (operand_ty.zigTypeTag(zcu)) { 22718 .vector => { 22719 const vec_len = operand_ty.vectorLen(zcu); 22720 const result_ty = try pt.vectorType(.{ 22721 .len = vec_len, 22722 .child = result_scalar_ty.toIntern(), 22723 }); 22724 if (try sema.resolveValue(operand)) |val| { 22725 if (val.isUndef(zcu)) return pt.undefRef(result_ty); 22726 22727 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 22728 const scalar_ty = operand_ty.scalarType(zcu); 22729 for (elems, 0..) |*elem, i| { 22730 const elem_val = try val.elemValue(pt, i); 22731 const count = comptimeOp(elem_val, scalar_ty, zcu); 22732 elem.* = (try pt.intValue(result_scalar_ty, count)).toIntern(); 22733 } 22734 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 22735 .ty = result_ty.toIntern(), 22736 .storage = .{ .elems = elems }, 22737 } }))); 22738 } else { 22739 try sema.requireRuntimeBlock(block, src, operand_src); 22740 return block.addTyOp(air_tag, result_ty, operand); 22741 } 22742 }, 22743 .int => { 22744 if (try sema.resolveValueResolveLazy(operand)) |val| { 22745 if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty); 22746 return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu)); 22747 } else { 22748 try sema.requireRuntimeBlock(block, src, operand_src); 22749 return block.addTyOp(air_tag, result_scalar_ty, operand); 22750 } 22751 }, 22752 else => unreachable, 22753 } 22754 } 22755 22756 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22757 const pt = sema.pt; 22758 const zcu = pt.zcu; 22759 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22760 const src = block.nodeOffset(inst_data.src_node); 22761 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22762 const operand = try sema.resolveInst(inst_data.operand); 22763 const operand_ty = sema.typeOf(operand); 22764 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 22765 const bits = scalar_ty.intInfo(zcu).bits; 22766 if (bits % 8 != 0) { 22767 return sema.fail( 22768 block, 22769 operand_src, 22770 "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {d} bits", 22771 .{ scalar_ty.fmt(pt), bits }, 22772 ); 22773 } 22774 22775 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22776 return Air.internedToRef(val.toIntern()); 22777 } 22778 22779 switch (operand_ty.zigTypeTag(zcu)) { 22780 .int => { 22781 const runtime_src = if (try sema.resolveValue(operand)) |val| { 22782 if (val.isUndef(zcu)) return pt.undefRef(operand_ty); 22783 const result_val = try val.byteSwap(operand_ty, pt, sema.arena); 22784 return Air.internedToRef(result_val.toIntern()); 22785 } else operand_src; 22786 22787 try sema.requireRuntimeBlock(block, src, runtime_src); 22788 return block.addTyOp(.byte_swap, operand_ty, operand); 22789 }, 22790 .vector => { 22791 const runtime_src = if (try sema.resolveValue(operand)) |val| { 22792 if (val.isUndef(zcu)) 22793 return pt.undefRef(operand_ty); 22794 22795 const vec_len = operand_ty.vectorLen(zcu); 22796 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 22797 for (elems, 0..) |*elem, i| { 22798 const elem_val = try val.elemValue(pt, i); 22799 elem.* = (try elem_val.byteSwap(scalar_ty, pt, sema.arena)).toIntern(); 22800 } 22801 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 22802 .ty = operand_ty.toIntern(), 22803 .storage = .{ .elems = elems }, 22804 } }))); 22805 } else operand_src; 22806 22807 try sema.requireRuntimeBlock(block, src, runtime_src); 22808 return block.addTyOp(.byte_swap, operand_ty, operand); 22809 }, 22810 else => unreachable, 22811 } 22812 } 22813 22814 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22815 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22816 const src = block.nodeOffset(inst_data.src_node); 22817 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22818 const operand = try sema.resolveInst(inst_data.operand); 22819 const operand_ty = sema.typeOf(operand); 22820 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 22821 22822 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22823 return Air.internedToRef(val.toIntern()); 22824 } 22825 22826 const pt = sema.pt; 22827 const zcu = pt.zcu; 22828 switch (operand_ty.zigTypeTag(zcu)) { 22829 .int => { 22830 const runtime_src = if (try sema.resolveValue(operand)) |val| { 22831 if (val.isUndef(zcu)) return pt.undefRef(operand_ty); 22832 const result_val = try val.bitReverse(operand_ty, pt, sema.arena); 22833 return Air.internedToRef(result_val.toIntern()); 22834 } else operand_src; 22835 22836 try sema.requireRuntimeBlock(block, src, runtime_src); 22837 return block.addTyOp(.bit_reverse, operand_ty, operand); 22838 }, 22839 .vector => { 22840 const runtime_src = if (try sema.resolveValue(operand)) |val| { 22841 if (val.isUndef(zcu)) 22842 return pt.undefRef(operand_ty); 22843 22844 const vec_len = operand_ty.vectorLen(zcu); 22845 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 22846 for (elems, 0..) |*elem, i| { 22847 const elem_val = try val.elemValue(pt, i); 22848 elem.* = (try elem_val.bitReverse(scalar_ty, pt, sema.arena)).toIntern(); 22849 } 22850 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 22851 .ty = operand_ty.toIntern(), 22852 .storage = .{ .elems = elems }, 22853 } }))); 22854 } else operand_src; 22855 22856 try sema.requireRuntimeBlock(block, src, runtime_src); 22857 return block.addTyOp(.bit_reverse, operand_ty, operand); 22858 }, 22859 else => unreachable, 22860 } 22861 } 22862 22863 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22864 const offset = try sema.bitOffsetOf(block, inst); 22865 return sema.pt.intRef(.comptime_int, offset); 22866 } 22867 22868 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22869 const offset = try sema.bitOffsetOf(block, inst); 22870 // TODO reminder to make this a compile error for packed structs 22871 return sema.pt.intRef(.comptime_int, offset / 8); 22872 } 22873 22874 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 { 22875 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 22876 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); 22877 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 22878 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); 22879 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22880 22881 const ty = try sema.resolveType(block, ty_src, extra.lhs); 22882 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name }); 22883 22884 const pt = sema.pt; 22885 const zcu = pt.zcu; 22886 const ip = &zcu.intern_pool; 22887 try ty.resolveLayout(pt); 22888 switch (ty.zigTypeTag(zcu)) { 22889 .@"struct" => {}, 22890 else => return sema.fail(block, ty_src, "expected struct type, found '{f}'", .{ty.fmt(pt)}), 22891 } 22892 22893 const field_index = if (ty.isTuple(zcu)) blk: { 22894 if (field_name.eqlSlice("len", ip)) { 22895 return sema.fail(block, src, "no offset available for 'len' field of tuple", .{}); 22896 } 22897 break :blk try sema.tupleFieldIndex(block, ty, field_name, field_name_src); 22898 } else try sema.structFieldIndex(block, ty, field_name, field_name_src); 22899 22900 if (ty.structFieldIsComptime(field_index, zcu)) { 22901 return sema.fail(block, src, "no offset available for comptime field", .{}); 22902 } 22903 22904 switch (ty.containerLayout(zcu)) { 22905 .@"packed" => { 22906 var bit_sum: u64 = 0; 22907 const struct_type = ip.loadStructType(ty.toIntern()); 22908 for (0..struct_type.field_types.len) |i| { 22909 if (i == field_index) { 22910 return bit_sum; 22911 } 22912 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 22913 bit_sum += field_ty.bitSize(zcu); 22914 } else unreachable; 22915 }, 22916 else => return ty.structFieldOffset(field_index, zcu) * 8, 22917 } 22918 } 22919 22920 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 22921 const pt = sema.pt; 22922 const zcu = pt.zcu; 22923 switch (ty.zigTypeTag(zcu)) { 22924 .@"struct", .@"enum", .@"union", .@"opaque" => return, 22925 else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{f}'", .{ty.fmt(pt)}), 22926 } 22927 } 22928 22929 /// Returns `true` if the type was a comptime_int. 22930 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { 22931 const pt = sema.pt; 22932 const zcu = pt.zcu; 22933 switch (ty.zigTypeTag(zcu)) { 22934 .comptime_int => return true, 22935 .int => return false, 22936 else => return sema.fail(block, src, "expected integer type, found '{f}'", .{ty.fmt(pt)}), 22937 } 22938 } 22939 22940 fn checkInvalidPtrIntArithmetic( 22941 sema: *Sema, 22942 block: *Block, 22943 src: LazySrcLoc, 22944 ty: Type, 22945 ) CompileError!void { 22946 const pt = sema.pt; 22947 const zcu = pt.zcu; 22948 switch (ty.zigTypeTag(zcu)) { 22949 .pointer => switch (ty.ptrSize(zcu)) { 22950 .one, .slice => return, 22951 .many, .c => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), 22952 }, 22953 else => return, 22954 } 22955 } 22956 22957 fn checkArithmeticOp( 22958 sema: *Sema, 22959 block: *Block, 22960 src: LazySrcLoc, 22961 scalar_tag: std.builtin.TypeId, 22962 lhs_zig_ty_tag: std.builtin.TypeId, 22963 rhs_zig_ty_tag: std.builtin.TypeId, 22964 zir_tag: Zir.Inst.Tag, 22965 ) CompileError!void { 22966 const is_int = scalar_tag == .int or scalar_tag == .comptime_int; 22967 const is_float = scalar_tag == .float or scalar_tag == .comptime_float; 22968 22969 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { 22970 return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ 22971 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), 22972 }); 22973 } 22974 } 22975 22976 fn checkPtrOperand( 22977 sema: *Sema, 22978 block: *Block, 22979 ty_src: LazySrcLoc, 22980 ty: Type, 22981 ) CompileError!void { 22982 const pt = sema.pt; 22983 const zcu = pt.zcu; 22984 switch (ty.zigTypeTag(zcu)) { 22985 .pointer => return, 22986 .@"fn" => { 22987 const msg = msg: { 22988 const msg = try sema.errMsg( 22989 ty_src, 22990 "expected pointer, found '{f}'", 22991 .{ty.fmt(pt)}, 22992 ); 22993 errdefer msg.destroy(sema.gpa); 22994 22995 try sema.errNote(ty_src, msg, "use '&' to obtain a function pointer", .{}); 22996 22997 break :msg msg; 22998 }; 22999 return sema.failWithOwnedErrorMsg(block, msg); 23000 }, 23001 .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return, 23002 else => {}, 23003 } 23004 return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)}); 23005 } 23006 23007 fn checkPtrType( 23008 sema: *Sema, 23009 block: *Block, 23010 ty_src: LazySrcLoc, 23011 ty: Type, 23012 allow_slice: bool, 23013 ) CompileError!void { 23014 const pt = sema.pt; 23015 const zcu = pt.zcu; 23016 switch (ty.zigTypeTag(zcu)) { 23017 .pointer => if (allow_slice or !ty.isSlice(zcu)) return, 23018 .@"fn" => { 23019 const msg = msg: { 23020 const msg = try sema.errMsg( 23021 ty_src, 23022 "expected pointer type, found '{f}'", 23023 .{ty.fmt(pt)}, 23024 ); 23025 errdefer msg.destroy(sema.gpa); 23026 23027 try sema.errNote(ty_src, msg, "use '*const ' to make a function pointer type", .{}); 23028 23029 break :msg msg; 23030 }; 23031 return sema.failWithOwnedErrorMsg(block, msg); 23032 }, 23033 .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return, 23034 else => {}, 23035 } 23036 return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)}); 23037 } 23038 23039 fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 23040 const pt = sema.pt; 23041 const zcu = pt.zcu; 23042 if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) { 23043 const target = zcu.getTarget(); 23044 const as = ty.ptrAddressSpace(zcu); 23045 if (target_util.arePointersLogical(target, as)) { 23046 return sema.failWithOwnedErrorMsg(block, msg: { 23047 const msg = try sema.errMsg(src, "illegal operation on logical pointer of type '{f}'", .{ty.fmt(pt)}); 23048 errdefer msg.destroy(sema.gpa); 23049 try sema.errNote( 23050 src, 23051 msg, 23052 "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}", 23053 .{ 23054 @tagName(as), 23055 @tagName(target.cpu.arch.family()), 23056 @tagName(target.os.tag), 23057 }, 23058 ); 23059 break :msg msg; 23060 }); 23061 } 23062 } 23063 } 23064 23065 fn checkVectorElemType( 23066 sema: *Sema, 23067 block: *Block, 23068 ty_src: LazySrcLoc, 23069 ty: Type, 23070 ) CompileError!void { 23071 const pt = sema.pt; 23072 const zcu = pt.zcu; 23073 switch (ty.zigTypeTag(zcu)) { 23074 .int, .float, .bool => return, 23075 .optional, .pointer => if (ty.isPtrAtRuntime(zcu)) return, 23076 else => {}, 23077 } 23078 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{f}'", .{ty.fmt(pt)}); 23079 } 23080 23081 fn checkFloatType( 23082 sema: *Sema, 23083 block: *Block, 23084 ty_src: LazySrcLoc, 23085 ty: Type, 23086 ) CompileError!void { 23087 const pt = sema.pt; 23088 const zcu = pt.zcu; 23089 switch (ty.zigTypeTag(zcu)) { 23090 .comptime_int, .comptime_float, .float => {}, 23091 else => return sema.fail(block, ty_src, "expected float type, found '{f}'", .{ty.fmt(pt)}), 23092 } 23093 } 23094 23095 fn checkNumericType( 23096 sema: *Sema, 23097 block: *Block, 23098 ty_src: LazySrcLoc, 23099 ty: Type, 23100 ) CompileError!void { 23101 const pt = sema.pt; 23102 const zcu = pt.zcu; 23103 switch (ty.zigTypeTag(zcu)) { 23104 .comptime_float, .float, .comptime_int, .int => {}, 23105 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 23106 .comptime_float, .float, .comptime_int, .int => {}, 23107 else => |t| return sema.fail(block, ty_src, "expected number, found '{t}'", .{t}), 23108 }, 23109 else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}), 23110 } 23111 } 23112 23113 /// Returns the casted pointer. 23114 fn checkAtomicPtrOperand( 23115 sema: *Sema, 23116 block: *Block, 23117 elem_ty: Type, 23118 elem_ty_src: LazySrcLoc, 23119 ptr: Air.Inst.Ref, 23120 ptr_src: LazySrcLoc, 23121 ptr_const: bool, 23122 ) CompileError!Air.Inst.Ref { 23123 const pt = sema.pt; 23124 const zcu = pt.zcu; 23125 var diag: Zcu.AtomicPtrAlignmentDiagnostics = .{}; 23126 const alignment = zcu.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) { 23127 error.OutOfMemory => return error.OutOfMemory, 23128 error.FloatTooBig => return sema.fail( 23129 block, 23130 elem_ty_src, 23131 "expected {d}-bit float type or smaller; found {d}-bit float type", 23132 .{ diag.max_bits, diag.bits }, 23133 ), 23134 error.IntTooBig => return sema.fail( 23135 block, 23136 elem_ty_src, 23137 "expected {d}-bit integer type or smaller; found {d}-bit integer type", 23138 .{ diag.max_bits, diag.bits }, 23139 ), 23140 error.BadType => return sema.fail( 23141 block, 23142 elem_ty_src, 23143 "expected bool, integer, float, enum, packed struct, or pointer type; found '{f}'", 23144 .{elem_ty.fmt(pt)}, 23145 ), 23146 }; 23147 23148 var wanted_ptr_data: InternPool.Key.PtrType = .{ 23149 .child = elem_ty.toIntern(), 23150 .flags = .{ 23151 .alignment = alignment, 23152 .is_const = ptr_const, 23153 }, 23154 }; 23155 23156 const ptr_ty = sema.typeOf(ptr); 23157 const ptr_data = switch (ptr_ty.zigTypeTag(zcu)) { 23158 .pointer => ptr_ty.ptrInfo(zcu), 23159 else => { 23160 const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data); 23161 _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 23162 unreachable; 23163 }, 23164 }; 23165 23166 wanted_ptr_data.flags.address_space = ptr_data.flags.address_space; 23167 wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero; 23168 wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile; 23169 23170 const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data); 23171 const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 23172 23173 return casted_ptr; 23174 } 23175 23176 fn checkPtrIsNotComptimeMutable( 23177 sema: *Sema, 23178 block: *Block, 23179 ptr_val: Value, 23180 ptr_src: LazySrcLoc, 23181 operand_src: LazySrcLoc, 23182 ) CompileError!void { 23183 _ = operand_src; 23184 if (sema.isComptimeMutablePtr(ptr_val)) { 23185 return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); 23186 } 23187 } 23188 23189 fn checkIntOrVector( 23190 sema: *Sema, 23191 block: *Block, 23192 operand: Air.Inst.Ref, 23193 operand_src: LazySrcLoc, 23194 ) CompileError!Type { 23195 const pt = sema.pt; 23196 const zcu = pt.zcu; 23197 const operand_ty = sema.typeOf(operand); 23198 switch (operand_ty.zigTypeTag(zcu)) { 23199 .int => return operand_ty, 23200 .vector => { 23201 const elem_ty = operand_ty.childType(zcu); 23202 switch (elem_ty.zigTypeTag(zcu)) { 23203 .int => return elem_ty, 23204 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{ 23205 elem_ty.fmt(pt), 23206 }), 23207 } 23208 }, 23209 else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{ 23210 operand_ty.fmt(pt), 23211 }), 23212 } 23213 } 23214 23215 fn checkIntOrVectorAllowComptime( 23216 sema: *Sema, 23217 block: *Block, 23218 operand_ty: Type, 23219 operand_src: LazySrcLoc, 23220 ) CompileError!Type { 23221 const pt = sema.pt; 23222 const zcu = pt.zcu; 23223 switch (operand_ty.zigTypeTag(zcu)) { 23224 .int, .comptime_int => return operand_ty, 23225 .vector => { 23226 const elem_ty = operand_ty.childType(zcu); 23227 switch (elem_ty.zigTypeTag(zcu)) { 23228 .int, .comptime_int => return elem_ty, 23229 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{ 23230 elem_ty.fmt(pt), 23231 }), 23232 } 23233 }, 23234 else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{ 23235 operand_ty.fmt(pt), 23236 }), 23237 } 23238 } 23239 23240 const SimdBinOp = struct { 23241 len: ?usize, 23242 /// Coerced to `result_ty`. 23243 lhs: Air.Inst.Ref, 23244 /// Coerced to `result_ty`. 23245 rhs: Air.Inst.Ref, 23246 lhs_val: ?Value, 23247 rhs_val: ?Value, 23248 /// Only different than `scalar_ty` when it is a vector operation. 23249 result_ty: Type, 23250 scalar_ty: Type, 23251 }; 23252 23253 fn checkSimdBinOp( 23254 sema: *Sema, 23255 block: *Block, 23256 src: LazySrcLoc, 23257 uncasted_lhs: Air.Inst.Ref, 23258 uncasted_rhs: Air.Inst.Ref, 23259 lhs_src: LazySrcLoc, 23260 rhs_src: LazySrcLoc, 23261 ) CompileError!SimdBinOp { 23262 const pt = sema.pt; 23263 const zcu = pt.zcu; 23264 const lhs_ty = sema.typeOf(uncasted_lhs); 23265 const rhs_ty = sema.typeOf(uncasted_rhs); 23266 23267 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 23268 const vec_len: ?usize = if (lhs_ty.zigTypeTag(zcu) == .vector) lhs_ty.vectorLen(zcu) else null; 23269 const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ 23270 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 23271 }); 23272 const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); 23273 const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); 23274 23275 return SimdBinOp{ 23276 .len = vec_len, 23277 .lhs = lhs, 23278 .rhs = rhs, 23279 .lhs_val = try sema.resolveValue(lhs), 23280 .rhs_val = try sema.resolveValue(rhs), 23281 .result_ty = result_ty, 23282 .scalar_ty = result_ty.scalarType(zcu), 23283 }; 23284 } 23285 23286 fn checkVectorizableBinaryOperands( 23287 sema: *Sema, 23288 block: *Block, 23289 src: LazySrcLoc, 23290 lhs_ty: Type, 23291 rhs_ty: Type, 23292 lhs_src: LazySrcLoc, 23293 rhs_src: LazySrcLoc, 23294 ) CompileError!void { 23295 const pt = sema.pt; 23296 const zcu = pt.zcu; 23297 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); 23298 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); 23299 if (lhs_zig_ty_tag != .vector and rhs_zig_ty_tag != .vector) return; 23300 23301 const lhs_is_vector = switch (lhs_zig_ty_tag) { 23302 .vector, .array => true, 23303 else => false, 23304 }; 23305 const rhs_is_vector = switch (rhs_zig_ty_tag) { 23306 .vector, .array => true, 23307 else => false, 23308 }; 23309 23310 if (lhs_is_vector and rhs_is_vector) { 23311 const lhs_len = lhs_ty.arrayLen(zcu); 23312 const rhs_len = rhs_ty.arrayLen(zcu); 23313 if (lhs_len != rhs_len) { 23314 const msg = msg: { 23315 const msg = try sema.errMsg(src, "vector length mismatch", .{}); 23316 errdefer msg.destroy(sema.gpa); 23317 try sema.errNote(lhs_src, msg, "length {d} here", .{lhs_len}); 23318 try sema.errNote(rhs_src, msg, "length {d} here", .{rhs_len}); 23319 break :msg msg; 23320 }; 23321 return sema.failWithOwnedErrorMsg(block, msg); 23322 } 23323 } else { 23324 const msg = msg: { 23325 const msg = try sema.errMsg(src, "mixed scalar and vector operands: '{f}' and '{f}'", .{ 23326 lhs_ty.fmt(pt), rhs_ty.fmt(pt), 23327 }); 23328 errdefer msg.destroy(sema.gpa); 23329 if (lhs_is_vector) { 23330 try sema.errNote(lhs_src, msg, "vector here", .{}); 23331 try sema.errNote(rhs_src, msg, "scalar here", .{}); 23332 } else { 23333 try sema.errNote(lhs_src, msg, "scalar here", .{}); 23334 try sema.errNote(rhs_src, msg, "vector here", .{}); 23335 } 23336 break :msg msg; 23337 }; 23338 return sema.failWithOwnedErrorMsg(block, msg); 23339 } 23340 } 23341 23342 fn resolveExportOptions( 23343 sema: *Sema, 23344 block: *Block, 23345 src: LazySrcLoc, 23346 zir_ref: Zir.Inst.Ref, 23347 ) CompileError!Zcu.Export.Options { 23348 const pt = sema.pt; 23349 const zcu = pt.zcu; 23350 const gpa = sema.gpa; 23351 const ip = &zcu.intern_pool; 23352 const export_options_ty = try sema.getBuiltinType(src, .ExportOptions); 23353 const air_ref = try sema.resolveInst(zir_ref); 23354 const options = try sema.coerce(block, export_options_ty, air_ref, src); 23355 23356 const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 23357 const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 23358 const section_src = block.src(.{ .init_field_section = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 23359 const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 23360 23361 const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src); 23362 const name = try sema.toConstString(block, name_src, name_operand, .{ .simple = .export_options }); 23363 23364 const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src); 23365 const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ .simple = .export_options }); 23366 const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage); 23367 23368 const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "section", .no_embedded_nulls), section_src); 23369 const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .simple = .export_options }); 23370 const section = if (section_opt_val.optionalValue(zcu)) |section_val| 23371 try sema.toConstString(block, section_src, Air.internedToRef(section_val.toIntern()), .{ .simple = .export_options }) 23372 else 23373 null; 23374 23375 const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src); 23376 const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ .simple = .export_options }); 23377 const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility); 23378 23379 if (name.len < 1) { 23380 return sema.fail(block, name_src, "exported symbol name cannot be empty", .{}); 23381 } 23382 23383 if (visibility != .default and linkage == .internal) { 23384 return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{ 23385 name, @tagName(visibility), 23386 }); 23387 } 23388 23389 return .{ 23390 .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls), 23391 .linkage = linkage, 23392 .section = try ip.getOrPutStringOpt(gpa, pt.tid, section, .no_embedded_nulls), 23393 .visibility = visibility, 23394 }; 23395 } 23396 23397 fn resolveBuiltinEnum( 23398 sema: *Sema, 23399 block: *Block, 23400 src: LazySrcLoc, 23401 zir_ref: Zir.Inst.Ref, 23402 comptime name: Zcu.BuiltinDecl, 23403 reason: ComptimeReason, 23404 ) CompileError!@field(std.builtin, @tagName(name)) { 23405 const ty = try sema.getBuiltinType(src, name); 23406 const air_ref = try sema.resolveInst(zir_ref); 23407 const coerced = try sema.coerce(block, ty, air_ref, src); 23408 const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); 23409 return sema.interpretBuiltinType(block, src, val, @field(std.builtin, @tagName(name))); 23410 } 23411 23412 fn resolveAtomicOrder( 23413 sema: *Sema, 23414 block: *Block, 23415 src: LazySrcLoc, 23416 zir_ref: Zir.Inst.Ref, 23417 reason: ComptimeReason, 23418 ) CompileError!std.builtin.AtomicOrder { 23419 return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicOrder, reason); 23420 } 23421 23422 fn resolveAtomicRmwOp( 23423 sema: *Sema, 23424 block: *Block, 23425 src: LazySrcLoc, 23426 zir_ref: Zir.Inst.Ref, 23427 ) CompileError!std.builtin.AtomicRmwOp { 23428 return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicRmwOp, .{ .simple = .operand_atomicRmw_operation }); 23429 } 23430 23431 fn zirCmpxchg( 23432 sema: *Sema, 23433 block: *Block, 23434 extended: Zir.Inst.Extended.InstData, 23435 ) CompileError!Air.Inst.Ref { 23436 const pt = sema.pt; 23437 const zcu = pt.zcu; 23438 const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data; 23439 const air_tag: Air.Inst.Tag = switch (extended.small) { 23440 0 => .cmpxchg_weak, 23441 1 => .cmpxchg_strong, 23442 else => unreachable, 23443 }; 23444 const src = block.nodeOffset(extra.node); 23445 // zig fmt: off 23446 const elem_ty_src = block.builtinCallArgSrc(extra.node, 0); 23447 const ptr_src = block.builtinCallArgSrc(extra.node, 1); 23448 const expected_src = block.builtinCallArgSrc(extra.node, 2); 23449 const new_value_src = block.builtinCallArgSrc(extra.node, 3); 23450 const success_order_src = block.builtinCallArgSrc(extra.node, 4); 23451 const failure_order_src = block.builtinCallArgSrc(extra.node, 5); 23452 // zig fmt: on 23453 const expected_value = try sema.resolveInst(extra.expected_value); 23454 const elem_ty = sema.typeOf(expected_value); 23455 if (elem_ty.zigTypeTag(zcu) == .float) { 23456 return sema.fail( 23457 block, 23458 elem_ty_src, 23459 "expected bool, integer, enum, packed struct, or pointer type; found '{f}'", 23460 .{elem_ty.fmt(pt)}, 23461 ); 23462 } 23463 const uncasted_ptr = try sema.resolveInst(extra.ptr); 23464 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 23465 const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); 23466 const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{ .simple = .atomic_order }); 23467 const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{ .simple = .atomic_order }); 23468 23469 if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.monotonic)) { 23470 return sema.fail(block, success_order_src, "success atomic ordering must be monotonic or stricter", .{}); 23471 } 23472 if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.monotonic)) { 23473 return sema.fail(block, failure_order_src, "failure atomic ordering must be monotonic or stricter", .{}); 23474 } 23475 if (@intFromEnum(failure_order) > @intFromEnum(success_order)) { 23476 return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{}); 23477 } 23478 if (failure_order == .release or failure_order == .acq_rel) { 23479 return sema.fail(block, failure_order_src, "failure atomic ordering must not be release or acq_rel", .{}); 23480 } 23481 23482 const result_ty = try pt.optionalType(elem_ty.toIntern()); 23483 23484 // special case zero bit types 23485 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { 23486 return Air.internedToRef((try pt.intern(.{ .opt = .{ 23487 .ty = result_ty.toIntern(), 23488 .val = .none, 23489 } }))); 23490 } 23491 23492 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 23493 if (try sema.resolveValue(expected_value)) |expected_val| { 23494 if (try sema.resolveValue(new_value)) |new_val| { 23495 if (expected_val.isUndef(zcu) or new_val.isUndef(zcu)) { 23496 // TODO: this should probably cause the memory stored at the pointer 23497 // to become undef as well 23498 return pt.undefRef(result_ty); 23499 } 23500 const ptr_ty = sema.typeOf(ptr); 23501 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 23502 const result_val = try pt.intern(.{ .opt = .{ 23503 .ty = result_ty.toIntern(), 23504 .val = if (stored_val.eql(expected_val, elem_ty, zcu)) blk: { 23505 try sema.storePtr(block, src, ptr, new_value); 23506 break :blk .none; 23507 } else stored_val.toIntern(), 23508 } }); 23509 return Air.internedToRef(result_val); 23510 } else break :rs new_value_src; 23511 } else break :rs expected_src; 23512 } else ptr_src; 23513 23514 const flags: u32 = @as(u32, @intFromEnum(success_order)) | 23515 (@as(u32, @intFromEnum(failure_order)) << 3); 23516 23517 try sema.requireRuntimeBlock(block, src, runtime_src); 23518 return block.addInst(.{ 23519 .tag = air_tag, 23520 .data = .{ .ty_pl = .{ 23521 .ty = Air.internedToRef(result_ty.toIntern()), 23522 .payload = try sema.addExtra(Air.Cmpxchg{ 23523 .ptr = ptr, 23524 .expected_value = expected_value, 23525 .new_value = new_value, 23526 .flags = flags, 23527 }), 23528 } }, 23529 }); 23530 } 23531 23532 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23533 const pt = sema.pt; 23534 const zcu = pt.zcu; 23535 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23536 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23537 const src = block.nodeOffset(inst_data.src_node); 23538 const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0); 23539 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat"); 23540 23541 switch (dest_ty.zigTypeTag(zcu)) { 23542 .array, .vector => {}, 23543 else => return sema.fail(block, src, "expected array or vector type, found '{f}'", .{dest_ty.fmt(pt)}), 23544 } 23545 23546 const operand = try sema.resolveInst(extra.rhs); 23547 const scalar_ty = dest_ty.childType(zcu); 23548 const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src); 23549 23550 const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu)); 23551 23552 if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { 23553 return Air.internedToRef(val.toIntern()); 23554 } 23555 23556 // We also need this case because `[0:s]T` is not OPV. 23557 if (len == 0) { 23558 const empty_aggregate = try pt.intern(.{ .aggregate = .{ 23559 .ty = dest_ty.toIntern(), 23560 .storage = .{ .elems = &.{} }, 23561 } }); 23562 return Air.internedToRef(empty_aggregate); 23563 } 23564 23565 const maybe_sentinel = dest_ty.sentinel(zcu); 23566 23567 if (try sema.resolveValue(scalar)) |scalar_val| { 23568 if (scalar_val.isUndef(zcu) and maybe_sentinel == null) { 23569 return pt.undefRef(dest_ty); 23570 } 23571 // TODO: I didn't want to put `.aggregate` on a separate line here; `zig fmt` bugs have forced my hand 23572 return Air.internedToRef(try pt.intern(.{ 23573 .aggregate = .{ 23574 .ty = dest_ty.toIntern(), 23575 .storage = s: { 23576 full: { 23577 if (dest_ty.zigTypeTag(zcu) == .vector) break :full; 23578 const sentinel = maybe_sentinel orelse break :full; 23579 if (sentinel.toIntern() == scalar_val.toIntern()) break :full; 23580 // This is a array with non-zero length and a sentinel which does not match the element. 23581 // We have to use the full `elems` representation. 23582 const elems = try sema.arena.alloc(InternPool.Index, len + 1); 23583 @memset(elems[0..len], scalar_val.toIntern()); 23584 elems[len] = sentinel.toIntern(); 23585 break :s .{ .elems = elems }; 23586 } 23587 break :s .{ .repeated_elem = scalar_val.toIntern() }; 23588 }, 23589 }, 23590 })); 23591 } 23592 23593 try sema.requireRuntimeBlock(block, src, scalar_src); 23594 23595 switch (dest_ty.zigTypeTag(zcu)) { 23596 .array => { 23597 const elems = try sema.arena.alloc(Air.Inst.Ref, len + @intFromBool(maybe_sentinel != null)); 23598 @memset(elems[0..len], scalar); 23599 if (maybe_sentinel) |s| elems[len] = Air.internedToRef(s.toIntern()); 23600 return block.addAggregateInit(dest_ty, elems); 23601 }, 23602 .vector => return block.addTyOp(.splat, dest_ty, scalar), 23603 else => unreachable, 23604 } 23605 } 23606 23607 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23608 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23609 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23610 const op_src = block.builtinCallArgSrc(inst_data.src_node, 0); 23611 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 1); 23612 const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, .ReduceOp, .{ .simple = .operand_reduce_operation }); 23613 const operand = try sema.resolveInst(extra.rhs); 23614 const operand_ty = sema.typeOf(operand); 23615 const pt = sema.pt; 23616 const zcu = pt.zcu; 23617 23618 if (operand_ty.zigTypeTag(zcu) != .vector) { 23619 return sema.fail(block, operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)}); 23620 } 23621 23622 const scalar_ty = operand_ty.childType(zcu); 23623 23624 // Type-check depending on operation. 23625 switch (operation) { 23626 .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) { 23627 .int, .bool => {}, 23628 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{f}'", .{ 23629 @tagName(operation), operand_ty.fmt(pt), 23630 }), 23631 }, 23632 .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) { 23633 .int, .float => {}, 23634 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{f}'", .{ 23635 @tagName(operation), operand_ty.fmt(pt), 23636 }), 23637 }, 23638 } 23639 23640 const vec_len = operand_ty.vectorLen(zcu); 23641 if (vec_len == 0) { 23642 // TODO re-evaluate if we should introduce a "neutral value" for some operations, 23643 // e.g. zero for add and one for mul. 23644 return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{}); 23645 } 23646 23647 if (try sema.resolveValue(operand)) |operand_val| { 23648 if (operand_val.isUndef(zcu)) return pt.undefRef(scalar_ty); 23649 23650 var accum: Value = try operand_val.elemValue(pt, 0); 23651 var i: u32 = 1; 23652 while (i < vec_len) : (i += 1) { 23653 const elem_val = try operand_val.elemValue(pt, i); 23654 switch (operation) { 23655 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, pt), 23656 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, pt), 23657 .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, pt), 23658 .Min => accum = accum.numberMin(elem_val, zcu), 23659 .Max => accum = accum.numberMax(elem_val, zcu), 23660 .Add => accum = try arith.addMaybeWrap(sema, scalar_ty, accum, elem_val), 23661 .Mul => accum = try arith.mulMaybeWrap(sema, scalar_ty, accum, elem_val), 23662 } 23663 } 23664 return Air.internedToRef(accum.toIntern()); 23665 } 23666 23667 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src); 23668 return block.addReduce(operand, operation); 23669 } 23670 23671 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23672 const pt = sema.pt; 23673 const zcu = pt.zcu; 23674 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23675 const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; 23676 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 23677 const mask_src = block.builtinCallArgSrc(inst_data.src_node, 3); 23678 23679 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 23680 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 23681 const a = try sema.resolveInst(extra.a); 23682 const b = try sema.resolveInst(extra.b); 23683 var mask = try sema.resolveInst(extra.mask); 23684 var mask_ty = sema.typeOf(mask); 23685 23686 const mask_len = switch (sema.typeOf(mask).zigTypeTag(zcu)) { 23687 .array, .vector => sema.typeOf(mask).arrayLen(zcu), 23688 else => return sema.fail(block, mask_src, "expected vector or array, found '{f}'", .{sema.typeOf(mask).fmt(pt)}), 23689 }; 23690 mask_ty = try pt.vectorType(.{ 23691 .len = @intCast(mask_len), 23692 .child = .i32_type, 23693 }); 23694 mask = try sema.coerce(block, mask_ty, mask, mask_src); 23695 const mask_val = try sema.resolveConstValue(block, mask_src, mask, .{ .simple = .operand_shuffle_mask }); 23696 return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(mask_len)); 23697 } 23698 23699 fn analyzeShuffle( 23700 sema: *Sema, 23701 block: *Block, 23702 src_node: std.zig.Ast.Node.Offset, 23703 elem_ty: Type, 23704 a_uncoerced: Air.Inst.Ref, 23705 b_uncoerced: Air.Inst.Ref, 23706 mask: Value, 23707 mask_len: u32, 23708 ) CompileError!Air.Inst.Ref { 23709 const pt = sema.pt; 23710 const zcu = pt.zcu; 23711 const a_src = block.builtinCallArgSrc(src_node, 1); 23712 const b_src = block.builtinCallArgSrc(src_node, 2); 23713 const mask_src = block.builtinCallArgSrc(src_node, 3); 23714 23715 // If the type of `a` is `@Type(.undefined)`, i.e. the argument is untyped, 23716 // this is 0, because it is an error to index into this vector. 23717 const a_len: u32 = switch (sema.typeOf(a_uncoerced).zigTypeTag(zcu)) { 23718 .array, .vector => @intCast(sema.typeOf(a_uncoerced).arrayLen(zcu)), 23719 .undefined => 0, 23720 else => return sema.fail(block, a_src, "expected vector of '{f}', found '{f}'", .{ 23721 elem_ty.fmt(pt), sema.typeOf(a_uncoerced).fmt(pt), 23722 }), 23723 }; 23724 const a_ty = try pt.vectorType(.{ .len = a_len, .child = elem_ty.toIntern() }); 23725 const a_coerced = try sema.coerce(block, a_ty, a_uncoerced, a_src); 23726 23727 // If the type of `b` is `@Type(.undefined)`, i.e. the argument is untyped, this is 0, because it is an error to index into this vector. 23728 const b_len: u32 = switch (sema.typeOf(b_uncoerced).zigTypeTag(zcu)) { 23729 .array, .vector => @intCast(sema.typeOf(b_uncoerced).arrayLen(zcu)), 23730 .undefined => 0, 23731 else => return sema.fail(block, b_src, "expected vector of '{f}', found '{f}'", .{ 23732 elem_ty.fmt(pt), sema.typeOf(b_uncoerced).fmt(pt), 23733 }), 23734 }; 23735 const b_ty = try pt.vectorType(.{ .len = b_len, .child = elem_ty.toIntern() }); 23736 const b_coerced = try sema.coerce(block, b_ty, b_uncoerced, b_src); 23737 23738 const result_ty = try pt.vectorType(.{ .len = mask_len, .child = elem_ty.toIntern() }); 23739 23740 // We're going to pre-emptively reserve space in `sema.air_extra`. The reason for this is we need 23741 // a `u32` buffer of length `mask_len` anyway, and putting it in `sema.air_extra` avoids a copy 23742 // in the runtime case. If the result is comptime-known, we'll shrink `air_extra` back. 23743 const air_extra_idx: u32 = @intCast(sema.air_extra.items.len); 23744 const air_mask_buf = try sema.air_extra.addManyAsSlice(sema.gpa, mask_len); 23745 23746 // We want to interpret that buffer in `air_extra` in a few ways. Initially, we'll consider its 23747 // elements as `Air.Inst.ShuffleTwoMask`, essentially representing the raw mask values; then, we'll 23748 // convert it to `InternPool.Index` or `Air.Inst.ShuffleOneMask` if there are comptime-known operands. 23749 const mask_ip_index: []InternPool.Index = @ptrCast(air_mask_buf); 23750 const mask_shuffle_one: []Air.ShuffleOneMask = @ptrCast(air_mask_buf); 23751 const mask_shuffle_two: []Air.ShuffleTwoMask = @ptrCast(air_mask_buf); 23752 23753 // Initial loop: check mask elements, populate `mask_shuffle_two`. 23754 var a_used = false; 23755 var b_used = false; 23756 for (mask_shuffle_two, 0..mask_len) |*out, mask_idx| { 23757 const mask_val = try mask.elemValue(pt, mask_idx); 23758 if (mask_val.isUndef(zcu)) { 23759 out.* = .undef; 23760 continue; 23761 } 23762 // Safe because mask elements are `i32` and we already checked for undef: 23763 const raw = (try sema.resolveLazyValue(mask_val)).toSignedInt(zcu); 23764 if (raw >= 0) { 23765 const idx: u32 = @intCast(raw); 23766 a_used = true; 23767 out.* = .aElem(idx); 23768 if (idx >= a_len) return sema.failWithOwnedErrorMsg(block, msg: { 23769 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx}); 23770 errdefer msg.destroy(sema.gpa); 23771 try sema.errNote(a_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, a_ty.fmt(pt) }); 23772 if (idx < b_len) { 23773 try sema.errNote(b_src, msg, "use '~@as(u32, {d})' to index into second vector given here", .{idx}); 23774 } 23775 break :msg msg; 23776 }); 23777 } else { 23778 const idx: u32 = @intCast(~raw); 23779 b_used = true; 23780 out.* = .bElem(idx); 23781 if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: { 23782 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx}); 23783 errdefer msg.destroy(sema.gpa); 23784 try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, b_ty.fmt(pt) }); 23785 break :msg msg; 23786 }); 23787 } 23788 } 23789 23790 const maybe_a_val = try sema.resolveValue(a_coerced); 23791 const maybe_b_val = try sema.resolveValue(b_coerced); 23792 23793 const a_rt = a_used and maybe_a_val == null; 23794 const b_rt = b_used and maybe_b_val == null; 23795 23796 if (a_rt and b_rt) { 23797 // Both operands are needed and runtime-known. We need a `[]ShuffleTwomask`... which is 23798 // exactly what we already have in `mask_shuffle_two`! So, we're basically done already. 23799 // We just need to append the two operands. 23800 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 2); 23801 sema.appendRefsAssumeCapacity(&.{ a_coerced, b_coerced }); 23802 return block.addInst(.{ 23803 .tag = .shuffle_two, 23804 .data = .{ .ty_pl = .{ 23805 .ty = Air.internedToRef(result_ty.toIntern()), 23806 .payload = air_extra_idx, 23807 } }, 23808 }); 23809 } else if (a_rt) { 23810 // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`. 23811 for (mask_shuffle_two, mask_shuffle_one) |in, *out| { 23812 out.* = switch (in.unwrap()) { 23813 .undef => .value(try pt.undefValue(elem_ty)), 23814 .a_elem => |idx| .elem(idx), 23815 .b_elem => |idx| .value(try maybe_b_val.?.elemValue(pt, idx)), 23816 }; 23817 } 23818 // Now just append our single runtime operand, and we're done. 23819 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1); 23820 sema.appendRefsAssumeCapacity(&.{a_coerced}); 23821 return block.addInst(.{ 23822 .tag = .shuffle_one, 23823 .data = .{ .ty_pl = .{ 23824 .ty = Air.internedToRef(result_ty.toIntern()), 23825 .payload = air_extra_idx, 23826 } }, 23827 }); 23828 } else if (b_rt) { 23829 // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`. 23830 for (mask_shuffle_two, mask_shuffle_one) |in, *out| { 23831 out.* = switch (in.unwrap()) { 23832 .undef => .value(try pt.undefValue(elem_ty)), 23833 .a_elem => |idx| .value(try maybe_a_val.?.elemValue(pt, idx)), 23834 .b_elem => |idx| .elem(idx), 23835 }; 23836 } 23837 // Now just append our single runtime operand, and we're done. 23838 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1); 23839 sema.appendRefsAssumeCapacity(&.{b_coerced}); 23840 return block.addInst(.{ 23841 .tag = .shuffle_one, 23842 .data = .{ .ty_pl = .{ 23843 .ty = Air.internedToRef(result_ty.toIntern()), 23844 .payload = air_extra_idx, 23845 } }, 23846 }); 23847 } else { 23848 // The result will be comptime-known. We must convert the `ShuffleTwoMask` values to 23849 // `InternPool.Index` values using the known operands. 23850 for (mask_shuffle_two, mask_ip_index) |in, *out| { 23851 const val: Value = switch (in.unwrap()) { 23852 .undef => try pt.undefValue(elem_ty), 23853 .a_elem => |idx| try maybe_a_val.?.elemValue(pt, idx), 23854 .b_elem => |idx| try maybe_b_val.?.elemValue(pt, idx), 23855 }; 23856 out.* = val.toIntern(); 23857 } 23858 const res = try pt.intern(.{ .aggregate = .{ 23859 .ty = result_ty.toIntern(), 23860 .storage = .{ .elems = mask_ip_index }, 23861 } }); 23862 // We have a comptime-known result, so didn't need `air_mask_buf` -- remove it from `sema.air_extra`. 23863 assert(sema.air_extra.items.len == air_extra_idx + air_mask_buf.len); 23864 sema.air_extra.shrinkRetainingCapacity(air_extra_idx); 23865 return Air.internedToRef(res); 23866 } 23867 } 23868 23869 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 23870 const pt = sema.pt; 23871 const zcu = pt.zcu; 23872 const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; 23873 23874 const src = block.nodeOffset(extra.node); 23875 const elem_ty_src = block.builtinCallArgSrc(extra.node, 0); 23876 const pred_src = block.builtinCallArgSrc(extra.node, 1); 23877 const a_src = block.builtinCallArgSrc(extra.node, 2); 23878 const b_src = block.builtinCallArgSrc(extra.node, 3); 23879 23880 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 23881 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 23882 const pred_uncoerced = try sema.resolveInst(extra.pred); 23883 const pred_ty = sema.typeOf(pred_uncoerced); 23884 23885 const vec_len_u64 = switch (pred_ty.zigTypeTag(zcu)) { 23886 .vector, .array => pred_ty.arrayLen(zcu), 23887 else => return sema.fail(block, pred_src, "expected vector or array, found '{f}'", .{pred_ty.fmt(pt)}), 23888 }; 23889 const vec_len: u32 = @intCast(try sema.usizeCast(block, pred_src, vec_len_u64)); 23890 23891 const bool_vec_ty = try pt.vectorType(.{ 23892 .len = vec_len, 23893 .child = .bool_type, 23894 }); 23895 const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); 23896 23897 const vec_ty = try pt.vectorType(.{ 23898 .len = vec_len, 23899 .child = elem_ty.toIntern(), 23900 }); 23901 const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src); 23902 const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src); 23903 23904 const maybe_pred = try sema.resolveValue(pred); 23905 const maybe_a = try sema.resolveValue(a); 23906 const maybe_b = try sema.resolveValue(b); 23907 23908 const runtime_src = if (maybe_pred) |pred_val| rs: { 23909 if (pred_val.isUndef(zcu)) return pt.undefRef(vec_ty); 23910 23911 if (maybe_a) |a_val| { 23912 if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty); 23913 23914 if (maybe_b) |b_val| { 23915 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); 23916 23917 const elems = try sema.gpa.alloc(InternPool.Index, vec_len); 23918 defer sema.gpa.free(elems); 23919 for (elems, 0..) |*elem, i| { 23920 const pred_elem_val = try pred_val.elemValue(pt, i); 23921 const should_choose_a = pred_elem_val.toBool(); 23922 elem.* = (try (if (should_choose_a) a_val else b_val).elemValue(pt, i)).toIntern(); 23923 } 23924 23925 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 23926 .ty = vec_ty.toIntern(), 23927 .storage = .{ .elems = elems }, 23928 } }))); 23929 } else { 23930 break :rs b_src; 23931 } 23932 } else { 23933 if (maybe_b) |b_val| { 23934 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); 23935 } 23936 break :rs a_src; 23937 } 23938 } else rs: { 23939 if (maybe_a) |a_val| { 23940 if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty); 23941 } 23942 if (maybe_b) |b_val| { 23943 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); 23944 } 23945 break :rs pred_src; 23946 }; 23947 23948 try sema.requireRuntimeBlock(block, src, runtime_src); 23949 return block.addInst(.{ 23950 .tag = .select, 23951 .data = .{ .pl_op = .{ 23952 .operand = pred, 23953 .payload = try block.sema.addExtra(Air.Bin{ 23954 .lhs = a, 23955 .rhs = b, 23956 }), 23957 } }, 23958 }); 23959 } 23960 23961 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23962 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23963 const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; 23964 // zig fmt: off 23965 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 23966 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); 23967 const order_src = block.builtinCallArgSrc(inst_data.src_node, 2); 23968 // zig fmt: on 23969 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 23970 const uncasted_ptr = try sema.resolveInst(extra.ptr); 23971 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); 23972 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); 23973 23974 switch (order) { 23975 .release, .acq_rel => { 23976 return sema.fail( 23977 block, 23978 order_src, 23979 "@atomicLoad atomic ordering must not be release or acq_rel", 23980 .{}, 23981 ); 23982 }, 23983 else => {}, 23984 } 23985 23986 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 23987 return Air.internedToRef(val.toIntern()); 23988 } 23989 23990 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 23991 if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { 23992 return Air.internedToRef(elem_val.toIntern()); 23993 } 23994 } 23995 23996 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src); 23997 return block.addInst(.{ 23998 .tag = .atomic_load, 23999 .data = .{ .atomic_load = .{ 24000 .ptr = ptr, 24001 .order = order, 24002 } }, 24003 }); 24004 } 24005 24006 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24007 const pt = sema.pt; 24008 const zcu = pt.zcu; 24009 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24010 const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; 24011 const src = block.nodeOffset(inst_data.src_node); 24012 // zig fmt: off 24013 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24014 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24015 const op_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24016 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 3); 24017 const order_src = block.builtinCallArgSrc(inst_data.src_node, 4); 24018 // zig fmt: on 24019 const operand = try sema.resolveInst(extra.operand); 24020 const elem_ty = sema.typeOf(operand); 24021 const uncasted_ptr = try sema.resolveInst(extra.ptr); 24022 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 24023 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); 24024 24025 switch (elem_ty.zigTypeTag(zcu)) { 24026 .@"enum" => if (op != .Xchg) { 24027 return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); 24028 }, 24029 .bool => if (op != .Xchg) { 24030 return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{}); 24031 }, 24032 .float => switch (op) { 24033 .Xchg, .Add, .Sub, .Max, .Min => {}, 24034 else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}), 24035 }, 24036 else => {}, 24037 } 24038 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); 24039 24040 if (order == .unordered) { 24041 return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be unordered", .{}); 24042 } 24043 24044 // special case zero bit types 24045 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 24046 return Air.internedToRef(val.toIntern()); 24047 } 24048 24049 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 24050 const maybe_operand_val = try sema.resolveValue(operand); 24051 const operand_val = maybe_operand_val orelse { 24052 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); 24053 break :rs operand_src; 24054 }; 24055 if (sema.isComptimeMutablePtr(ptr_val)) { 24056 const ptr_ty = sema.typeOf(ptr); 24057 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 24058 const new_val = switch (op) { 24059 // zig fmt: off 24060 .Xchg => operand_val, 24061 .Add => try arith.addMaybeWrap(sema, elem_ty, stored_val, operand_val), 24062 .Sub => try arith.subMaybeWrap(sema, elem_ty, stored_val, operand_val), 24063 .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, pt ), 24064 .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, pt ), 24065 .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, pt ), 24066 .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, pt ), 24067 .Max => stored_val.numberMax (operand_val, zcu), 24068 .Min => stored_val.numberMin (operand_val, zcu), 24069 // zig fmt: on 24070 }; 24071 try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); 24072 return Air.internedToRef(stored_val.toIntern()); 24073 } else break :rs ptr_src; 24074 } else ptr_src; 24075 24076 const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3); 24077 24078 try sema.requireRuntimeBlock(block, src, runtime_src); 24079 return block.addInst(.{ 24080 .tag = .atomic_rmw, 24081 .data = .{ .pl_op = .{ 24082 .operand = ptr, 24083 .payload = try sema.addExtra(Air.AtomicRmw{ 24084 .operand = operand, 24085 .flags = flags, 24086 }), 24087 } }, 24088 }); 24089 } 24090 24091 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 24092 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24093 const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; 24094 const src = block.nodeOffset(inst_data.src_node); 24095 // zig fmt: off 24096 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24097 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24098 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24099 const order_src = block.builtinCallArgSrc(inst_data.src_node, 3); 24100 // zig fmt: on 24101 const operand = try sema.resolveInst(extra.operand); 24102 const elem_ty = sema.typeOf(operand); 24103 const uncasted_ptr = try sema.resolveInst(extra.ptr); 24104 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 24105 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); 24106 24107 const air_tag: Air.Inst.Tag = switch (order) { 24108 .acquire, .acq_rel => { 24109 return sema.fail( 24110 block, 24111 order_src, 24112 "@atomicStore atomic ordering must not be acquire or acq_rel", 24113 .{}, 24114 ); 24115 }, 24116 .unordered => .atomic_store_unordered, 24117 .monotonic => .atomic_store_monotonic, 24118 .release => .atomic_store_release, 24119 .seq_cst => .atomic_store_seq_cst, 24120 }; 24121 24122 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 24123 } 24124 24125 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24126 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24127 const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data; 24128 const src = block.nodeOffset(inst_data.src_node); 24129 24130 const mulend1_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24131 const mulend2_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24132 const addend_src = block.builtinCallArgSrc(inst_data.src_node, 3); 24133 24134 const addend = try sema.resolveInst(extra.addend); 24135 const ty = sema.typeOf(addend); 24136 const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src); 24137 const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src); 24138 24139 const maybe_mulend1 = try sema.resolveValue(mulend1); 24140 const maybe_mulend2 = try sema.resolveValue(mulend2); 24141 const maybe_addend = try sema.resolveValue(addend); 24142 const pt = sema.pt; 24143 const zcu = pt.zcu; 24144 24145 switch (ty.scalarType(zcu).zigTypeTag(zcu)) { 24146 .comptime_float, .float => {}, 24147 else => return sema.fail(block, src, "expected vector of floats or float type, found '{f}'", .{ty.fmt(pt)}), 24148 } 24149 24150 const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { 24151 if (maybe_mulend2) |mulend2_val| { 24152 if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty); 24153 24154 if (maybe_addend) |addend_val| { 24155 if (addend_val.isUndef(zcu)) return pt.undefRef(ty); 24156 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, pt); 24157 return Air.internedToRef(result_val.toIntern()); 24158 } else { 24159 break :rs addend_src; 24160 } 24161 } else { 24162 if (maybe_addend) |addend_val| { 24163 if (addend_val.isUndef(zcu)) return pt.undefRef(ty); 24164 } 24165 break :rs mulend2_src; 24166 } 24167 } else rs: { 24168 if (maybe_mulend2) |mulend2_val| { 24169 if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty); 24170 } 24171 if (maybe_addend) |addend_val| { 24172 if (addend_val.isUndef(zcu)) return pt.undefRef(ty); 24173 } 24174 break :rs mulend1_src; 24175 }; 24176 24177 try sema.requireRuntimeBlock(block, src, runtime_src); 24178 return block.addInst(.{ 24179 .tag = .mul_add, 24180 .data = .{ .pl_op = .{ 24181 .operand = addend, 24182 .payload = try sema.addExtra(Air.Bin{ 24183 .lhs = mulend1, 24184 .rhs = mulend2, 24185 }), 24186 } }, 24187 }); 24188 } 24189 24190 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24191 const tracy = trace(@src()); 24192 defer tracy.end(); 24193 24194 const pt = sema.pt; 24195 const zcu = pt.zcu; 24196 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24197 const modifier_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24198 const func_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24199 const args_src = block.builtinCallArgSrc(inst_data.src_node, 2); 24200 const call_src = block.nodeOffset(inst_data.src_node); 24201 24202 const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; 24203 const func = try sema.resolveInst(extra.callee); 24204 24205 const modifier_ty = try sema.getBuiltinType(call_src, .CallModifier); 24206 const air_ref = try sema.resolveInst(extra.modifier); 24207 const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); 24208 const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ .simple = .call_modifier }); 24209 var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier); 24210 switch (modifier) { 24211 // These can be upgraded to comptime or nosuspend calls. 24212 .auto, .never_tail, .no_suspend => { 24213 if (block.isComptime()) { 24214 if (modifier == .never_tail) { 24215 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{}); 24216 } 24217 modifier = .compile_time; 24218 } else if (extra.flags.is_nosuspend) { 24219 modifier = .no_suspend; 24220 } 24221 }, 24222 // These can be upgraded to comptime. nosuspend bit can be safely ignored. 24223 .always_inline, .compile_time => { 24224 _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { 24225 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)}); 24226 }; 24227 24228 if (block.isComptime()) { 24229 modifier = .compile_time; 24230 } 24231 }, 24232 .always_tail => { 24233 if (block.isComptime()) { 24234 modifier = .compile_time; 24235 } 24236 }, 24237 .never_inline => { 24238 if (block.isComptime()) { 24239 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{}); 24240 } 24241 }, 24242 } 24243 24244 const args = try sema.resolveInst(extra.args); 24245 24246 const args_ty = sema.typeOf(args); 24247 if (!args_ty.isTuple(zcu)) { 24248 return sema.fail(block, args_src, "expected a tuple, found '{f}'", .{args_ty.fmt(pt)}); 24249 } 24250 24251 const resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(zcu)); 24252 for (resolved_args, 0..) |*resolved, i| { 24253 resolved.* = try sema.tupleFieldValByIndex(block, args, @intCast(i), args_ty); 24254 } 24255 24256 const callee_ty = sema.typeOf(func); 24257 const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false); 24258 const ensure_result_used = extra.flags.ensure_result_used; 24259 return sema.analyzeCall( 24260 block, 24261 func, 24262 func_ty, 24263 func_src, 24264 call_src, 24265 modifier, 24266 ensure_result_used, 24267 .{ .call_builtin = .{ 24268 .call_node_offset = inst_data.src_node, 24269 .args = resolved_args, 24270 } }, 24271 null, 24272 .@"@call", 24273 ); 24274 } 24275 24276 fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 24277 const pt = sema.pt; 24278 const zcu = pt.zcu; 24279 const ip = &zcu.intern_pool; 24280 24281 const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, extended.operand).data; 24282 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 24283 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); 24284 assert(!flags.ptr_cast); 24285 const inst_src = block.nodeOffset(extra.src_node); 24286 const field_name_src = block.builtinCallArgSrc(extra.src_node, 0); 24287 const field_ptr_src = block.builtinCallArgSrc(extra.src_node, 1); 24288 24289 const parent_ptr_ty = try sema.resolveDestType(block, inst_src, extra.parent_ptr_type, .remove_eu, "@fieldParentPtr"); 24290 try sema.checkPtrType(block, inst_src, parent_ptr_ty, true); 24291 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); 24292 if (parent_ptr_info.flags.size != .one) { 24293 return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)}); 24294 } 24295 const parent_ty: Type = .fromInterned(parent_ptr_info.child); 24296 switch (parent_ty.zigTypeTag(zcu)) { 24297 .@"struct", .@"union" => {}, 24298 else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}), 24299 } 24300 try parent_ty.resolveLayout(pt); 24301 24302 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); 24303 const field_index = switch (parent_ty.zigTypeTag(zcu)) { 24304 .@"struct" => blk: { 24305 if (parent_ty.isTuple(zcu)) { 24306 if (field_name.eqlSlice("len", ip)) { 24307 return sema.fail(block, inst_src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); 24308 } 24309 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, field_name_src); 24310 } else { 24311 break :blk try sema.structFieldIndex(block, parent_ty, field_name, field_name_src); 24312 } 24313 }, 24314 .@"union" => try sema.unionFieldIndex(block, parent_ty, field_name, field_name_src), 24315 else => unreachable, 24316 }; 24317 if (parent_ty.zigTypeTag(zcu) == .@"struct" and parent_ty.structFieldIsComptime(field_index, zcu)) { 24318 return sema.fail(block, field_name_src, "cannot get @fieldParentPtr of a comptime field", .{}); 24319 } 24320 24321 const field_ptr = try sema.resolveInst(extra.field_ptr); 24322 const field_ptr_ty = sema.typeOf(field_ptr); 24323 try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty); 24324 const field_ptr_info = field_ptr_ty.ptrInfo(zcu); 24325 24326 var actual_parent_ptr_info: InternPool.Key.PtrType = .{ 24327 .child = parent_ty.toIntern(), 24328 .flags = .{ 24329 .alignment = try parent_ptr_ty.ptrAlignmentSema(pt), 24330 .is_const = field_ptr_info.flags.is_const, 24331 .is_volatile = field_ptr_info.flags.is_volatile, 24332 .is_allowzero = field_ptr_info.flags.is_allowzero, 24333 .address_space = field_ptr_info.flags.address_space, 24334 }, 24335 .packed_offset = parent_ptr_info.packed_offset, 24336 }; 24337 const field_ty = parent_ty.fieldType(field_index, zcu); 24338 var actual_field_ptr_info: InternPool.Key.PtrType = .{ 24339 .child = field_ty.toIntern(), 24340 .flags = .{ 24341 .alignment = try field_ptr_ty.ptrAlignmentSema(pt), 24342 .is_const = field_ptr_info.flags.is_const, 24343 .is_volatile = field_ptr_info.flags.is_volatile, 24344 .is_allowzero = field_ptr_info.flags.is_allowzero, 24345 .address_space = field_ptr_info.flags.address_space, 24346 }, 24347 .packed_offset = field_ptr_info.packed_offset, 24348 }; 24349 switch (parent_ty.containerLayout(zcu)) { 24350 .auto => { 24351 actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict( 24352 if (zcu.typeToStruct(parent_ty)) |struct_obj| 24353 try field_ty.structFieldAlignmentSema( 24354 struct_obj.fieldAlign(ip, field_index), 24355 struct_obj.layout, 24356 pt, 24357 ) 24358 else if (zcu.typeToUnion(parent_ty)) |union_obj| 24359 try field_ty.unionFieldAlignmentSema( 24360 union_obj.fieldAlign(ip, field_index), 24361 union_obj.flagsUnordered(ip).layout, 24362 pt, 24363 ) 24364 else 24365 actual_field_ptr_info.flags.alignment, 24366 ); 24367 24368 actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; 24369 actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; 24370 }, 24371 .@"extern" => { 24372 const field_offset = parent_ty.structFieldOffset(field_index, zcu); 24373 actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (field_offset > 0) 24374 Alignment.fromLog2Units(@ctz(field_offset)) 24375 else 24376 actual_field_ptr_info.flags.alignment); 24377 24378 actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; 24379 actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 }; 24380 }, 24381 .@"packed" => { 24382 const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) + 24383 (if (zcu.typeToStruct(parent_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, field_index) else 0) - 24384 actual_field_ptr_info.packed_offset.bit_offset), 8) catch 24385 return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{}); 24386 actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0) 24387 Alignment.fromLog2Units(@ctz(byte_offset)) 24388 else 24389 actual_field_ptr_info.flags.alignment); 24390 }, 24391 } 24392 24393 const actual_field_ptr_ty = try pt.ptrTypeSema(actual_field_ptr_info); 24394 const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, field_ptr_src); 24395 const actual_parent_ptr_ty = try pt.ptrTypeSema(actual_parent_ptr_info); 24396 24397 const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: { 24398 switch (parent_ty.zigTypeTag(zcu)) { 24399 .@"struct" => switch (parent_ty.containerLayout(zcu)) { 24400 .auto => {}, 24401 .@"extern" => { 24402 const byte_offset = parent_ty.structFieldOffset(field_index, zcu); 24403 const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty); 24404 break :result Air.internedToRef(parent_ptr_val.toIntern()); 24405 }, 24406 .@"packed" => { 24407 // Logic lifted from type computation above - I'm just assuming it's correct. 24408 // `catch unreachable` since error case handled above. 24409 const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) + 24410 zcu.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) - 24411 actual_field_ptr_info.packed_offset.bit_offset), 8) catch unreachable; 24412 const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty); 24413 break :result Air.internedToRef(parent_ptr_val.toIntern()); 24414 }, 24415 }, 24416 .@"union" => switch (parent_ty.containerLayout(zcu)) { 24417 .auto => {}, 24418 .@"extern", .@"packed" => { 24419 // For an extern or packed union, just coerce the pointer. 24420 const parent_ptr_val = try pt.getCoerced(field_ptr_val, actual_parent_ptr_ty); 24421 break :result Air.internedToRef(parent_ptr_val.toIntern()); 24422 }, 24423 }, 24424 else => unreachable, 24425 } 24426 24427 const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: { 24428 const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) { 24429 .ptr => |ptr| ptr, 24430 else => break :opt_field null, 24431 }; 24432 if (ptr.byte_offset != 0) break :opt_field null; 24433 break :opt_field switch (ptr.base_addr) { 24434 .field => |field| field, 24435 else => null, 24436 }; 24437 }; 24438 24439 const field = opt_field orelse { 24440 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); 24441 }; 24442 24443 if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) { 24444 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); 24445 } 24446 24447 if (field.index != field_index) { 24448 return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{ 24449 field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt), 24450 }); 24451 } 24452 break :result try sema.coerce(block, actual_parent_ptr_ty, Air.internedToRef(field.base), inst_src); 24453 } else result: { 24454 try sema.requireRuntimeBlock(block, inst_src, field_ptr_src); 24455 break :result try block.addInst(.{ 24456 .tag = .field_parent_ptr, 24457 .data = .{ .ty_pl = .{ 24458 .ty = Air.internedToRef(actual_parent_ptr_ty.toIntern()), 24459 .payload = try block.sema.addExtra(Air.FieldParentPtr{ 24460 .field_ptr = casted_field_ptr, 24461 .field_index = @intCast(field_index), 24462 }), 24463 } }, 24464 }); 24465 }; 24466 return sema.ptrCastFull(block, flags, inst_src, result, inst_src, parent_ptr_ty, "@fieldParentPtr"); 24467 } 24468 24469 fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value { 24470 const pt = sema.pt; 24471 const zcu = pt.zcu; 24472 if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty); 24473 var ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) { 24474 .undef => return sema.failWithUseOfUndef(block, src), 24475 .ptr => |ptr| ptr, 24476 else => unreachable, 24477 }; 24478 if (ptr.byte_offset < byte_subtract) { 24479 return sema.failWithOwnedErrorMsg(block, msg: { 24480 const msg = try sema.errMsg(src, "pointer computation here causes illegal behavior", .{}); 24481 errdefer msg.destroy(sema.gpa); 24482 try sema.errNote(src, msg, "resulting pointer exceeds bounds of containing value which may trigger overflow", .{}); 24483 break :msg msg; 24484 }); 24485 } 24486 ptr.byte_offset -= byte_subtract; 24487 ptr.ty = new_ty.toIntern(); 24488 return Value.fromInterned(try pt.intern(.{ .ptr = ptr })); 24489 } 24490 24491 fn zirMinMax( 24492 sema: *Sema, 24493 block: *Block, 24494 inst: Zir.Inst.Index, 24495 comptime air_tag: Air.Inst.Tag, 24496 ) CompileError!Air.Inst.Ref { 24497 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24498 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 24499 const src = block.nodeOffset(inst_data.src_node); 24500 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24501 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24502 const lhs = try sema.resolveInst(extra.lhs); 24503 const rhs = try sema.resolveInst(extra.rhs); 24504 return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src }); 24505 } 24506 24507 fn zirMinMaxMulti( 24508 sema: *Sema, 24509 block: *Block, 24510 extended: Zir.Inst.Extended.InstData, 24511 comptime air_tag: Air.Inst.Tag, 24512 ) CompileError!Air.Inst.Ref { 24513 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 24514 const src_node = extra.data.src_node; 24515 const src = block.nodeOffset(src_node); 24516 const operands = sema.code.refSlice(extra.end, extended.small); 24517 24518 const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 24519 const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len); 24520 24521 for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| { 24522 op_src.* = block.builtinCallArgSrc(src_node, @intCast(i)); 24523 air_ref.* = try sema.resolveInst(zir_ref); 24524 } 24525 24526 return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs); 24527 } 24528 24529 fn analyzeMinMax( 24530 sema: *Sema, 24531 block: *Block, 24532 src: LazySrcLoc, 24533 comptime air_tag: Air.Inst.Tag, 24534 operands: []const Air.Inst.Ref, 24535 operand_srcs: []const LazySrcLoc, 24536 ) CompileError!Air.Inst.Ref { 24537 assert(operands.len == operand_srcs.len); 24538 assert(operands.len > 0); 24539 24540 const pt = sema.pt; 24541 const zcu = pt.zcu; 24542 24543 // This function has the signature `fn (Value, Value, *Zcu) Value`. 24544 // It is only used on scalar values, although the values may have different types. 24545 // If either operand is undef, it returns undef. 24546 const opFunc = switch (air_tag) { 24547 .min => Value.numberMin, 24548 .max => Value.numberMax, 24549 else => comptime unreachable, 24550 }; 24551 24552 if (operands.len == 1) { 24553 try sema.checkNumericType(block, operand_srcs[0], sema.typeOf(operands[0])); 24554 return operands[0]; 24555 } 24556 24557 // First, basic type validation; we'll make sure all the operands are numeric and agree on vector length. 24558 // This value will be `null` for a scalar type, otherwise the length of the vector type. 24559 const vector_len: ?u64 = vec_len: { 24560 const first_operand_ty = sema.typeOf(operands[0]); 24561 try sema.checkNumericType(block, operand_srcs[0], first_operand_ty); 24562 if (first_operand_ty.zigTypeTag(zcu) == .vector) { 24563 const vec_len = first_operand_ty.vectorLen(zcu); 24564 for (operands[1..], operand_srcs[1..]) |operand, operand_src| { 24565 const operand_ty = sema.typeOf(operand); 24566 try sema.checkNumericType(block, operand_src, operand_ty); 24567 if (operand_ty.zigTypeTag(zcu) != .vector) { 24568 return sema.failWithOwnedErrorMsg(block, msg: { 24569 const msg = try sema.errMsg(operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)}); 24570 errdefer msg.destroy(zcu.gpa); 24571 try sema.errNote(operand_srcs[0], msg, "vector operand here", .{}); 24572 break :msg msg; 24573 }); 24574 } 24575 if (operand_ty.vectorLen(zcu) != vec_len) { 24576 return sema.failWithOwnedErrorMsg(block, msg: { 24577 const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{f}'", .{ vec_len, operand_ty.fmt(pt) }); 24578 errdefer msg.destroy(zcu.gpa); 24579 try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len}); 24580 break :msg msg; 24581 }); 24582 } 24583 } 24584 break :vec_len vec_len; 24585 } else { 24586 for (operands[1..], operand_srcs[1..]) |operand, operand_src| { 24587 const operand_ty = sema.typeOf(operand); 24588 try sema.checkNumericType(block, operand_src, operand_ty); 24589 if (operand_ty.zigTypeTag(zcu) == .vector) { 24590 return sema.failWithOwnedErrorMsg(block, msg: { 24591 const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)}); 24592 errdefer msg.destroy(zcu.gpa); 24593 try sema.errNote(operand_src, msg, "vector operand here", .{}); 24594 break :msg msg; 24595 }); 24596 } 24597 } 24598 break :vec_len null; 24599 } 24600 }; 24601 24602 // Now we want to look at the scalar types. If any is a float, our result will be a float. This 24603 // union is in "priority" order: `float` overrides `comptime_float` overrides `int`. 24604 const TypeStrat = union(enum) { 24605 float: Type, 24606 comptime_float, 24607 int: struct { 24608 /// If this is still `true` at the end, we will just use a `comptime_int`. 24609 all_comptime_int: bool, 24610 // These two fields tells us about the *result* type, which is refined based on operand types. 24611 // e.g. `@max(u32, i64)` results in a `u63`, because the result is >=0 and <=maxInt(i64). 24612 result_min: Value, 24613 result_max: Value, 24614 // These two fields tell us the *intermediate* type to use for actually computing the min/max. 24615 // e.g. `@max(u32, i64)` uses an intermediate `i64`, because it can fit all our operands. 24616 operand_min: Value, 24617 operand_max: Value, 24618 }, 24619 none, 24620 }; 24621 var cur_strat: TypeStrat = .none; 24622 for (operands) |operand| { 24623 const operand_scalar_ty = sema.typeOf(operand).scalarType(zcu); 24624 const want_strat: TypeStrat = switch (operand_scalar_ty.zigTypeTag(zcu)) { 24625 .comptime_int => s: { 24626 const val = (try sema.resolveValueResolveLazy(operand)).?; 24627 if (val.isUndef(zcu)) break :s .none; 24628 break :s .{ .int = .{ 24629 .all_comptime_int = true, 24630 .result_min = val, 24631 .result_max = val, 24632 .operand_min = val, 24633 .operand_max = val, 24634 } }; 24635 }, 24636 .comptime_float => .comptime_float, 24637 .float => .{ .float = operand_scalar_ty }, 24638 .int => s: { 24639 // If the *value* is comptime-known, we will use that to get tighter bounds. If #3806 24640 // is accepted and implemented, so that integer literals have a tightly-bounded ranged 24641 // integer type (and `comptime_int` ceases to exist), this block should probably go away 24642 // (replaced with just the simple calls to `Type.minInt`/`Type.maxInt`) so that we only 24643 // use the input *types* to determine the result type. 24644 const min: Value, const max: Value = bounds: { 24645 if (try sema.resolveValueResolveLazy(operand)) |operand_val| { 24646 if (vector_len) |len| { 24647 var min = try operand_val.elemValue(pt, 0); 24648 var max = min; 24649 for (1..@intCast(len)) |elem_idx| { 24650 const elem_val = try operand_val.elemValue(pt, elem_idx); 24651 min = Value.numberMin(min, elem_val, zcu); 24652 max = Value.numberMax(max, elem_val, zcu); 24653 } 24654 if (!min.isUndef(zcu) and !max.isUndef(zcu)) { 24655 break :bounds .{ min, max }; 24656 } 24657 } else { 24658 if (!operand_val.isUndef(zcu)) { 24659 break :bounds .{ operand_val, operand_val }; 24660 } 24661 } 24662 } 24663 break :bounds .{ 24664 try operand_scalar_ty.minInt(pt, operand_scalar_ty), 24665 try operand_scalar_ty.maxInt(pt, operand_scalar_ty), 24666 }; 24667 }; 24668 break :s .{ .int = .{ 24669 .all_comptime_int = false, 24670 .result_min = min, 24671 .result_max = max, 24672 .operand_min = min, 24673 .operand_max = max, 24674 } }; 24675 }, 24676 else => unreachable, 24677 }; 24678 if (@intFromEnum(want_strat) < @intFromEnum(cur_strat)) { 24679 // `want_strat` overrides `cur_strat`. 24680 cur_strat = want_strat; 24681 } else if (@intFromEnum(want_strat) == @intFromEnum(cur_strat)) { 24682 // The behavior depends on the tag. 24683 switch (cur_strat) { 24684 .none, .comptime_float => {}, // no payload, so nop 24685 .float => |cur_float| { 24686 const want_float = want_strat.float; 24687 // Select the larger bit size. If the bit size is the same, select whichever is not c_longdouble. 24688 const cur_bits = cur_float.floatBits(zcu.getTarget()); 24689 const want_bits = want_float.floatBits(zcu.getTarget()); 24690 if (want_bits > cur_bits or 24691 (want_bits == cur_bits and 24692 cur_float.toIntern() == .c_longdouble_type and 24693 want_float.toIntern() != .c_longdouble_type)) 24694 { 24695 cur_strat = want_strat; 24696 } 24697 }, 24698 .int => |*cur_int| { 24699 const want_int = want_strat.int; 24700 if (!want_int.all_comptime_int) cur_int.all_comptime_int = false; 24701 cur_int.result_min = opFunc(cur_int.result_min, want_int.result_min, zcu); 24702 cur_int.result_max = opFunc(cur_int.result_max, want_int.result_max, zcu); 24703 cur_int.operand_min = Value.numberMin(cur_int.operand_min, want_int.operand_min, zcu); 24704 cur_int.operand_max = Value.numberMax(cur_int.operand_max, want_int.operand_max, zcu); 24705 }, 24706 } 24707 } 24708 } 24709 24710 // Use `cur_strat` to actually resolve the result type (and intermediate type). 24711 const result_scalar_ty: Type, const intermediate_scalar_ty: Type = switch (cur_strat) { 24712 .float => |ty| .{ ty, ty }, 24713 .comptime_float => .{ .comptime_float, .comptime_float }, 24714 .int => |int| if (int.all_comptime_int) .{ 24715 .comptime_int, 24716 .comptime_int, 24717 } else .{ 24718 try pt.intFittingRange(int.result_min, int.result_max), 24719 try pt.intFittingRange(int.operand_min, int.operand_max), 24720 }, 24721 .none => .{ .comptime_int, .comptime_int }, // all undef comptime ints 24722 }; 24723 const result_ty: Type = if (vector_len) |l| try pt.vectorType(.{ 24724 .len = @intCast(l), 24725 .child = result_scalar_ty.toIntern(), 24726 }) else result_scalar_ty; 24727 const intermediate_ty: Type = if (vector_len) |l| try pt.vectorType(.{ 24728 .len = @intCast(l), 24729 .child = intermediate_scalar_ty.toIntern(), 24730 }) else intermediate_scalar_ty; 24731 24732 // This value, if not `null`, will have type `intermediate_ty`. 24733 const comptime_part: ?Value = ct: { 24734 // Contains the comptime-known scalar result values. 24735 // Values are scalars with no particular type. 24736 // `elems.len` is `vector_len orelse 1`. 24737 const elems: []InternPool.Index = try sema.arena.alloc( 24738 InternPool.Index, 24739 try sema.usizeCast(block, src, vector_len orelse 1), 24740 ); 24741 // If `false`, we've not seen any comptime-known operand yet, so `elems` contains `undefined`. 24742 // Otherwise, `elems` is populated with the comptime-known results so far. 24743 var elems_populated = false; 24744 // Populated when we see a runtime-known operand. 24745 var opt_runtime_src: ?LazySrcLoc = null; 24746 24747 for (operands, operand_srcs) |operand, operand_src| { 24748 const operand_val = try sema.resolveValueResolveLazy(operand) orelse { 24749 if (opt_runtime_src == null) opt_runtime_src = operand_src; 24750 continue; 24751 }; 24752 if (vector_len) |len| { 24753 // Vector case; apply `opFunc` to each element. 24754 if (elems_populated) { 24755 for (elems, 0..@intCast(len)) |*elem, elem_idx| { 24756 const new_elem = try operand_val.elemValue(pt, elem_idx); 24757 elem.* = opFunc(.fromInterned(elem.*), new_elem, zcu).toIntern(); 24758 } 24759 } else { 24760 elems_populated = true; 24761 for (elems, 0..@intCast(len)) |*elem_out, elem_idx| { 24762 elem_out.* = (try operand_val.elemValue(pt, elem_idx)).toIntern(); 24763 } 24764 } 24765 } else { 24766 // Scalar case; just apply `opFunc`. 24767 if (elems_populated) { 24768 elems[0] = opFunc(.fromInterned(elems[0]), operand_val, zcu).toIntern(); 24769 } else { 24770 elems_populated = true; 24771 elems[0] = operand_val.toIntern(); 24772 } 24773 } 24774 } 24775 const runtime_src = opt_runtime_src orelse { 24776 // The result is comptime-known. Coerce each element to its scalar type. 24777 assert(elems_populated); 24778 for (elems) |*elem| { 24779 if (Value.fromInterned(elem.*).isUndef(zcu)) { 24780 elem.* = (try pt.undefValue(result_scalar_ty)).toIntern(); 24781 } else { 24782 // This coercion will always succeed, because `result_scalar_ty` can definitely hold the result. 24783 const coerced_ref = try sema.coerce(block, result_scalar_ty, Air.internedToRef(elem.*), .unneeded); 24784 elem.* = coerced_ref.toInterned().?; 24785 } 24786 } 24787 if (vector_len == null) return Air.internedToRef(elems[0]); 24788 return Air.internedToRef(try pt.intern(.{ .aggregate = .{ 24789 .ty = result_ty.toIntern(), 24790 .storage = .{ .elems = elems }, 24791 } })); 24792 }; 24793 _ = runtime_src; 24794 // The result is runtime-known. 24795 // Coerce each element to the intermediate scalar type, unless there were no comptime-known operands. 24796 if (!elems_populated) break :ct null; 24797 for (elems) |*elem| { 24798 if (Value.fromInterned(elem.*).isUndef(zcu)) { 24799 elem.* = (try pt.undefValue(intermediate_scalar_ty)).toIntern(); 24800 } else { 24801 // This coercion will always succeed, because `intermediate_scalar_ty` can definitely hold all operands. 24802 const coerced_ref = try sema.coerce(block, intermediate_scalar_ty, Air.internedToRef(elem.*), .unneeded); 24803 elem.* = coerced_ref.toInterned().?; 24804 } 24805 } 24806 break :ct .fromInterned(if (vector_len != null) try pt.intern(.{ .aggregate = .{ 24807 .ty = intermediate_ty.toIntern(), 24808 .storage = .{ .elems = elems }, 24809 } }) else elems[0]); 24810 }; 24811 24812 // Time to emit the runtime operations. All runtime-known peers are coerced to `intermediate_ty`, and we cast down to `result_ty` at the end. 24813 24814 // `.none` indicates no result so far. 24815 var cur_result: Air.Inst.Ref = if (comptime_part) |val| Air.internedToRef(val.toIntern()) else .none; 24816 for (operands, operand_srcs) |operand, operand_src| { 24817 if (try sema.isComptimeKnown(operand)) continue; // already in `comptime_part` 24818 // This coercion could fail; e.g. coercing a runtime integer peer to a `comptime_float` in a case like `@min(runtime_int, 1.5)`. 24819 const operand_coerced = try sema.coerce(block, intermediate_ty, operand, operand_src); 24820 if (cur_result == .none) { 24821 cur_result = operand_coerced; 24822 } else { 24823 cur_result = try block.addBinOp(air_tag, cur_result, operand_coerced); 24824 } 24825 } 24826 24827 assert(cur_result != .none); 24828 assert(sema.typeOf(cur_result).toIntern() == intermediate_ty.toIntern()); 24829 24830 // If there is a comptime-known undef operand, we actually return comptime-known undef -- but we had to do the runtime stuff to check for coercion errors. 24831 if (comptime_part) |val| { 24832 if (val.isUndefDeep(zcu)) { 24833 return pt.undefRef(result_ty); 24834 } 24835 } 24836 24837 if (result_ty.toIntern() == intermediate_ty.toIntern()) { 24838 // No final cast needed; we're all done. 24839 return cur_result; 24840 } 24841 24842 // A final cast is needed. The only case where `intermediate_ty` is different is for integers, 24843 // where we have refined the range, so we should be doing an intcast. 24844 assert(intermediate_scalar_ty.zigTypeTag(zcu) == .int); 24845 assert(result_scalar_ty.zigTypeTag(zcu) == .int); 24846 return block.addTyOp(.intcast, result_ty, cur_result); 24847 } 24848 24849 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { 24850 const pt = sema.pt; 24851 const zcu = pt.zcu; 24852 const ptr_ty = sema.typeOf(ptr); 24853 const info = ptr_ty.ptrInfo(zcu); 24854 if (info.flags.size == .one) { 24855 // Already an array pointer. 24856 return ptr; 24857 } 24858 const new_ty = try pt.ptrTypeSema(.{ 24859 .child = (try pt.arrayType(.{ 24860 .len = len, 24861 .sentinel = info.sentinel, 24862 .child = info.child, 24863 })).toIntern(), 24864 .flags = .{ 24865 .alignment = info.flags.alignment, 24866 .is_const = info.flags.is_const, 24867 .is_volatile = info.flags.is_volatile, 24868 .is_allowzero = info.flags.is_allowzero, 24869 .address_space = info.flags.address_space, 24870 }, 24871 }); 24872 const non_slice_ptr = if (info.flags.size == .slice) 24873 try block.addTyOp(.slice_ptr, ptr_ty.slicePtrFieldType(zcu), ptr) 24874 else 24875 ptr; 24876 return block.addBitCast(new_ty, non_slice_ptr); 24877 } 24878 24879 fn zirMemcpy( 24880 sema: *Sema, 24881 block: *Block, 24882 inst: Zir.Inst.Index, 24883 air_tag: Air.Inst.Tag, 24884 check_aliasing: bool, 24885 ) CompileError!void { 24886 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24887 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 24888 const src = block.nodeOffset(inst_data.src_node); 24889 const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0); 24890 const src_src = block.builtinCallArgSrc(inst_data.src_node, 1); 24891 const dest_ptr = try sema.resolveInst(extra.lhs); 24892 const src_ptr = try sema.resolveInst(extra.rhs); 24893 const dest_ty = sema.typeOf(dest_ptr); 24894 const src_ty = sema.typeOf(src_ptr); 24895 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); 24896 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); 24897 const pt = sema.pt; 24898 const zcu = pt.zcu; 24899 24900 if (dest_ty.isConstPtr(zcu)) { 24901 return sema.fail(block, dest_src, "cannot copy to constant pointer", .{}); 24902 } 24903 24904 if (dest_len == .none and src_len == .none) { 24905 const msg = msg: { 24906 const msg = try sema.errMsg(src, "unknown copy length", .{}); 24907 errdefer msg.destroy(sema.gpa); 24908 try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{ 24909 dest_ty.fmt(pt), 24910 }); 24911 try sema.errNote(src_src, msg, "source type '{f}' provides no length", .{ 24912 src_ty.fmt(pt), 24913 }); 24914 break :msg msg; 24915 }; 24916 return sema.failWithOwnedErrorMsg(block, msg); 24917 } 24918 24919 const dest_elem_ty = dest_ty.indexablePtrElem(zcu); 24920 const src_elem_ty = src_ty.indexablePtrElem(zcu); 24921 24922 const imc = try sema.coerceInMemoryAllowed( 24923 block, 24924 dest_elem_ty, 24925 src_elem_ty, 24926 false, 24927 zcu.getTarget(), 24928 dest_src, 24929 src_src, 24930 null, 24931 ); 24932 if (imc != .ok) return sema.failWithOwnedErrorMsg(block, msg: { 24933 const msg = try sema.errMsg( 24934 src, 24935 "pointer element type '{f}' cannot coerce into element type '{f}'", 24936 .{ src_elem_ty.fmt(pt), dest_elem_ty.fmt(pt) }, 24937 ); 24938 errdefer msg.destroy(sema.gpa); 24939 try imc.report(sema, src, msg); 24940 break :msg msg; 24941 }); 24942 24943 var len_val: ?Value = null; 24944 24945 if (dest_len != .none and src_len != .none) check: { 24946 // If we can check at compile-time, no need for runtime safety. 24947 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 24948 len_val = dest_len_val; 24949 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 24950 if (!(try sema.valuesEqual(dest_len_val, src_len_val, .usize))) { 24951 const msg = msg: { 24952 const msg = try sema.errMsg(src, "non-matching copy lengths", .{}); 24953 errdefer msg.destroy(sema.gpa); 24954 try sema.errNote(dest_src, msg, "length {f} here", .{ 24955 dest_len_val.fmtValueSema(pt, sema), 24956 }); 24957 try sema.errNote(src_src, msg, "length {f} here", .{ 24958 src_len_val.fmtValueSema(pt, sema), 24959 }); 24960 break :msg msg; 24961 }; 24962 return sema.failWithOwnedErrorMsg(block, msg); 24963 } 24964 break :check; 24965 } 24966 } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 24967 len_val = src_len_val; 24968 } 24969 24970 if (block.wantSafety()) { 24971 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); 24972 try sema.addSafetyCheck(block, src, ok, .copy_len_mismatch); 24973 } 24974 } else if (dest_len != .none) { 24975 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 24976 len_val = dest_len_val; 24977 } 24978 } else if (src_len != .none) { 24979 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 24980 len_val = src_len_val; 24981 } 24982 } 24983 24984 zero_bit: { 24985 const src_comptime = try src_elem_ty.comptimeOnlySema(pt); 24986 const dest_comptime = try dest_elem_ty.comptimeOnlySema(pt); 24987 assert(src_comptime == dest_comptime); // IMC 24988 if (src_comptime) break :zero_bit; 24989 24990 const src_has_bits = try src_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); 24991 const dest_has_bits = try dest_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt); 24992 assert(src_has_bits == dest_has_bits); // IMC 24993 if (src_has_bits) break :zero_bit; 24994 24995 // The element type is zero-bit. We've done all validation (aside from the aliasing check, 24996 // which we must skip) so we're done. 24997 return; 24998 } 24999 25000 const runtime_src = rs: { 25001 const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; 25002 const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src; 25003 25004 const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val; 25005 const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val; 25006 25007 const len_u64 = try len_val.?.toUnsignedIntSema(pt); 25008 25009 if (check_aliasing) { 25010 if (Value.doPointersOverlap( 25011 raw_src_ptr, 25012 raw_dest_ptr, 25013 len_u64, 25014 zcu, 25015 )) return sema.fail(block, src, "'@memcpy' arguments alias", .{}); 25016 } 25017 25018 if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src; 25019 25020 // Because comptime pointer access is a somewhat expensive operation, we implement @memcpy 25021 // as one load and store of an array, rather than N loads and stores of individual elements. 25022 25023 const array_ty = try pt.arrayType(.{ 25024 .child = dest_elem_ty.toIntern(), 25025 .len = len_u64, 25026 }); 25027 25028 const dest_array_ptr_ty = try pt.ptrType(info: { 25029 var info = dest_ty.ptrInfo(zcu); 25030 info.flags.size = .one; 25031 info.child = array_ty.toIntern(); 25032 info.sentinel = .none; 25033 break :info info; 25034 }); 25035 const src_array_ptr_ty = try pt.ptrType(info: { 25036 var info = src_ty.ptrInfo(zcu); 25037 info.flags.size = .one; 25038 info.child = array_ty.toIntern(); 25039 info.sentinel = .none; 25040 break :info info; 25041 }); 25042 25043 const coerced_dest_ptr = try pt.getCoerced(raw_dest_ptr, dest_array_ptr_ty); 25044 const coerced_src_ptr = try pt.getCoerced(raw_src_ptr, src_array_ptr_ty); 25045 25046 const array_val = try sema.pointerDeref(block, src_src, coerced_src_ptr, src_array_ptr_ty) orelse break :rs src_src; 25047 try sema.storePtrVal(block, dest_src, coerced_dest_ptr, array_val, array_ty); 25048 return; 25049 }; 25050 25051 // If the length is comptime-known, then upgrade src and destination types 25052 // into pointer-to-array. At this point we know they are both pointers 25053 // already. 25054 var new_dest_ptr = dest_ptr; 25055 var new_src_ptr = src_ptr; 25056 if (len_val) |val| { 25057 const len = try val.toUnsignedIntSema(pt); 25058 if (len == 0) { 25059 // This AIR instruction guarantees length > 0 if it is comptime-known. 25060 return; 25061 } 25062 new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len); 25063 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len); 25064 } 25065 25066 if (dest_len != .none) { 25067 // Change the src from slice to a many pointer, to avoid multiple ptr 25068 // slice extractions in AIR instructions. 25069 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 25070 if (new_src_ptr_ty.isSlice(zcu)) { 25071 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 25072 } 25073 } else if (dest_len == .none and len_val == null) { 25074 // Change the dest to a slice, since its type must have the length. 25075 const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr); 25076 new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, LazySrcLoc.unneeded, dest_src, dest_src, dest_src, false); 25077 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 25078 if (new_src_ptr_ty.isSlice(zcu)) { 25079 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 25080 } 25081 } 25082 25083 try sema.requireRuntimeBlock(block, src, runtime_src); 25084 try sema.validateRuntimeValue(block, dest_src, dest_ptr); 25085 try sema.validateRuntimeValue(block, src_src, src_ptr); 25086 25087 // Aliasing safety check. 25088 if (check_aliasing and block.wantSafety()) { 25089 const len = if (len_val) |v| 25090 Air.internedToRef(v.toIntern()) 25091 else if (dest_len != .none) 25092 dest_len 25093 else 25094 src_len; 25095 25096 // Extract raw pointer from dest slice. The AIR instructions could support them, but 25097 // it would cause redundant machine code instructions. 25098 const new_dest_ptr_ty = sema.typeOf(new_dest_ptr); 25099 const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(zcu)) 25100 try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty) 25101 else if (new_dest_ptr_ty.ptrSize(zcu) == .one) ptr: { 25102 var dest_manyptr_ty_key = zcu.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type; 25103 assert(dest_manyptr_ty_key.flags.size == .one); 25104 dest_manyptr_ty_key.child = dest_elem_ty.toIntern(); 25105 dest_manyptr_ty_key.flags.size = .many; 25106 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(dest_manyptr_ty_key), new_dest_ptr, dest_src); 25107 } else new_dest_ptr; 25108 25109 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 25110 const raw_src_ptr = if (new_src_ptr_ty.isSlice(zcu)) 25111 try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty) 25112 else if (new_src_ptr_ty.ptrSize(zcu) == .one) ptr: { 25113 var src_manyptr_ty_key = zcu.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type; 25114 assert(src_manyptr_ty_key.flags.size == .one); 25115 src_manyptr_ty_key.child = src_elem_ty.toIntern(); 25116 src_manyptr_ty_key.flags.size = .many; 25117 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(src_manyptr_ty_key), new_src_ptr, src_src); 25118 } else new_src_ptr; 25119 25120 // ok1: dest >= src + len 25121 // ok2: src >= dest + len 25122 const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src); 25123 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src); 25124 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len); 25125 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len); 25126 const ok = try block.addBinOp(.bool_or, ok1, ok2); 25127 try sema.addSafetyCheck(block, src, ok, .memcpy_alias); 25128 } 25129 25130 _ = try block.addInst(.{ 25131 .tag = air_tag, 25132 .data = .{ .bin_op = .{ 25133 .lhs = new_dest_ptr, 25134 .rhs = new_src_ptr, 25135 } }, 25136 }); 25137 } 25138 25139 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 25140 const pt = sema.pt; 25141 const zcu = pt.zcu; 25142 const gpa = sema.gpa; 25143 const ip = &zcu.intern_pool; 25144 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 25145 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 25146 const src = block.nodeOffset(inst_data.src_node); 25147 const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0); 25148 const value_src = block.builtinCallArgSrc(inst_data.src_node, 1); 25149 const dest_ptr = try sema.resolveInst(extra.lhs); 25150 const uncoerced_elem = try sema.resolveInst(extra.rhs); 25151 const dest_ptr_ty = sema.typeOf(dest_ptr); 25152 try checkMemOperand(sema, block, dest_src, dest_ptr_ty); 25153 25154 if (dest_ptr_ty.isConstPtr(zcu)) { 25155 return sema.fail(block, dest_src, "cannot memset constant pointer", .{}); 25156 } 25157 25158 const dest_elem_ty: Type = dest_elem_ty: { 25159 const ptr_info = dest_ptr_ty.ptrInfo(zcu); 25160 switch (ptr_info.flags.size) { 25161 .slice => break :dest_elem_ty .fromInterned(ptr_info.child), 25162 .one => { 25163 if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { 25164 break :dest_elem_ty Type.fromInterned(ptr_info.child).childType(zcu); 25165 } 25166 }, 25167 .many, .c => {}, 25168 } 25169 return sema.failWithOwnedErrorMsg(block, msg: { 25170 const msg = try sema.errMsg(src, "unknown @memset length", .{}); 25171 errdefer msg.destroy(sema.gpa); 25172 try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{ 25173 dest_ptr_ty.fmt(pt), 25174 }); 25175 break :msg msg; 25176 }); 25177 }; 25178 25179 const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); 25180 25181 const runtime_src = rs: { 25182 const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; 25183 const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), dest_src); 25184 const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src; 25185 const len_u64 = try len_val.toUnsignedIntSema(pt); 25186 const len = try sema.usizeCast(block, dest_src, len_u64); 25187 if (len == 0) { 25188 // This AIR instruction guarantees length > 0 if it is comptime-known. 25189 return; 25190 } 25191 25192 if (!sema.isComptimeMutablePtr(ptr_val)) break :rs dest_src; 25193 const elem_val = try sema.resolveValue(elem) orelse break :rs value_src; 25194 const array_ty = try pt.arrayType(.{ 25195 .child = dest_elem_ty.toIntern(), 25196 .len = len_u64, 25197 }); 25198 const array_val = Value.fromInterned(try pt.intern(.{ .aggregate = .{ 25199 .ty = array_ty.toIntern(), 25200 .storage = .{ .repeated_elem = elem_val.toIntern() }, 25201 } })); 25202 const array_ptr_ty = ty: { 25203 var info = dest_ptr_ty.ptrInfo(zcu); 25204 info.flags.size = .one; 25205 info.child = array_ty.toIntern(); 25206 break :ty try pt.ptrType(info); 25207 }; 25208 const raw_ptr_val = if (dest_ptr_ty.isSlice(zcu)) ptr_val.slicePtr(zcu) else ptr_val; 25209 const array_ptr_val = try pt.getCoerced(raw_ptr_val, array_ptr_ty); 25210 return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty); 25211 }; 25212 25213 try sema.requireRuntimeBlock(block, src, runtime_src); 25214 try sema.validateRuntimeValue(block, dest_src, dest_ptr); 25215 try sema.validateRuntimeValue(block, value_src, elem); 25216 25217 _ = try block.addInst(.{ 25218 .tag = if (block.wantSafety()) .memset_safe else .memset, 25219 .data = .{ .bin_op = .{ 25220 .lhs = dest_ptr, 25221 .rhs = elem, 25222 } }, 25223 }); 25224 } 25225 25226 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 25227 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 25228 const src = block.nodeOffset(inst_data.src_node); 25229 return sema.failWithUseOfAsync(block, src); 25230 } 25231 25232 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 25233 const tracy = trace(@src()); 25234 defer tracy.end(); 25235 25236 const pt = sema.pt; 25237 const zcu = pt.zcu; 25238 const ip = &zcu.intern_pool; 25239 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 25240 const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); 25241 const target = zcu.getTarget(); 25242 25243 const cc_src = block.src(.{ .node_offset_fn_type_cc = inst_data.src_node }); 25244 const ret_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node }); 25245 const has_body = extra.data.body_len != 0; 25246 25247 var extra_index: usize = extra.end; 25248 25249 const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: { 25250 const body_len = sema.code.extra[extra_index]; 25251 extra_index += 1; 25252 const body = sema.code.bodySlice(extra_index, body_len); 25253 extra_index += body.len; 25254 25255 const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention); 25256 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{ .simple = .@"callconv" }); 25257 break :blk try sema.analyzeValueAsCallconv(block, cc_src, val); 25258 } else if (extra.data.bits.has_cc_ref) blk: { 25259 const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 25260 extra_index += 1; 25261 const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention); 25262 const uncoerced_cc = try sema.resolveInst(cc_ref); 25263 const coerced_cc = try sema.coerce(block, cc_ty, uncoerced_cc, cc_src); 25264 const cc_val = try sema.resolveConstDefinedValue(block, cc_src, coerced_cc, .{ .simple = .@"callconv" }); 25265 break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val); 25266 } else cc: { 25267 if (has_body) { 25268 const func_decl_nav = sema.owner.unwrap().nav_val; 25269 const func_decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(&zcu.intern_pool) orelse return error.AnalysisFail; 25270 const zir_decl = sema.code.getDeclaration(func_decl_inst); 25271 if (zir_decl.linkage == .@"export") { 25272 break :cc target.cCallingConvention() orelse { 25273 // This target has no default C calling convention. We sometimes trigger a similar 25274 // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency, 25275 // let's eval that now and just get the transitive error. (It's guaranteed to error 25276 // because it does the exact `cCallingConvention` call we just did.) 25277 const cc_type = try sema.getBuiltinType(cc_src, .CallingConvention); 25278 _ = try sema.namespaceLookupVal( 25279 block, 25280 LazySrcLoc.unneeded, 25281 cc_type.getNamespaceIndex(zcu), 25282 try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls), 25283 ); 25284 // The above should have errored. 25285 @panic("std.builtin is corrupt"); 25286 }; 25287 } 25288 } 25289 break :cc .auto; 25290 }; 25291 25292 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { 25293 const body_len = sema.code.extra[extra_index]; 25294 extra_index += 1; 25295 const body = sema.code.bodySlice(extra_index, body_len); 25296 extra_index += body.len; 25297 if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison; 25298 25299 const val = try sema.resolveGenericBody(block, ret_src, body, inst, .type, .{ .simple = .function_ret_ty }); 25300 const ty = val.toType(); 25301 break :blk ty; 25302 } else if (extra.data.bits.has_ret_ty_ref) blk: { 25303 const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 25304 extra_index += 1; 25305 if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison; 25306 25307 const ret_ty_air_ref = try sema.resolveInst(ret_ty_ref); 25308 const ret_ty_val = try sema.resolveConstDefinedValue(block, ret_src, ret_ty_air_ref, .{ .simple = .function_ret_ty }); 25309 break :blk ret_ty_val.toType(); 25310 } else .void; 25311 25312 const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { 25313 const x = sema.code.extra[extra_index]; 25314 extra_index += 1; 25315 break :blk x; 25316 } else 0; 25317 25318 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 25319 if (has_body) { 25320 extra_index += extra.data.body_len; 25321 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 25322 } 25323 25324 const is_var_args = extra.data.bits.is_var_args; 25325 const is_inferred_error = extra.data.bits.is_inferred_error; 25326 const is_noinline = extra.data.bits.is_noinline; 25327 25328 return sema.funcCommon( 25329 block, 25330 inst_data.src_node, 25331 inst, 25332 cc, 25333 ret_ty, 25334 is_var_args, 25335 is_inferred_error, 25336 has_body, 25337 src_locs, 25338 noalias_bits, 25339 is_noinline, 25340 ); 25341 } 25342 25343 fn zirCUndef( 25344 sema: *Sema, 25345 block: *Block, 25346 extended: Zir.Inst.Extended.InstData, 25347 ) CompileError!Air.Inst.Ref { 25348 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25349 const src = block.builtinCallArgSrc(extra.node, 0); 25350 25351 const name = try sema.resolveConstString(block, src, extra.operand, .{ .simple = .operand_cUndef_macro_name }); 25352 try block.c_import_buf.?.print("#undef {s}\n", .{name}); 25353 return .void_value; 25354 } 25355 25356 fn zirCInclude( 25357 sema: *Sema, 25358 block: *Block, 25359 extended: Zir.Inst.Extended.InstData, 25360 ) CompileError!Air.Inst.Ref { 25361 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25362 const src = block.builtinCallArgSrc(extra.node, 0); 25363 25364 const name = try sema.resolveConstString(block, src, extra.operand, .{ .simple = .operand_cInclude_file_name }); 25365 try block.c_import_buf.?.print("#include <{s}>\n", .{name}); 25366 return .void_value; 25367 } 25368 25369 fn zirCDefine( 25370 sema: *Sema, 25371 block: *Block, 25372 extended: Zir.Inst.Extended.InstData, 25373 ) CompileError!Air.Inst.Ref { 25374 const pt = sema.pt; 25375 const zcu = pt.zcu; 25376 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25377 const name_src = block.builtinCallArgSrc(extra.node, 0); 25378 const val_src = block.builtinCallArgSrc(extra.node, 1); 25379 25380 const name = try sema.resolveConstString(block, name_src, extra.lhs, .{ .simple = .operand_cDefine_macro_name }); 25381 const rhs = try sema.resolveInst(extra.rhs); 25382 if (sema.typeOf(rhs).zigTypeTag(zcu) != .void) { 25383 const value = try sema.resolveConstString(block, val_src, extra.rhs, .{ .simple = .operand_cDefine_macro_value }); 25384 try block.c_import_buf.?.print("#define {s} {s}\n", .{ name, value }); 25385 } else { 25386 try block.c_import_buf.?.print("#define {s}\n", .{name}); 25387 } 25388 return .void_value; 25389 } 25390 25391 fn zirWasmMemorySize( 25392 sema: *Sema, 25393 block: *Block, 25394 extended: Zir.Inst.Extended.InstData, 25395 ) CompileError!Air.Inst.Ref { 25396 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25397 const index_src = block.builtinCallArgSrc(extra.node, 0); 25398 const builtin_src = block.nodeOffset(extra.node); 25399 const target = sema.pt.zcu.getTarget(); 25400 if (!target.cpu.arch.isWasm()) { 25401 return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 25402 } 25403 25404 const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.operand, .u32, .{ .simple = .wasm_memory_index })); 25405 try sema.requireRuntimeBlock(block, builtin_src, null); 25406 return block.addInst(.{ 25407 .tag = .wasm_memory_size, 25408 .data = .{ .pl_op = .{ 25409 .operand = .none, 25410 .payload = index, 25411 } }, 25412 }); 25413 } 25414 25415 fn zirWasmMemoryGrow( 25416 sema: *Sema, 25417 block: *Block, 25418 extended: Zir.Inst.Extended.InstData, 25419 ) CompileError!Air.Inst.Ref { 25420 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25421 const builtin_src = block.nodeOffset(extra.node); 25422 const index_src = block.builtinCallArgSrc(extra.node, 0); 25423 const delta_src = block.builtinCallArgSrc(extra.node, 1); 25424 const target = sema.pt.zcu.getTarget(); 25425 if (!target.cpu.arch.isWasm()) { 25426 return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 25427 } 25428 25429 const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, .u32, .{ .simple = .wasm_memory_index })); 25430 const delta = try sema.coerce(block, .usize, try sema.resolveInst(extra.rhs), delta_src); 25431 25432 try sema.requireRuntimeBlock(block, builtin_src, null); 25433 return block.addInst(.{ 25434 .tag = .wasm_memory_grow, 25435 .data = .{ .pl_op = .{ 25436 .operand = delta, 25437 .payload = index, 25438 } }, 25439 }); 25440 } 25441 25442 fn resolvePrefetchOptions( 25443 sema: *Sema, 25444 block: *Block, 25445 src: LazySrcLoc, 25446 zir_ref: Zir.Inst.Ref, 25447 ) CompileError!std.builtin.PrefetchOptions { 25448 const pt = sema.pt; 25449 const zcu = pt.zcu; 25450 const gpa = sema.gpa; 25451 const ip = &zcu.intern_pool; 25452 const options_ty = try sema.getBuiltinType(src, .PrefetchOptions); 25453 const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src); 25454 25455 const rw_src = block.src(.{ .init_field_rw = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25456 const locality_src = block.src(.{ .init_field_locality = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25457 const cache_src = block.src(.{ .init_field_cache = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25458 25459 const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "rw", .no_embedded_nulls), rw_src); 25460 const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ .simple = .prefetch_options }); 25461 25462 const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "locality", .no_embedded_nulls), locality_src); 25463 const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ .simple = .prefetch_options }); 25464 25465 const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "cache", .no_embedded_nulls), cache_src); 25466 const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ .simple = .prefetch_options }); 25467 25468 return std.builtin.PrefetchOptions{ 25469 .rw = try sema.interpretBuiltinType(block, rw_src, rw_val, std.builtin.PrefetchOptions.Rw), 25470 .locality = @intCast(try locality_val.toUnsignedIntSema(pt)), 25471 .cache = try sema.interpretBuiltinType(block, cache_src, cache_val, std.builtin.PrefetchOptions.Cache), 25472 }; 25473 } 25474 25475 fn zirPrefetch( 25476 sema: *Sema, 25477 block: *Block, 25478 extended: Zir.Inst.Extended.InstData, 25479 ) CompileError!Air.Inst.Ref { 25480 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25481 const ptr_src = block.builtinCallArgSrc(extra.node, 0); 25482 const opts_src = block.builtinCallArgSrc(extra.node, 1); 25483 const ptr = try sema.resolveInst(extra.lhs); 25484 try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); 25485 25486 const options = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs); 25487 25488 if (!block.isComptime()) { 25489 _ = try block.addInst(.{ 25490 .tag = .prefetch, 25491 .data = .{ .prefetch = .{ 25492 .ptr = ptr, 25493 .rw = options.rw, 25494 .locality = options.locality, 25495 .cache = options.cache, 25496 } }, 25497 }); 25498 } 25499 25500 return .void_value; 25501 } 25502 25503 fn resolveExternOptions( 25504 sema: *Sema, 25505 block: *Block, 25506 src: LazySrcLoc, 25507 zir_ref: Zir.Inst.Ref, 25508 ) CompileError!struct { 25509 name: InternPool.NullTerminatedString, 25510 library_name: InternPool.OptionalNullTerminatedString, 25511 linkage: std.builtin.GlobalLinkage, 25512 visibility: std.builtin.SymbolVisibility, 25513 is_thread_local: bool, 25514 is_dll_import: bool, 25515 relocation: std.builtin.ExternOptions.Relocation, 25516 } { 25517 const pt = sema.pt; 25518 const zcu = pt.zcu; 25519 const gpa = sema.gpa; 25520 const ip = &zcu.intern_pool; 25521 const options_inst = try sema.resolveInst(zir_ref); 25522 const extern_options_ty = try sema.getBuiltinType(src, .ExternOptions); 25523 const options = try sema.coerce(block, extern_options_ty, options_inst, src); 25524 25525 const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25526 const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25527 const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25528 const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25529 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25530 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25531 const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node }); 25532 25533 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src); 25534 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options }); 25535 25536 const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "library_name", .no_embedded_nulls), library_src); 25537 const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ .simple = .extern_options }); 25538 25539 const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src); 25540 const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options }); 25541 const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage); 25542 25543 const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src); 25544 const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options }); 25545 const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility); 25546 25547 const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src); 25548 const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options }); 25549 25550 const library_name = if (library_name_val.optionalValue(zcu)) |library_name_payload| library_name: { 25551 const library_name = try sema.toConstString(block, library_src, Air.internedToRef(library_name_payload.toIntern()), .{ .simple = .extern_options }); 25552 if (library_name.len == 0) { 25553 return sema.fail(block, library_src, "library name cannot be empty", .{}); 25554 } 25555 try sema.handleExternLibName(block, library_src, library_name); 25556 break :library_name library_name; 25557 } else null; 25558 25559 const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src); 25560 const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options }); 25561 25562 const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "relocation", .no_embedded_nulls), relocation_src); 25563 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options }); 25564 const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation); 25565 25566 if (name.len == 0) { 25567 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{}); 25568 } 25569 25570 if (linkage != .weak and linkage != .strong) { 25571 return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{}); 25572 } 25573 25574 return .{ 25575 .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls), 25576 .library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls), 25577 .linkage = linkage, 25578 .visibility = visibility, 25579 .is_thread_local = is_thread_local_val.toBool(), 25580 .is_dll_import = is_dll_import_val.toBool(), 25581 .relocation = relocation, 25582 }; 25583 } 25584 25585 fn zirBuiltinExtern( 25586 sema: *Sema, 25587 block: *Block, 25588 extended: Zir.Inst.Extended.InstData, 25589 ) CompileError!Air.Inst.Ref { 25590 const pt = sema.pt; 25591 const zcu = pt.zcu; 25592 const ip = &zcu.intern_pool; 25593 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25594 const src = block.nodeOffset(extra.node); 25595 const ty_src = block.builtinCallArgSrc(extra.node, 0); 25596 const options_src = block.builtinCallArgSrc(extra.node, 1); 25597 25598 var ty = try sema.resolveType(block, ty_src, extra.lhs); 25599 if (!ty.isPtrAtRuntime(zcu)) { 25600 return sema.fail(block, ty_src, "expected (optional) pointer", .{}); 25601 } 25602 if (!try sema.validateExternType(ty, .other)) { 25603 const msg = msg: { 25604 const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ty.fmt(pt)}); 25605 errdefer msg.destroy(sema.gpa); 25606 try sema.explainWhyTypeIsNotExtern(msg, ty_src, ty, .other); 25607 break :msg msg; 25608 }; 25609 return sema.failWithOwnedErrorMsg(block, msg); 25610 } 25611 25612 const options = try sema.resolveExternOptions(block, options_src, extra.rhs); 25613 switch (options.linkage) { 25614 .internal => if (options.visibility != .default) { 25615 return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{}); 25616 }, 25617 .strong, .weak => {}, 25618 .link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}), 25619 } 25620 switch (options.relocation) { 25621 .any => {}, 25622 .pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}), 25623 } 25624 25625 // TODO: error for threadlocal functions, non-const functions, etc 25626 25627 if (options.linkage == .weak and !ty.ptrAllowsZero(zcu)) { 25628 ty = try pt.optionalType(ty.toIntern()); 25629 } 25630 const ptr_info = ty.ptrInfo(zcu); 25631 25632 const extern_val = try pt.getExtern(.{ 25633 .name = options.name, 25634 .ty = ptr_info.child, 25635 .lib_name = options.library_name, 25636 .linkage = options.linkage, 25637 .visibility = options.visibility, 25638 .is_threadlocal = options.is_thread_local, 25639 .is_dll_import = options.is_dll_import, 25640 .relocation = options.relocation, 25641 .is_const = ptr_info.flags.is_const, 25642 .alignment = ptr_info.flags.alignment, 25643 .@"addrspace" = ptr_info.flags.address_space, 25644 // This instruction is just for source locations. 25645 // `builtin_extern` doesn't provide enough information, and isn't currently tracked. 25646 // So, for now, just use our containing `declaration`. 25647 .zir_index = switch (sema.owner.unwrap()) { 25648 .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index, 25649 .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?, 25650 .memoized_state => unreachable, 25651 .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index, 25652 .func => |func| zir_index: { 25653 const func_info = zcu.funcInfo(func); 25654 const owner_func_info = if (func_info.generic_owner != .none) owner: { 25655 break :owner zcu.funcInfo(func_info.generic_owner); 25656 } else func_info; 25657 break :zir_index ip.getNav(owner_func_info.owner_nav).analysis.?.zir_index; 25658 }, 25659 }, 25660 .owner_nav = undefined, // ignored by `getExtern` 25661 }); 25662 25663 const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav); 25664 // We want to cast to `ty`, but that isn't necessarily an allowed coercion. 25665 if (try sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| { 25666 const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, ty); 25667 return Air.internedToRef(casted_ptr_val.toIntern()); 25668 } else { 25669 return block.addBitCast(ty, uncasted_ptr); 25670 } 25671 } 25672 25673 fn zirWorkItem( 25674 sema: *Sema, 25675 block: *Block, 25676 extended: Zir.Inst.Extended.InstData, 25677 zir_tag: Zir.Inst.Extended, 25678 ) CompileError!Air.Inst.Ref { 25679 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25680 const dimension_src = block.builtinCallArgSrc(extra.node, 0); 25681 const builtin_src = block.nodeOffset(extra.node); 25682 const target = sema.pt.zcu.getTarget(); 25683 25684 switch (target.cpu.arch) { 25685 // TODO: Allow for other GPU targets. 25686 .amdgcn, .spirv64, .spirv32, .nvptx, .nvptx64 => {}, 25687 else => { 25688 return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)}); 25689 }, 25690 } 25691 25692 const dimension: u32 = @intCast(try sema.resolveInt(block, dimension_src, extra.operand, .u32, .{ .simple = .work_group_dim_index })); 25693 try sema.requireRuntimeBlock(block, builtin_src, null); 25694 25695 return block.addInst(.{ 25696 .tag = switch (zir_tag) { 25697 .work_item_id => .work_item_id, 25698 .work_group_size => .work_group_size, 25699 .work_group_id => .work_group_id, 25700 else => unreachable, 25701 }, 25702 .data = .{ .pl_op = .{ 25703 .operand = .none, 25704 .payload = dimension, 25705 } }, 25706 }); 25707 } 25708 25709 fn zirInComptime( 25710 sema: *Sema, 25711 block: *Block, 25712 ) CompileError!Air.Inst.Ref { 25713 _ = sema; 25714 return if (block.isComptime()) .bool_true else .bool_false; 25715 } 25716 25717 fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 25718 const pt = sema.pt; 25719 const zcu = pt.zcu; 25720 const gpa = zcu.gpa; 25721 const ip = &zcu.intern_pool; 25722 25723 const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand))); 25724 const src = block.nodeOffset(src_node); 25725 const value: Zir.Inst.BuiltinValue = @enumFromInt(extended.small); 25726 25727 const ty = switch (value) { 25728 // zig fmt: off 25729 .atomic_order => try sema.getBuiltinType(src, .AtomicOrder), 25730 .atomic_rmw_op => try sema.getBuiltinType(src, .AtomicRmwOp), 25731 .calling_convention => try sema.getBuiltinType(src, .CallingConvention), 25732 .address_space => try sema.getBuiltinType(src, .AddressSpace), 25733 .float_mode => try sema.getBuiltinType(src, .FloatMode), 25734 .reduce_op => try sema.getBuiltinType(src, .ReduceOp), 25735 .call_modifier => try sema.getBuiltinType(src, .CallModifier), 25736 .prefetch_options => try sema.getBuiltinType(src, .PrefetchOptions), 25737 .export_options => try sema.getBuiltinType(src, .ExportOptions), 25738 .extern_options => try sema.getBuiltinType(src, .ExternOptions), 25739 .type_info => try sema.getBuiltinType(src, .Type), 25740 .branch_hint => try sema.getBuiltinType(src, .BranchHint), 25741 .clobbers => try sema.getBuiltinType(src, .@"assembly.Clobbers"), 25742 // zig fmt: on 25743 25744 // Values are handled here. 25745 .calling_convention_c => { 25746 const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); 25747 return try sema.namespaceLookupVal( 25748 block, 25749 src, 25750 callconv_ty.getNamespaceIndex(zcu), 25751 try ip.getOrPutString(gpa, pt.tid, "c", .no_embedded_nulls), 25752 ) orelse @panic("std.builtin is corrupt"); 25753 }, 25754 .calling_convention_inline => { 25755 comptime assert(@typeInfo(std.builtin.CallingConvention.Tag).@"enum".tag_type == u8); 25756 const callconv_ty = try sema.getBuiltinType(src, .CallingConvention); 25757 const callconv_tag_ty = callconv_ty.unionTagType(zcu) orelse @panic("std.builtin is corrupt"); 25758 const inline_tag_val = try pt.enumValue( 25759 callconv_tag_ty, 25760 (try pt.intValue( 25761 .u8, 25762 @intFromEnum(std.builtin.CallingConvention.@"inline"), 25763 )).toIntern(), 25764 ); 25765 return sema.coerce(block, callconv_ty, Air.internedToRef(inline_tag_val.toIntern()), src); 25766 }, 25767 }; 25768 return Air.internedToRef(ty.toIntern()); 25769 } 25770 25771 fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 25772 const pt = sema.pt; 25773 const zcu = pt.zcu; 25774 25775 const lhs = try sema.resolveInst(@enumFromInt(extended.operand)); 25776 const lhs_ty = sema.typeOf(lhs); 25777 25778 const op: Zir.Inst.InplaceOp = @enumFromInt(extended.small); 25779 const ty: Type = switch (op) { 25780 .add_eq => ty: { 25781 const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty; 25782 switch (ptr_size) { 25783 .one, .slice => break :ty lhs_ty, // invalid, let it error 25784 .many, .c => break :ty .usize, // `[*]T + usize` 25785 } 25786 }, 25787 .sub_eq => ty: { 25788 const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty; 25789 switch (ptr_size) { 25790 .one, .slice => break :ty lhs_ty, // invalid, let it error 25791 .many, .c => break :ty .generic_poison, // could be `[*]T - [*]T` or `[*]T - usize` 25792 } 25793 }, 25794 }; 25795 return Air.internedToRef(ty.toIntern()); 25796 } 25797 25798 fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 25799 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25800 const uncoerced_hint = try sema.resolveInst(extra.operand); 25801 const operand_src = block.builtinCallArgSrc(extra.node, 0); 25802 25803 const hint_ty = try sema.getBuiltinType(operand_src, .BranchHint); 25804 const coerced_hint = try sema.coerce(block, hint_ty, uncoerced_hint, operand_src); 25805 const hint_val = try sema.resolveConstDefinedValue(block, operand_src, coerced_hint, .{ .simple = .operand_branchHint }); 25806 25807 // We only apply the first hint in a branch. 25808 // This allows user-provided hints to override implicit cold hints. 25809 if (sema.branch_hint == null) { 25810 sema.branch_hint = try sema.interpretBuiltinType(block, operand_src, hint_val, std.builtin.BranchHint); 25811 } 25812 } 25813 25814 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { 25815 if (block.isComptime()) { 25816 const msg, const fail_block = msg: { 25817 const msg = try sema.errMsg(src, "unable to evaluate comptime expression", .{}); 25818 errdefer msg.destroy(sema.gpa); 25819 25820 if (runtime_src) |some| { 25821 try sema.errNote(some, msg, "operation is runtime due to this operand", .{}); 25822 } 25823 25824 const fail_block = try block.explainWhyBlockIsComptime(msg); 25825 25826 break :msg .{ msg, fail_block }; 25827 }; 25828 return sema.failWithOwnedErrorMsg(fail_block, msg); 25829 } 25830 } 25831 25832 /// Emit a compile error if type cannot be used for a runtime variable. 25833 pub fn validateVarType( 25834 sema: *Sema, 25835 block: *Block, 25836 src: LazySrcLoc, 25837 var_ty: Type, 25838 is_extern: bool, 25839 ) CompileError!void { 25840 const pt = sema.pt; 25841 const zcu = pt.zcu; 25842 if (is_extern) { 25843 if (!try sema.validateExternType(var_ty, .other)) { 25844 const msg = msg: { 25845 const msg = try sema.errMsg(src, "extern variable cannot have type '{f}'", .{var_ty.fmt(pt)}); 25846 errdefer msg.destroy(sema.gpa); 25847 try sema.explainWhyTypeIsNotExtern(msg, src, var_ty, .other); 25848 break :msg msg; 25849 }; 25850 return sema.failWithOwnedErrorMsg(block, msg); 25851 } 25852 } else { 25853 if (var_ty.zigTypeTag(zcu) == .@"opaque") { 25854 return sema.fail( 25855 block, 25856 src, 25857 "non-extern variable with opaque type '{f}'", 25858 .{var_ty.fmt(pt)}, 25859 ); 25860 } 25861 } 25862 25863 if (!try var_ty.comptimeOnlySema(pt)) return; 25864 25865 const msg = msg: { 25866 const msg = try sema.errMsg(src, "variable of type '{f}' must be const or comptime", .{var_ty.fmt(pt)}); 25867 errdefer msg.destroy(sema.gpa); 25868 25869 try sema.explainWhyTypeIsComptime(msg, src, var_ty); 25870 if (var_ty.zigTypeTag(zcu) == .comptime_int or var_ty.zigTypeTag(zcu) == .comptime_float) { 25871 try sema.errNote(src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{}); 25872 } 25873 25874 break :msg msg; 25875 }; 25876 return sema.failWithOwnedErrorMsg(block, msg); 25877 } 25878 25879 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void); 25880 25881 fn explainWhyTypeIsComptime( 25882 sema: *Sema, 25883 msg: *Zcu.ErrorMsg, 25884 src_loc: LazySrcLoc, 25885 ty: Type, 25886 ) CompileError!void { 25887 var type_set = TypeSet{}; 25888 defer type_set.deinit(sema.gpa); 25889 25890 try ty.resolveFully(sema.pt); 25891 return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set); 25892 } 25893 25894 fn explainWhyTypeIsComptimeInner( 25895 sema: *Sema, 25896 msg: *Zcu.ErrorMsg, 25897 src_loc: LazySrcLoc, 25898 ty: Type, 25899 type_set: *TypeSet, 25900 ) CompileError!void { 25901 const pt = sema.pt; 25902 const zcu = pt.zcu; 25903 const ip = &zcu.intern_pool; 25904 switch (ty.zigTypeTag(zcu)) { 25905 .bool, 25906 .int, 25907 .float, 25908 .error_set, 25909 .@"enum", 25910 .frame, 25911 .@"anyframe", 25912 .void, 25913 => return, 25914 25915 .@"fn" => { 25916 try sema.errNote(src_loc, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)}); 25917 }, 25918 25919 .type => { 25920 try sema.errNote(src_loc, msg, "types are not available at runtime", .{}); 25921 }, 25922 25923 .comptime_float, 25924 .comptime_int, 25925 .enum_literal, 25926 .noreturn, 25927 .undefined, 25928 .null, 25929 => return, 25930 25931 .@"opaque" => { 25932 try sema.errNote(src_loc, msg, "opaque type '{f}' has undefined size", .{ty.fmt(pt)}); 25933 }, 25934 25935 .array, .vector => { 25936 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set); 25937 }, 25938 .pointer => { 25939 const elem_ty = ty.elemType2(zcu); 25940 if (elem_ty.zigTypeTag(zcu) == .@"fn") { 25941 const fn_info = zcu.typeToFunc(elem_ty).?; 25942 if (fn_info.is_generic) { 25943 try sema.errNote(src_loc, msg, "function is generic", .{}); 25944 } 25945 switch (fn_info.cc) { 25946 .@"inline" => try sema.errNote(src_loc, msg, "function has inline calling convention", .{}), 25947 else => {}, 25948 } 25949 if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) { 25950 try sema.errNote(src_loc, msg, "function has a comptime-only return type", .{}); 25951 } 25952 return; 25953 } 25954 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set); 25955 }, 25956 25957 .optional => { 25958 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(zcu), type_set); 25959 }, 25960 .error_union => { 25961 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(zcu), type_set); 25962 }, 25963 25964 .@"struct" => { 25965 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 25966 25967 if (zcu.typeToStruct(ty)) |struct_type| { 25968 for (0..struct_type.field_types.len) |i| { 25969 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 25970 const field_src: LazySrcLoc = .{ 25971 .base_node_inst = struct_type.zir_index, 25972 .offset = .{ .container_field_type = @intCast(i) }, 25973 }; 25974 25975 if (try field_ty.comptimeOnlySema(pt)) { 25976 try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{}); 25977 try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set); 25978 } 25979 } 25980 } 25981 // TODO tuples 25982 }, 25983 25984 .@"union" => { 25985 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 25986 25987 if (zcu.typeToUnion(ty)) |union_obj| { 25988 for (0..union_obj.field_types.len) |i| { 25989 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]); 25990 const field_src: LazySrcLoc = .{ 25991 .base_node_inst = union_obj.zir_index, 25992 .offset = .{ .container_field_type = @intCast(i) }, 25993 }; 25994 25995 if (try field_ty.comptimeOnlySema(pt)) { 25996 try sema.errNote(field_src, msg, "union requires comptime because of this field", .{}); 25997 try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set); 25998 } 25999 } 26000 } 26001 }, 26002 } 26003 } 26004 26005 const ExternPosition = enum { 26006 ret_ty, 26007 param_ty, 26008 union_field, 26009 struct_field, 26010 element, 26011 other, 26012 }; 26013 26014 /// Returns true if `ty` is allowed in extern types. 26015 /// Does *NOT* require `ty` to be resolved in any way. 26016 /// Calls `resolveLayout` for packed containers. 26017 fn validateExternType( 26018 sema: *Sema, 26019 ty: Type, 26020 position: ExternPosition, 26021 ) !bool { 26022 const pt = sema.pt; 26023 const zcu = pt.zcu; 26024 switch (ty.zigTypeTag(zcu)) { 26025 .type, 26026 .comptime_float, 26027 .comptime_int, 26028 .enum_literal, 26029 .undefined, 26030 .null, 26031 .error_union, 26032 .error_set, 26033 .frame, 26034 => return false, 26035 .void => return position == .union_field or position == .ret_ty or position == .struct_field or position == .element, 26036 .noreturn => return position == .ret_ty, 26037 .@"opaque", 26038 .bool, 26039 .float, 26040 .@"anyframe", 26041 => return true, 26042 .pointer => { 26043 if (ty.childType(zcu).zigTypeTag(zcu) == .@"fn") { 26044 return ty.isConstPtr(zcu) and try sema.validateExternType(ty.childType(zcu), .other); 26045 } 26046 return !(ty.isSlice(zcu) or try ty.comptimeOnlySema(pt)); 26047 }, 26048 .int => switch (ty.intInfo(zcu).bits) { 26049 0, 8, 16, 32, 64, 128 => return true, 26050 else => return false, 26051 }, 26052 .@"fn" => { 26053 if (position != .other) return false; 26054 // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI. 26055 // The goal is to experiment with more integrated CPU/GPU code. 26056 if (ty.fnCallingConvention(zcu) == .nvptx_kernel) { 26057 return true; 26058 } 26059 return !target_util.fnCallConvAllowsZigTypes(ty.fnCallingConvention(zcu)); 26060 }, 26061 .@"enum" => { 26062 return sema.validateExternType(ty.intTagType(zcu), position); 26063 }, 26064 .@"struct", .@"union" => switch (ty.containerLayout(zcu)) { 26065 .@"extern" => return true, 26066 .@"packed" => { 26067 const bit_size = try ty.bitSizeSema(pt); 26068 switch (bit_size) { 26069 0, 8, 16, 32, 64, 128 => return true, 26070 else => return false, 26071 } 26072 }, 26073 .auto => return !(try ty.hasRuntimeBitsSema(pt)), 26074 }, 26075 .array => { 26076 if (position == .ret_ty or position == .param_ty) return false; 26077 return sema.validateExternType(ty.elemType2(zcu), .element); 26078 }, 26079 .vector => return sema.validateExternType(ty.elemType2(zcu), .element), 26080 .optional => return ty.isPtrLikeOptional(zcu), 26081 } 26082 } 26083 26084 fn explainWhyTypeIsNotExtern( 26085 sema: *Sema, 26086 msg: *Zcu.ErrorMsg, 26087 src_loc: LazySrcLoc, 26088 ty: Type, 26089 position: ExternPosition, 26090 ) CompileError!void { 26091 const pt = sema.pt; 26092 const zcu = pt.zcu; 26093 switch (ty.zigTypeTag(zcu)) { 26094 .@"opaque", 26095 .bool, 26096 .float, 26097 .@"anyframe", 26098 => return, 26099 26100 .type, 26101 .comptime_float, 26102 .comptime_int, 26103 .enum_literal, 26104 .undefined, 26105 .null, 26106 .error_union, 26107 .error_set, 26108 .frame, 26109 => return, 26110 26111 .pointer => { 26112 if (ty.isSlice(zcu)) { 26113 try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); 26114 } else { 26115 const pointee_ty = ty.childType(zcu); 26116 if (!ty.isConstPtr(zcu) and pointee_ty.zigTypeTag(zcu) == .@"fn") { 26117 try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{}); 26118 } else if (try ty.comptimeOnlySema(pt)) { 26119 try sema.errNote(src_loc, msg, "pointer to comptime-only type '{f}'", .{pointee_ty.fmt(pt)}); 26120 try sema.explainWhyTypeIsComptime(msg, src_loc, ty); 26121 } 26122 try sema.explainWhyTypeIsNotExtern(msg, src_loc, pointee_ty, .other); 26123 } 26124 }, 26125 .void => try sema.errNote(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}), 26126 .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), 26127 .int => if (!std.math.isPowerOfTwo(ty.intInfo(zcu).bits)) { 26128 try sema.errNote(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{}); 26129 } else { 26130 try sema.errNote(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{}); 26131 }, 26132 .@"fn" => { 26133 if (position != .other) { 26134 try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 26135 try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 26136 return; 26137 } 26138 switch (ty.fnCallingConvention(zcu)) { 26139 .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}), 26140 .async => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}), 26141 .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}), 26142 else => return, 26143 } 26144 }, 26145 .@"enum" => { 26146 const tag_ty = ty.intTagType(zcu); 26147 try sema.errNote(src_loc, msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)}); 26148 try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position); 26149 }, 26150 .@"struct" => try sema.errNote(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}), 26151 .@"union" => try sema.errNote(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}), 26152 .array => { 26153 if (position == .ret_ty) { 26154 return sema.errNote(src_loc, msg, "arrays are not allowed as a return type", .{}); 26155 } else if (position == .param_ty) { 26156 return sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{}); 26157 } 26158 try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element); 26159 }, 26160 .vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element), 26161 .optional => try sema.errNote(src_loc, msg, "only pointer like optionals are extern compatible", .{}), 26162 } 26163 } 26164 26165 /// Returns true if `ty` is allowed in packed types. 26166 /// Does not require `ty` to be resolved in any way, but may resolve whether it is comptime-only. 26167 fn validatePackedType(sema: *Sema, ty: Type) !bool { 26168 const pt = sema.pt; 26169 const zcu = pt.zcu; 26170 return switch (ty.zigTypeTag(zcu)) { 26171 .type, 26172 .comptime_float, 26173 .comptime_int, 26174 .enum_literal, 26175 .undefined, 26176 .null, 26177 .error_union, 26178 .error_set, 26179 .frame, 26180 .noreturn, 26181 .@"opaque", 26182 .@"anyframe", 26183 .@"fn", 26184 .array, 26185 => false, 26186 .optional => return ty.isPtrLikeOptional(zcu), 26187 .void, 26188 .bool, 26189 .float, 26190 .int, 26191 .vector, 26192 => true, 26193 .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).tag_mode) { 26194 .auto => false, 26195 .explicit, .nonexhaustive => true, 26196 }, 26197 .pointer => !ty.isSlice(zcu) and !try ty.comptimeOnlySema(pt), 26198 .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed", 26199 }; 26200 } 26201 26202 fn explainWhyTypeIsNotPacked( 26203 sema: *Sema, 26204 msg: *Zcu.ErrorMsg, 26205 src_loc: LazySrcLoc, 26206 ty: Type, 26207 ) CompileError!void { 26208 const pt = sema.pt; 26209 const zcu = pt.zcu; 26210 switch (ty.zigTypeTag(zcu)) { 26211 .void, 26212 .bool, 26213 .float, 26214 .int, 26215 .vector, 26216 .@"enum", 26217 => return, 26218 .type, 26219 .comptime_float, 26220 .comptime_int, 26221 .enum_literal, 26222 .undefined, 26223 .null, 26224 .frame, 26225 .noreturn, 26226 .@"opaque", 26227 .error_union, 26228 .error_set, 26229 .@"anyframe", 26230 .optional, 26231 .array, 26232 => try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}), 26233 .pointer => if (ty.isSlice(zcu)) { 26234 try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); 26235 } else { 26236 try sema.errNote(src_loc, msg, "comptime-only pointer has no guaranteed in-memory representation", .{}); 26237 try sema.explainWhyTypeIsComptime(msg, src_loc, ty); 26238 }, 26239 .@"fn" => { 26240 try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 26241 try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 26242 }, 26243 .@"struct" => try sema.errNote(src_loc, msg, "only packed structs layout are allowed in packed types", .{}), 26244 .@"union" => try sema.errNote(src_loc, msg, "only packed unions layout are allowed in packed types", .{}), 26245 } 26246 } 26247 26248 /// Backends depend on panic decls being available when lowering safety-checked 26249 /// instructions. This function ensures the panic function will be available to 26250 /// be called during that time. 26251 fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void { 26252 // If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers. 26253 // The backend will transform panics into traps instead. 26254 if (sema.pt.zcu.backendSupportsFeature(.panic_fn)) { 26255 _ = try sema.getPanicIdFunc(src, panic_id); 26256 } 26257 } 26258 26259 fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index { 26260 const zcu = sema.pt.zcu; 26261 try sema.ensureMemoizedStateResolved(src, .panic); 26262 const panic_func = zcu.builtin_decl_values.get(panic_id.toBuiltin()); 26263 try zcu.ensureFuncBodyAnalysisQueued(panic_func); 26264 switch (sema.owner.unwrap()) { 26265 .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, 26266 .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true), 26267 } 26268 return panic_func; 26269 } 26270 26271 fn addSafetyCheck( 26272 sema: *Sema, 26273 parent_block: *Block, 26274 src: LazySrcLoc, 26275 ok: Air.Inst.Ref, 26276 panic_id: Zcu.SimplePanicId, 26277 ) !void { 26278 const gpa = sema.gpa; 26279 assert(!parent_block.isComptime()); 26280 26281 var fail_block: Block = .{ 26282 .parent = parent_block, 26283 .sema = sema, 26284 .namespace = parent_block.namespace, 26285 .instructions = .{}, 26286 .inlining = parent_block.inlining, 26287 .comptime_reason = null, 26288 .src_base_inst = parent_block.src_base_inst, 26289 .type_name_ctx = parent_block.type_name_ctx, 26290 }; 26291 26292 defer fail_block.instructions.deinit(gpa); 26293 26294 try sema.safetyPanic(&fail_block, src, panic_id); 26295 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 26296 } 26297 26298 fn addSafetyCheckExtra( 26299 sema: *Sema, 26300 parent_block: *Block, 26301 ok: Air.Inst.Ref, 26302 fail_block: *Block, 26303 ) !void { 26304 const gpa = sema.gpa; 26305 26306 try parent_block.instructions.ensureUnusedCapacity(gpa, 1); 26307 26308 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 26309 1 + // The main block only needs space for the cond_br. 26310 @typeInfo(Air.CondBr).@"struct".fields.len + 26311 1 + // The ok branch of the cond_br only needs space for the br. 26312 fail_block.instructions.items.len); 26313 26314 try sema.air_instructions.ensureUnusedCapacity(gpa, 3); 26315 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 26316 const cond_br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1); 26317 const br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(cond_br_inst) + 1); 26318 sema.air_instructions.appendAssumeCapacity(.{ 26319 .tag = .block, 26320 .data = .{ .ty_pl = .{ 26321 .ty = .void_type, 26322 .payload = sema.addExtraAssumeCapacity(Air.Block{ 26323 .body_len = 1, 26324 }), 26325 } }, 26326 }); 26327 sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst)); 26328 26329 sema.air_instructions.appendAssumeCapacity(.{ 26330 .tag = .cond_br, 26331 .data = .{ 26332 .pl_op = .{ 26333 .operand = ok, 26334 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 26335 .then_body_len = 1, 26336 .else_body_len = @intCast(fail_block.instructions.items.len), 26337 .branch_hints = .{ 26338 // Safety check failure branch is cold. 26339 .true = .likely, 26340 .false = .cold, 26341 // Code coverage not wanted for panic branches. 26342 .then_cov = .none, 26343 .else_cov = .none, 26344 }, 26345 }), 26346 }, 26347 }, 26348 }); 26349 sema.air_extra.appendAssumeCapacity(@intFromEnum(br_inst)); 26350 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items)); 26351 26352 sema.air_instructions.appendAssumeCapacity(.{ 26353 .tag = .br, 26354 .data = .{ .br = .{ 26355 .block_inst = block_inst, 26356 .operand = .void_value, 26357 } }, 26358 }); 26359 26360 parent_block.instructions.appendAssumeCapacity(block_inst); 26361 } 26362 26363 fn addSafetyCheckUnwrapError( 26364 sema: *Sema, 26365 parent_block: *Block, 26366 src: LazySrcLoc, 26367 operand: Air.Inst.Ref, 26368 unwrap_err_tag: Air.Inst.Tag, 26369 is_non_err_tag: Air.Inst.Tag, 26370 ) !void { 26371 assert(!parent_block.isComptime()); 26372 const ok = try parent_block.addUnOp(is_non_err_tag, operand); 26373 const gpa = sema.gpa; 26374 26375 var fail_block: Block = .{ 26376 .parent = parent_block, 26377 .sema = sema, 26378 .namespace = parent_block.namespace, 26379 .instructions = .{}, 26380 .inlining = parent_block.inlining, 26381 .comptime_reason = null, 26382 .src_base_inst = parent_block.src_base_inst, 26383 .type_name_ctx = parent_block.type_name_ctx, 26384 }; 26385 26386 defer fail_block.instructions.deinit(gpa); 26387 26388 const err = try fail_block.addTyOp(unwrap_err_tag, .anyerror, operand); 26389 try safetyPanicUnwrapError(sema, &fail_block, src, err); 26390 26391 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 26392 } 26393 26394 fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.Inst.Ref) !void { 26395 const pt = sema.pt; 26396 const zcu = pt.zcu; 26397 if (!zcu.backendSupportsFeature(.panic_fn)) { 26398 _ = try block.addNoOp(.trap); 26399 } else { 26400 const panic_fn = try getBuiltin(sema, src, .@"panic.unwrapError"); 26401 try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{err}, .@"safety check"); 26402 } 26403 } 26404 26405 fn addSafetyCheckIndexOob( 26406 sema: *Sema, 26407 parent_block: *Block, 26408 src: LazySrcLoc, 26409 index: Air.Inst.Ref, 26410 len: Air.Inst.Ref, 26411 cmp_op: Air.Inst.Tag, 26412 ) !void { 26413 assert(!parent_block.isComptime()); 26414 const ok = try parent_block.addBinOp(cmp_op, index, len); 26415 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.outOfBounds", &.{ index, len }); 26416 } 26417 26418 fn addSafetyCheckInactiveUnionField( 26419 sema: *Sema, 26420 parent_block: *Block, 26421 src: LazySrcLoc, 26422 active_tag: Air.Inst.Ref, 26423 wanted_tag: Air.Inst.Ref, 26424 ) !void { 26425 assert(!parent_block.isComptime()); 26426 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); 26427 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.inactiveUnionField", &.{ active_tag, wanted_tag }); 26428 } 26429 26430 fn addSafetyCheckSentinelMismatch( 26431 sema: *Sema, 26432 parent_block: *Block, 26433 src: LazySrcLoc, 26434 maybe_sentinel: ?Value, 26435 sentinel_ty: Type, 26436 ptr: Air.Inst.Ref, 26437 sentinel_index: Air.Inst.Ref, 26438 ) !void { 26439 assert(!parent_block.isComptime()); 26440 const pt = sema.pt; 26441 const zcu = pt.zcu; 26442 const expected_sentinel_val = maybe_sentinel orelse return; 26443 const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern()); 26444 26445 const ptr_ty = sema.typeOf(ptr); 26446 const actual_sentinel = if (ptr_ty.isSlice(zcu)) 26447 try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) 26448 else blk: { 26449 const elem_ptr_ty = try ptr_ty.elemPtrType(null, pt); 26450 const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty); 26451 break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); 26452 }; 26453 26454 const ok = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: { 26455 const eql = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); 26456 break :ok try parent_block.addReduce(eql, .And); 26457 } else ok: { 26458 assert(sentinel_ty.isSelfComparable(zcu, true)); 26459 break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel); 26460 }; 26461 26462 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{ 26463 expected_sentinel, actual_sentinel, 26464 }); 26465 } 26466 26467 fn addSafetyCheckCall( 26468 sema: *Sema, 26469 parent_block: *Block, 26470 src: LazySrcLoc, 26471 ok: Air.Inst.Ref, 26472 comptime func_decl: Zcu.BuiltinDecl, 26473 args: []const Air.Inst.Ref, 26474 ) !void { 26475 assert(!parent_block.isComptime()); 26476 const gpa = sema.gpa; 26477 const pt = sema.pt; 26478 const zcu = pt.zcu; 26479 26480 var fail_block: Block = .{ 26481 .parent = parent_block, 26482 .sema = sema, 26483 .namespace = parent_block.namespace, 26484 .instructions = .{}, 26485 .inlining = parent_block.inlining, 26486 .comptime_reason = null, 26487 .src_base_inst = parent_block.src_base_inst, 26488 .type_name_ctx = parent_block.type_name_ctx, 26489 }; 26490 26491 defer fail_block.instructions.deinit(gpa); 26492 26493 if (!zcu.backendSupportsFeature(.panic_fn)) { 26494 _ = try fail_block.addNoOp(.trap); 26495 } else { 26496 const panic_fn = try getBuiltin(sema, src, func_decl); 26497 try sema.callBuiltin(&fail_block, src, Air.internedToRef(panic_fn), .auto, args, .@"safety check"); 26498 } 26499 26500 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 26501 } 26502 26503 /// This does not set `sema.branch_hint`. 26504 fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) CompileError!void { 26505 if (!sema.pt.zcu.backendSupportsFeature(.panic_fn)) { 26506 _ = try block.addNoOp(.trap); 26507 } else { 26508 const panic_fn = try sema.getPanicIdFunc(src, panic_id); 26509 try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{}, .@"safety check"); 26510 } 26511 } 26512 26513 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { 26514 sema.branch_count += 1; 26515 if (sema.branch_count > sema.branch_quota) { 26516 const msg = try sema.errMsg( 26517 src, 26518 "evaluation exceeded {d} backwards branches", 26519 .{sema.branch_quota}, 26520 ); 26521 try sema.errNote( 26522 src, 26523 msg, 26524 "use @setEvalBranchQuota() to raise the branch limit from {d}", 26525 .{sema.branch_quota}, 26526 ); 26527 return sema.failWithOwnedErrorMsg(block, msg); 26528 } 26529 } 26530 26531 fn fieldVal( 26532 sema: *Sema, 26533 block: *Block, 26534 src: LazySrcLoc, 26535 object: Air.Inst.Ref, 26536 field_name: InternPool.NullTerminatedString, 26537 field_name_src: LazySrcLoc, 26538 ) CompileError!Air.Inst.Ref { 26539 // When editing this function, note that there is corresponding logic to be edited 26540 // in `fieldPtr`. This function takes a value and returns a value. 26541 26542 const pt = sema.pt; 26543 const zcu = pt.zcu; 26544 const ip = &zcu.intern_pool; 26545 const object_src = src; // TODO better source location 26546 const object_ty = sema.typeOf(object); 26547 26548 // Zig allows dereferencing a single pointer during field lookup. Note that 26549 // we don't actually need to generate the dereference some field lookups, like the 26550 // length of arrays and other comptime operations. 26551 const is_pointer_to = object_ty.isSinglePointer(zcu); 26552 26553 const inner_ty = if (is_pointer_to) 26554 object_ty.childType(zcu) 26555 else 26556 object_ty; 26557 26558 switch (inner_ty.zigTypeTag(zcu)) { 26559 .array => { 26560 if (field_name.eqlSlice("len", ip)) { 26561 return Air.internedToRef((try pt.intValue(.usize, inner_ty.arrayLen(zcu))).toIntern()); 26562 } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { 26563 const ptr_info = object_ty.ptrInfo(zcu); 26564 const result_ty = try pt.ptrTypeSema(.{ 26565 .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(), 26566 .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none, 26567 .flags = .{ 26568 .size = .many, 26569 .alignment = ptr_info.flags.alignment, 26570 .is_const = ptr_info.flags.is_const, 26571 .is_volatile = ptr_info.flags.is_volatile, 26572 .is_allowzero = ptr_info.flags.is_allowzero, 26573 .address_space = ptr_info.flags.address_space, 26574 .vector_index = ptr_info.flags.vector_index, 26575 }, 26576 .packed_offset = ptr_info.packed_offset, 26577 }); 26578 return sema.coerce(block, result_ty, object, src); 26579 } else { 26580 return sema.fail( 26581 block, 26582 field_name_src, 26583 "no member named '{f}' in '{f}'", 26584 .{ field_name.fmt(ip), object_ty.fmt(pt) }, 26585 ); 26586 } 26587 }, 26588 .pointer => { 26589 const ptr_info = inner_ty.ptrInfo(zcu); 26590 if (ptr_info.flags.size == .slice) { 26591 if (field_name.eqlSlice("ptr", ip)) { 26592 const slice = if (is_pointer_to) 26593 try sema.analyzeLoad(block, src, object, object_src) 26594 else 26595 object; 26596 return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); 26597 } else if (field_name.eqlSlice("len", ip)) { 26598 const slice = if (is_pointer_to) 26599 try sema.analyzeLoad(block, src, object, object_src) 26600 else 26601 object; 26602 return sema.analyzeSliceLen(block, src, slice); 26603 } else { 26604 return sema.fail( 26605 block, 26606 field_name_src, 26607 "no member named '{f}' in '{f}'", 26608 .{ field_name.fmt(ip), object_ty.fmt(pt) }, 26609 ); 26610 } 26611 } 26612 }, 26613 .type => { 26614 const dereffed_type = if (is_pointer_to) 26615 try sema.analyzeLoad(block, src, object, object_src) 26616 else 26617 object; 26618 26619 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?; 26620 const child_type = val.toType(); 26621 26622 switch (child_type.zigTypeTag(zcu)) { 26623 .error_set => { 26624 switch (ip.indexToKey(child_type.toIntern())) { 26625 .error_set_type => |error_set_type| blk: { 26626 if (error_set_type.nameIndex(ip, field_name) != null) break :blk; 26627 return sema.fail(block, src, "no error named '{f}' in '{f}'", .{ 26628 field_name.fmt(ip), child_type.fmt(pt), 26629 }); 26630 }, 26631 .inferred_error_set_type => { 26632 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 26633 }, 26634 .simple_type => |t| { 26635 assert(t == .anyerror); 26636 _ = try pt.getErrorValue(field_name); 26637 }, 26638 else => unreachable, 26639 } 26640 26641 const error_set_type = if (!child_type.isAnyError(zcu)) 26642 child_type 26643 else 26644 try pt.singleErrorSetType(field_name); 26645 return Air.internedToRef((try pt.intern(.{ .err = .{ 26646 .ty = error_set_type.toIntern(), 26647 .name = field_name, 26648 } }))); 26649 }, 26650 .@"union" => { 26651 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26652 return inst; 26653 } 26654 try child_type.resolveFields(pt); 26655 if (child_type.unionTagType(zcu)) |enum_ty| { 26656 if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| { 26657 const field_index: u32 = @intCast(field_index_usize); 26658 return Air.internedToRef((try pt.enumValueFieldIndex(enum_ty, field_index)).toIntern()); 26659 } 26660 } 26661 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26662 }, 26663 .@"enum" => { 26664 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26665 return inst; 26666 } 26667 const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse 26668 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26669 const field_index: u32 = @intCast(field_index_usize); 26670 const enum_val = try pt.enumValueFieldIndex(child_type, field_index); 26671 return Air.internedToRef(enum_val.toIntern()); 26672 }, 26673 .@"struct", .@"opaque" => { 26674 if (!child_type.isTuple(zcu) and child_type.toIntern() != .anyopaque_type) { 26675 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26676 return inst; 26677 } 26678 } 26679 return sema.failWithBadMemberAccess(block, child_type, src, field_name); 26680 }, 26681 else => return sema.failWithOwnedErrorMsg(block, msg: { 26682 const msg = try sema.errMsg(src, "type '{f}' has no members", .{child_type.fmt(pt)}); 26683 errdefer msg.destroy(sema.gpa); 26684 if (child_type.isSlice(zcu)) try sema.errNote(src, msg, "slice values have 'len' and 'ptr' members", .{}); 26685 if (child_type.zigTypeTag(zcu) == .array) try sema.errNote(src, msg, "array values have 'len' member", .{}); 26686 break :msg msg; 26687 }), 26688 } 26689 }, 26690 .@"struct" => if (is_pointer_to) { 26691 // Avoid loading the entire struct by fetching a pointer and loading that 26692 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 26693 return sema.analyzeLoad(block, src, field_ptr, object_src); 26694 } else { 26695 return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty); 26696 }, 26697 .@"union" => if (is_pointer_to) { 26698 // Avoid loading the entire union by fetching a pointer and loading that 26699 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 26700 return sema.analyzeLoad(block, src, field_ptr, object_src); 26701 } else { 26702 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty); 26703 }, 26704 else => {}, 26705 } 26706 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 26707 } 26708 26709 fn fieldPtr( 26710 sema: *Sema, 26711 block: *Block, 26712 src: LazySrcLoc, 26713 object_ptr: Air.Inst.Ref, 26714 field_name: InternPool.NullTerminatedString, 26715 field_name_src: LazySrcLoc, 26716 initializing: bool, 26717 ) CompileError!Air.Inst.Ref { 26718 // When editing this function, note that there is corresponding logic to be edited 26719 // in `fieldVal`. This function takes a pointer and returns a pointer. 26720 26721 const pt = sema.pt; 26722 const zcu = pt.zcu; 26723 const ip = &zcu.intern_pool; 26724 const object_ptr_src = src; // TODO better source location 26725 const object_ptr_ty = sema.typeOf(object_ptr); 26726 const object_ty = switch (object_ptr_ty.zigTypeTag(zcu)) { 26727 .pointer => object_ptr_ty.childType(zcu), 26728 else => return sema.fail(block, object_ptr_src, "expected pointer, found '{f}'", .{object_ptr_ty.fmt(pt)}), 26729 }; 26730 26731 // Zig allows dereferencing a single pointer during field lookup. Note that 26732 // we don't actually need to generate the dereference some field lookups, like the 26733 // length of arrays and other comptime operations. 26734 const is_pointer_to = object_ty.isSinglePointer(zcu); 26735 26736 const inner_ty = if (is_pointer_to) 26737 object_ty.childType(zcu) 26738 else 26739 object_ty; 26740 26741 switch (inner_ty.zigTypeTag(zcu)) { 26742 .array => { 26743 if (field_name.eqlSlice("len", ip)) { 26744 const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu)); 26745 return uavRef(sema, int_val.toIntern()); 26746 } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { 26747 const ptr_info = object_ty.ptrInfo(zcu); 26748 const new_ptr_ty = try pt.ptrTypeSema(.{ 26749 .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(), 26750 .sentinel = if (object_ty.sentinel(zcu)) |s| s.toIntern() else .none, 26751 .flags = .{ 26752 .size = .many, 26753 .alignment = ptr_info.flags.alignment, 26754 .is_const = ptr_info.flags.is_const, 26755 .is_volatile = ptr_info.flags.is_volatile, 26756 .is_allowzero = ptr_info.flags.is_allowzero, 26757 .address_space = ptr_info.flags.address_space, 26758 .vector_index = ptr_info.flags.vector_index, 26759 }, 26760 .packed_offset = ptr_info.packed_offset, 26761 }); 26762 const ptr_ptr_info = object_ptr_ty.ptrInfo(zcu); 26763 const result_ty = try pt.ptrTypeSema(.{ 26764 .child = new_ptr_ty.toIntern(), 26765 .sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, 26766 .flags = .{ 26767 .alignment = ptr_ptr_info.flags.alignment, 26768 .is_const = ptr_ptr_info.flags.is_const, 26769 .is_volatile = ptr_ptr_info.flags.is_volatile, 26770 .is_allowzero = ptr_ptr_info.flags.is_allowzero, 26771 .address_space = ptr_ptr_info.flags.address_space, 26772 .vector_index = ptr_ptr_info.flags.vector_index, 26773 }, 26774 .packed_offset = ptr_ptr_info.packed_offset, 26775 }); 26776 return sema.bitCast(block, result_ty, object_ptr, src, null); 26777 } else { 26778 return sema.fail( 26779 block, 26780 field_name_src, 26781 "no member named '{f}' in '{f}'", 26782 .{ field_name.fmt(ip), object_ty.fmt(pt) }, 26783 ); 26784 } 26785 }, 26786 .pointer => if (inner_ty.isSlice(zcu)) { 26787 const inner_ptr = if (is_pointer_to) 26788 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 26789 else 26790 object_ptr; 26791 26792 const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty; 26793 26794 if (field_name.eqlSlice("ptr", ip)) { 26795 const slice_ptr_ty = inner_ty.slicePtrFieldType(zcu); 26796 26797 const result_ty = try pt.ptrTypeSema(.{ 26798 .child = slice_ptr_ty.toIntern(), 26799 .flags = .{ 26800 .is_const = !attr_ptr_ty.ptrIsMutable(zcu), 26801 .is_volatile = attr_ptr_ty.isVolatilePtr(zcu), 26802 .address_space = attr_ptr_ty.ptrAddressSpace(zcu), 26803 }, 26804 }); 26805 26806 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 26807 return Air.internedToRef((try val.ptrField(Value.slice_ptr_index, pt)).toIntern()); 26808 } 26809 try sema.requireRuntimeBlock(block, src, null); 26810 26811 const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); 26812 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); 26813 return field_ptr; 26814 } else if (field_name.eqlSlice("len", ip)) { 26815 const result_ty = try pt.ptrTypeSema(.{ 26816 .child = .usize_type, 26817 .flags = .{ 26818 .is_const = !attr_ptr_ty.ptrIsMutable(zcu), 26819 .is_volatile = attr_ptr_ty.isVolatilePtr(zcu), 26820 .address_space = attr_ptr_ty.ptrAddressSpace(zcu), 26821 }, 26822 }); 26823 26824 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 26825 return Air.internedToRef((try val.ptrField(Value.slice_len_index, pt)).toIntern()); 26826 } 26827 try sema.requireRuntimeBlock(block, src, null); 26828 26829 const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); 26830 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); 26831 return field_ptr; 26832 } else { 26833 return sema.fail( 26834 block, 26835 field_name_src, 26836 "no member named '{f}' in '{f}'", 26837 .{ field_name.fmt(ip), object_ty.fmt(pt) }, 26838 ); 26839 } 26840 }, 26841 .type => { 26842 _ = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, object_ptr, undefined); 26843 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); 26844 const inner = if (is_pointer_to) 26845 try sema.analyzeLoad(block, src, result, object_ptr_src) 26846 else 26847 result; 26848 26849 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?; 26850 const child_type = val.toType(); 26851 26852 switch (child_type.zigTypeTag(zcu)) { 26853 .error_set => { 26854 switch (ip.indexToKey(child_type.toIntern())) { 26855 .error_set_type => |error_set_type| blk: { 26856 if (error_set_type.nameIndex(ip, field_name) != null) { 26857 break :blk; 26858 } 26859 return sema.fail(block, src, "no error named '{f}' in '{f}'", .{ 26860 field_name.fmt(ip), child_type.fmt(pt), 26861 }); 26862 }, 26863 .inferred_error_set_type => { 26864 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 26865 }, 26866 .simple_type => |t| { 26867 assert(t == .anyerror); 26868 _ = try pt.getErrorValue(field_name); 26869 }, 26870 else => unreachable, 26871 } 26872 26873 const error_set_type = if (!child_type.isAnyError(zcu)) 26874 child_type 26875 else 26876 try pt.singleErrorSetType(field_name); 26877 return uavRef(sema, try pt.intern(.{ .err = .{ 26878 .ty = error_set_type.toIntern(), 26879 .name = field_name, 26880 } })); 26881 }, 26882 .@"union" => { 26883 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26884 return inst; 26885 } 26886 try child_type.resolveFields(pt); 26887 if (child_type.unionTagType(zcu)) |enum_ty| { 26888 if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| { 26889 const field_index_u32: u32 = @intCast(field_index); 26890 const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32); 26891 return uavRef(sema, idx_val.toIntern()); 26892 } 26893 } 26894 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26895 }, 26896 .@"enum" => { 26897 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26898 return inst; 26899 } 26900 const field_index = child_type.enumFieldIndex(field_name, zcu) orelse { 26901 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26902 }; 26903 const field_index_u32: u32 = @intCast(field_index); 26904 const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32); 26905 return uavRef(sema, idx_val.toIntern()); 26906 }, 26907 .@"struct", .@"opaque" => { 26908 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { 26909 return inst; 26910 } 26911 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26912 }, 26913 else => return sema.fail(block, src, "type '{f}' has no members", .{child_type.fmt(pt)}), 26914 } 26915 }, 26916 .@"struct" => { 26917 const inner_ptr = if (is_pointer_to) 26918 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 26919 else 26920 object_ptr; 26921 const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 26922 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); 26923 return field_ptr; 26924 }, 26925 .@"union" => { 26926 const inner_ptr = if (is_pointer_to) 26927 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 26928 else 26929 object_ptr; 26930 const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 26931 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); 26932 return field_ptr; 26933 }, 26934 else => {}, 26935 } 26936 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 26937 } 26938 26939 const ResolvedFieldCallee = union(enum) { 26940 /// The LHS of the call was an actual field with this value. 26941 direct: Air.Inst.Ref, 26942 /// This is a method call, with the function and first argument given. 26943 method: struct { 26944 func_inst: Air.Inst.Ref, 26945 arg0_inst: Air.Inst.Ref, 26946 }, 26947 }; 26948 26949 fn fieldCallBind( 26950 sema: *Sema, 26951 block: *Block, 26952 src: LazySrcLoc, 26953 raw_ptr: Air.Inst.Ref, 26954 field_name: InternPool.NullTerminatedString, 26955 field_name_src: LazySrcLoc, 26956 ) CompileError!ResolvedFieldCallee { 26957 // When editing this function, note that there is corresponding logic to be edited 26958 // in `fieldVal`. This function takes a pointer and returns a pointer. 26959 26960 const pt = sema.pt; 26961 const zcu = pt.zcu; 26962 const ip = &zcu.intern_pool; 26963 const raw_ptr_src = src; // TODO better source location 26964 const raw_ptr_ty = sema.typeOf(raw_ptr); 26965 const inner_ty = if (raw_ptr_ty.zigTypeTag(zcu) == .pointer and (raw_ptr_ty.ptrSize(zcu) == .one or raw_ptr_ty.ptrSize(zcu) == .c)) 26966 raw_ptr_ty.childType(zcu) 26967 else 26968 return sema.fail(block, raw_ptr_src, "expected single pointer, found '{f}'", .{raw_ptr_ty.fmt(pt)}); 26969 26970 // Optionally dereference a second pointer to get the concrete type. 26971 const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one; 26972 const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty; 26973 const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; 26974 const object_ptr = if (is_double_ptr) 26975 try sema.analyzeLoad(block, src, raw_ptr, src) 26976 else 26977 raw_ptr; 26978 26979 find_field: { 26980 switch (concrete_ty.zigTypeTag(zcu)) { 26981 .@"struct" => { 26982 try concrete_ty.resolveFields(pt); 26983 if (zcu.typeToStruct(concrete_ty)) |struct_type| { 26984 const field_index = struct_type.nameIndex(ip, field_name) orelse 26985 break :find_field; 26986 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); 26987 26988 return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr); 26989 } else if (concrete_ty.isTuple(zcu)) { 26990 if (field_name.eqlSlice("len", ip)) { 26991 return .{ .direct = try pt.intRef(.usize, concrete_ty.structFieldCount(zcu)) }; 26992 } 26993 if (field_name.toUnsigned(ip)) |field_index| { 26994 if (field_index >= concrete_ty.structFieldCount(zcu)) break :find_field; 26995 return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.fieldType(field_index, zcu), field_index, object_ptr); 26996 } 26997 } else { 26998 const max = concrete_ty.structFieldCount(zcu); 26999 for (0..max) |i_usize| { 27000 const i: u32 = @intCast(i_usize); 27001 if (field_name == concrete_ty.structFieldName(i, zcu).unwrap().?) { 27002 return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.fieldType(i, zcu), i, object_ptr); 27003 } 27004 } 27005 } 27006 }, 27007 .@"union" => { 27008 try concrete_ty.resolveFields(pt); 27009 const union_obj = zcu.typeToUnion(concrete_ty).?; 27010 _ = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse break :find_field; 27011 const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false); 27012 return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) }; 27013 }, 27014 .type => { 27015 const namespace = try sema.analyzeLoad(block, src, object_ptr, src); 27016 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) }; 27017 }, 27018 else => {}, 27019 } 27020 } 27021 27022 // If we get here, we need to look for a decl in the struct type instead. 27023 const found_nav = found_nav: { 27024 const namespace = concrete_ty.getNamespace(zcu).unwrap() orelse 27025 break :found_nav null; 27026 const nav_index = try sema.namespaceLookup(block, src, namespace, field_name) orelse 27027 break :found_nav null; 27028 27029 const decl_val = try sema.analyzeNavVal(block, src, nav_index); 27030 const decl_type = sema.typeOf(decl_val); 27031 if (zcu.typeToFunc(decl_type)) |func_type| f: { 27032 if (func_type.param_types.len == 0) break :f; 27033 27034 const first_param_type: Type = .fromInterned(func_type.param_types.get(ip)[0]); 27035 if (first_param_type.isGenericPoison() or 27036 (first_param_type.zigTypeTag(zcu) == .pointer and 27037 (first_param_type.ptrSize(zcu) == .one or 27038 first_param_type.ptrSize(zcu) == .c) and 27039 first_param_type.childType(zcu).eql(concrete_ty, zcu))) 27040 { 27041 // Note that if the param type is generic poison, we know that it must 27042 // specifically be `anytype` since it's the first parameter, meaning we 27043 // can safely assume it can be a pointer. 27044 // TODO: bound fn calls on rvalues should probably 27045 // generate a by-value argument somehow. 27046 return .{ .method = .{ 27047 .func_inst = decl_val, 27048 .arg0_inst = object_ptr, 27049 } }; 27050 } else if (first_param_type.eql(concrete_ty, zcu)) { 27051 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 27052 return .{ .method = .{ 27053 .func_inst = decl_val, 27054 .arg0_inst = deref, 27055 } }; 27056 } else if (first_param_type.zigTypeTag(zcu) == .optional) { 27057 const child = first_param_type.optionalChild(zcu); 27058 if (child.eql(concrete_ty, zcu)) { 27059 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 27060 return .{ .method = .{ 27061 .func_inst = decl_val, 27062 .arg0_inst = deref, 27063 } }; 27064 } else if (child.zigTypeTag(zcu) == .pointer and 27065 child.ptrSize(zcu) == .one and 27066 child.childType(zcu).eql(concrete_ty, zcu)) 27067 { 27068 return .{ .method = .{ 27069 .func_inst = decl_val, 27070 .arg0_inst = object_ptr, 27071 } }; 27072 } 27073 } else if (first_param_type.zigTypeTag(zcu) == .error_union and 27074 first_param_type.errorUnionPayload(zcu).eql(concrete_ty, zcu)) 27075 { 27076 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 27077 return .{ .method = .{ 27078 .func_inst = decl_val, 27079 .arg0_inst = deref, 27080 } }; 27081 } 27082 } 27083 break :found_nav nav_index; 27084 }; 27085 27086 const msg = msg: { 27087 const msg = try sema.errMsg(src, "no field or member function named '{f}' in '{f}'", .{ 27088 field_name.fmt(ip), 27089 concrete_ty.fmt(pt), 27090 }); 27091 errdefer msg.destroy(sema.gpa); 27092 try sema.addDeclaredHereNote(msg, concrete_ty); 27093 if (found_nav) |nav_index| { 27094 try sema.errNote( 27095 zcu.navSrcLoc(nav_index), 27096 msg, 27097 "'{f}' is not a member function", 27098 .{field_name.fmt(ip)}, 27099 ); 27100 } 27101 if (concrete_ty.zigTypeTag(zcu) == .error_union) { 27102 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); 27103 } 27104 if (is_double_ptr) { 27105 try sema.errNote(src, msg, "method invocation only supports up to one level of implicit pointer dereferencing", .{}); 27106 try sema.errNote(src, msg, "use '.*' to dereference pointer", .{}); 27107 } 27108 break :msg msg; 27109 }; 27110 return sema.failWithOwnedErrorMsg(block, msg); 27111 } 27112 27113 fn finishFieldCallBind( 27114 sema: *Sema, 27115 block: *Block, 27116 src: LazySrcLoc, 27117 ptr_ty: Type, 27118 field_ty: Type, 27119 field_index: u32, 27120 object_ptr: Air.Inst.Ref, 27121 ) CompileError!ResolvedFieldCallee { 27122 const pt = sema.pt; 27123 const zcu = pt.zcu; 27124 const ptr_field_ty = try pt.ptrTypeSema(.{ 27125 .child = field_ty.toIntern(), 27126 .flags = .{ 27127 .is_const = !ptr_ty.ptrIsMutable(zcu), 27128 .address_space = ptr_ty.ptrAddressSpace(zcu), 27129 }, 27130 }); 27131 27132 const container_ty = ptr_ty.childType(zcu); 27133 if (container_ty.zigTypeTag(zcu) == .@"struct") { 27134 if (container_ty.structFieldIsComptime(field_index, zcu)) { 27135 try container_ty.resolveStructFieldInits(pt); 27136 const default_val = (try container_ty.structFieldValueComptime(pt, field_index)).?; 27137 return .{ .direct = Air.internedToRef(default_val.toIntern()) }; 27138 } 27139 } 27140 27141 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| { 27142 const ptr_val = try struct_ptr_val.ptrField(field_index, pt); 27143 const pointer = Air.internedToRef(ptr_val.toIntern()); 27144 return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) }; 27145 } 27146 27147 try sema.requireRuntimeBlock(block, src, null); 27148 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); 27149 return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) }; 27150 } 27151 27152 fn namespaceLookup( 27153 sema: *Sema, 27154 block: *Block, 27155 src: LazySrcLoc, 27156 namespace: InternPool.NamespaceIndex, 27157 decl_name: InternPool.NullTerminatedString, 27158 ) CompileError!?InternPool.Nav.Index { 27159 const pt = sema.pt; 27160 const zcu = pt.zcu; 27161 const gpa = sema.gpa; 27162 if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| { 27163 if (!lookup.accessible) { 27164 return sema.failWithOwnedErrorMsg(block, msg: { 27165 const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{ 27166 decl_name.fmt(&zcu.intern_pool), 27167 }); 27168 errdefer msg.destroy(gpa); 27169 try sema.errNote(zcu.navSrcLoc(lookup.nav), msg, "declared here", .{}); 27170 break :msg msg; 27171 }); 27172 } 27173 return lookup.nav; 27174 } 27175 return null; 27176 } 27177 27178 fn namespaceLookupRef( 27179 sema: *Sema, 27180 block: *Block, 27181 src: LazySrcLoc, 27182 namespace: InternPool.NamespaceIndex, 27183 decl_name: InternPool.NullTerminatedString, 27184 ) CompileError!?Air.Inst.Ref { 27185 const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null; 27186 return try sema.analyzeNavRef(block, src, nav); 27187 } 27188 27189 fn namespaceLookupVal( 27190 sema: *Sema, 27191 block: *Block, 27192 src: LazySrcLoc, 27193 namespace: InternPool.NamespaceIndex, 27194 decl_name: InternPool.NullTerminatedString, 27195 ) CompileError!?Air.Inst.Ref { 27196 const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null; 27197 return try sema.analyzeNavVal(block, src, nav); 27198 } 27199 27200 fn structFieldPtr( 27201 sema: *Sema, 27202 block: *Block, 27203 src: LazySrcLoc, 27204 struct_ptr: Air.Inst.Ref, 27205 field_name: InternPool.NullTerminatedString, 27206 field_name_src: LazySrcLoc, 27207 struct_ty: Type, 27208 initializing: bool, 27209 ) CompileError!Air.Inst.Ref { 27210 const pt = sema.pt; 27211 const zcu = pt.zcu; 27212 const ip = &zcu.intern_pool; 27213 assert(struct_ty.zigTypeTag(zcu) == .@"struct"); 27214 27215 try struct_ty.resolveFields(pt); 27216 try struct_ty.resolveLayout(pt); 27217 27218 if (struct_ty.isTuple(zcu)) { 27219 if (field_name.eqlSlice("len", ip)) { 27220 const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu)); 27221 return sema.analyzeRef(block, src, len_inst); 27222 } 27223 const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); 27224 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); 27225 } 27226 27227 const struct_type = zcu.typeToStruct(struct_ty).?; 27228 27229 const field_index = struct_type.nameIndex(ip, field_name) orelse 27230 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); 27231 27232 return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty); 27233 } 27234 27235 fn structFieldPtrByIndex( 27236 sema: *Sema, 27237 block: *Block, 27238 src: LazySrcLoc, 27239 struct_ptr: Air.Inst.Ref, 27240 field_index: u32, 27241 struct_ty: Type, 27242 ) CompileError!Air.Inst.Ref { 27243 const pt = sema.pt; 27244 const zcu = pt.zcu; 27245 const ip = &zcu.intern_pool; 27246 27247 const struct_type = zcu.typeToStruct(struct_ty).?; 27248 const field_is_comptime = struct_type.fieldIsComptime(ip, field_index); 27249 27250 // Comptime fields are handled later 27251 if (!field_is_comptime) { 27252 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { 27253 const val = try struct_ptr_val.ptrField(field_index, pt); 27254 return Air.internedToRef(val.toIntern()); 27255 } 27256 } 27257 27258 const field_ty = struct_type.field_types.get(ip)[field_index]; 27259 const struct_ptr_ty = sema.typeOf(struct_ptr); 27260 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu); 27261 27262 var ptr_ty_data: InternPool.Key.PtrType = .{ 27263 .child = field_ty, 27264 .flags = .{ 27265 .is_const = struct_ptr_ty_info.flags.is_const, 27266 .is_volatile = struct_ptr_ty_info.flags.is_volatile, 27267 .address_space = struct_ptr_ty_info.flags.address_space, 27268 }, 27269 }; 27270 27271 const parent_align = if (struct_ptr_ty_info.flags.alignment != .none) 27272 struct_ptr_ty_info.flags.alignment 27273 else 27274 try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt); 27275 27276 if (struct_type.layout == .@"packed") { 27277 assert(!field_is_comptime); 27278 switch (struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt)) { 27279 .bit_ptr => |packed_offset| { 27280 ptr_ty_data.flags.alignment = parent_align; 27281 ptr_ty_data.packed_offset = packed_offset; 27282 }, 27283 .byte_ptr => |ptr_info| { 27284 ptr_ty_data.flags.alignment = ptr_info.alignment; 27285 }, 27286 } 27287 } else if (struct_type.layout == .@"extern") { 27288 assert(!field_is_comptime); 27289 // For extern structs, field alignment might be bigger than type's 27290 // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the 27291 // second field is aligned as u32. 27292 const field_offset = struct_ty.structFieldOffset(field_index, zcu); 27293 ptr_ty_data.flags.alignment = if (parent_align == .none) 27294 .none 27295 else 27296 @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset))); 27297 } else { 27298 // Our alignment is capped at the field alignment. 27299 const field_align = try Type.fromInterned(field_ty).structFieldAlignmentSema( 27300 struct_type.fieldAlign(ip, field_index), 27301 struct_type.layout, 27302 pt, 27303 ); 27304 ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none) 27305 field_align 27306 else 27307 field_align.min(parent_align); 27308 } 27309 27310 const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data); 27311 27312 if (field_is_comptime) { 27313 try struct_ty.resolveStructFieldInits(pt); 27314 const val = try pt.intern(.{ .ptr = .{ 27315 .ty = ptr_field_ty.toIntern(), 27316 .base_addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] }, 27317 .byte_offset = 0, 27318 } }); 27319 return Air.internedToRef(val); 27320 } 27321 27322 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); 27323 } 27324 27325 fn structFieldVal( 27326 sema: *Sema, 27327 block: *Block, 27328 struct_byval: Air.Inst.Ref, 27329 field_name: InternPool.NullTerminatedString, 27330 field_name_src: LazySrcLoc, 27331 struct_ty: Type, 27332 ) CompileError!Air.Inst.Ref { 27333 const pt = sema.pt; 27334 const zcu = pt.zcu; 27335 const ip = &zcu.intern_pool; 27336 assert(struct_ty.zigTypeTag(zcu) == .@"struct"); 27337 27338 try struct_ty.resolveFields(pt); 27339 27340 switch (ip.indexToKey(struct_ty.toIntern())) { 27341 .struct_type => { 27342 const struct_type = ip.loadStructType(struct_ty.toIntern()); 27343 27344 const field_index = struct_type.nameIndex(ip, field_name) orelse 27345 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); 27346 if (struct_type.fieldIsComptime(ip, field_index)) { 27347 try struct_ty.resolveStructFieldInits(pt); 27348 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]); 27349 } 27350 27351 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); 27352 if (try sema.typeHasOnePossibleValue(field_ty)) |field_val| 27353 return Air.internedToRef(field_val.toIntern()); 27354 27355 if (try sema.resolveValue(struct_byval)) |struct_val| { 27356 if (struct_val.isUndef(zcu)) return pt.undefRef(field_ty); 27357 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { 27358 return Air.internedToRef(opv.toIntern()); 27359 } 27360 return Air.internedToRef((try struct_val.fieldValue(pt, field_index)).toIntern()); 27361 } 27362 27363 try field_ty.resolveLayout(pt); 27364 return block.addStructFieldVal(struct_byval, field_index, field_ty); 27365 }, 27366 .tuple_type => { 27367 return sema.tupleFieldVal(block, struct_byval, field_name, field_name_src, struct_ty); 27368 }, 27369 else => unreachable, 27370 } 27371 } 27372 27373 fn tupleFieldVal( 27374 sema: *Sema, 27375 block: *Block, 27376 tuple_byval: Air.Inst.Ref, 27377 field_name: InternPool.NullTerminatedString, 27378 field_name_src: LazySrcLoc, 27379 tuple_ty: Type, 27380 ) CompileError!Air.Inst.Ref { 27381 const pt = sema.pt; 27382 const zcu = pt.zcu; 27383 if (field_name.eqlSlice("len", &zcu.intern_pool)) { 27384 return pt.intRef(.usize, tuple_ty.structFieldCount(zcu)); 27385 } 27386 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src); 27387 return sema.tupleFieldValByIndex(block, tuple_byval, field_index, tuple_ty); 27388 } 27389 27390 /// Asserts that `field_name` is not "len". 27391 fn tupleFieldIndex( 27392 sema: *Sema, 27393 block: *Block, 27394 tuple_ty: Type, 27395 field_name: InternPool.NullTerminatedString, 27396 field_name_src: LazySrcLoc, 27397 ) CompileError!u32 { 27398 const pt = sema.pt; 27399 const ip = &pt.zcu.intern_pool; 27400 assert(!field_name.eqlSlice("len", ip)); 27401 if (field_name.toUnsigned(ip)) |field_index| { 27402 if (field_index < tuple_ty.structFieldCount(pt.zcu)) return field_index; 27403 return sema.fail(block, field_name_src, "index '{f}' out of bounds of tuple '{f}'", .{ 27404 field_name.fmt(ip), tuple_ty.fmt(pt), 27405 }); 27406 } 27407 27408 return sema.fail(block, field_name_src, "no field named '{f}' in tuple '{f}'", .{ 27409 field_name.fmt(ip), tuple_ty.fmt(pt), 27410 }); 27411 } 27412 27413 fn tupleFieldValByIndex( 27414 sema: *Sema, 27415 block: *Block, 27416 tuple_byval: Air.Inst.Ref, 27417 field_index: u32, 27418 tuple_ty: Type, 27419 ) CompileError!Air.Inst.Ref { 27420 const pt = sema.pt; 27421 const zcu = pt.zcu; 27422 const field_ty = tuple_ty.fieldType(field_index, zcu); 27423 27424 if (tuple_ty.structFieldIsComptime(field_index, zcu)) 27425 try tuple_ty.resolveStructFieldInits(pt); 27426 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| { 27427 return Air.internedToRef(default_value.toIntern()); 27428 } 27429 27430 if (try sema.resolveValue(tuple_byval)) |tuple_val| { 27431 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { 27432 return Air.internedToRef(opv.toIntern()); 27433 } 27434 return switch (zcu.intern_pool.indexToKey(tuple_val.toIntern())) { 27435 .undef => pt.undefRef(field_ty), 27436 .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) { 27437 .bytes => |bytes| try pt.intValue(.u8, bytes.at(field_index, &zcu.intern_pool)), 27438 .elems => |elems| Value.fromInterned(elems[field_index]), 27439 .repeated_elem => |elem| Value.fromInterned(elem), 27440 }.toIntern()), 27441 else => unreachable, 27442 }; 27443 } 27444 27445 try field_ty.resolveLayout(pt); 27446 return block.addStructFieldVal(tuple_byval, field_index, field_ty); 27447 } 27448 27449 fn unionFieldPtr( 27450 sema: *Sema, 27451 block: *Block, 27452 src: LazySrcLoc, 27453 union_ptr: Air.Inst.Ref, 27454 field_name: InternPool.NullTerminatedString, 27455 field_name_src: LazySrcLoc, 27456 union_ty: Type, 27457 initializing: bool, 27458 ) CompileError!Air.Inst.Ref { 27459 const pt = sema.pt; 27460 const zcu = pt.zcu; 27461 const ip = &zcu.intern_pool; 27462 27463 assert(union_ty.zigTypeTag(zcu) == .@"union"); 27464 27465 const union_ptr_ty = sema.typeOf(union_ptr); 27466 const union_ptr_info = union_ptr_ty.ptrInfo(zcu); 27467 try union_ty.resolveFields(pt); 27468 const union_obj = zcu.typeToUnion(union_ty).?; 27469 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 27470 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 27471 const ptr_field_ty = try pt.ptrTypeSema(.{ 27472 .child = field_ty.toIntern(), 27473 .flags = .{ 27474 .is_const = union_ptr_info.flags.is_const, 27475 .is_volatile = union_ptr_info.flags.is_volatile, 27476 .address_space = union_ptr_info.flags.address_space, 27477 .alignment = if (union_obj.flagsUnordered(ip).layout == .auto) blk: { 27478 const union_align = if (union_ptr_info.flags.alignment != .none) 27479 union_ptr_info.flags.alignment 27480 else 27481 try union_ty.abiAlignmentSema(pt); 27482 const field_align = try union_ty.fieldAlignmentSema(field_index, pt); 27483 break :blk union_align.min(field_align); 27484 } else union_ptr_info.flags.alignment, 27485 }, 27486 .packed_offset = union_ptr_info.packed_offset, 27487 }); 27488 const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?); 27489 27490 if (initializing and field_ty.zigTypeTag(zcu) == .noreturn) { 27491 const msg = msg: { 27492 const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{}); 27493 errdefer msg.destroy(sema.gpa); 27494 27495 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ 27496 field_name.fmt(ip), 27497 }); 27498 try sema.addDeclaredHereNote(msg, union_ty); 27499 break :msg msg; 27500 }; 27501 return sema.failWithOwnedErrorMsg(block, msg); 27502 } 27503 27504 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { 27505 switch (union_obj.flagsUnordered(ip).layout) { 27506 .auto => if (initializing) { 27507 if (!sema.isComptimeMutablePtr(union_ptr_val)) { 27508 // The initialization is a runtime operation. 27509 break :ct; 27510 } 27511 // Store to the union to initialize the tag. 27512 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27513 const payload_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 27514 const new_union_val = try pt.unionValue(union_ty, field_tag, try pt.undefValue(payload_ty)); 27515 try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty); 27516 } else { 27517 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse 27518 break :ct; 27519 if (union_val.isUndef(zcu)) { 27520 return sema.failWithUseOfUndef(block, src); 27521 } 27522 const un = ip.indexToKey(union_val.toIntern()).un; 27523 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27524 const tag_matches = un.tag == field_tag.toIntern(); 27525 if (!tag_matches) { 27526 const msg = msg: { 27527 const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?; 27528 const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu); 27529 const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ 27530 field_name.fmt(ip), 27531 active_field_name.fmt(ip), 27532 }); 27533 errdefer msg.destroy(sema.gpa); 27534 try sema.addDeclaredHereNote(msg, union_ty); 27535 break :msg msg; 27536 }; 27537 return sema.failWithOwnedErrorMsg(block, msg); 27538 } 27539 }, 27540 .@"packed", .@"extern" => {}, 27541 } 27542 const field_ptr_val = try union_ptr_val.ptrField(field_index, pt); 27543 return Air.internedToRef(field_ptr_val.toIntern()); 27544 } 27545 27546 // If the union has a tag, we must either set or or safety check it depending on `initializing`. 27547 tag: { 27548 if (union_ty.containerLayout(zcu) != .auto) break :tag; 27549 const tag_ty: Type = .fromInterned(union_obj.enum_tag_ty); 27550 if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag; 27551 // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but 27552 // only emit a safety check if it's available at runtime (i.e. it's safety-tagged). 27553 const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index); 27554 if (initializing) { 27555 const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag)); 27556 try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store 27557 } else if (block.wantSafety() and union_obj.hasTag(ip)) { 27558 // The tag exists at runtime (safety tag), so emit a safety check. 27559 // TODO would it be better if get_union_tag supported pointers to unions? 27560 const union_val = try block.addTyOp(.load, union_ty, union_ptr); 27561 const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val); 27562 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag)); 27563 } 27564 } 27565 if (field_ty.zigTypeTag(zcu) == .noreturn) { 27566 _ = try block.addNoOp(.unreach); 27567 return .unreachable_value; 27568 } 27569 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); 27570 } 27571 27572 fn unionFieldVal( 27573 sema: *Sema, 27574 block: *Block, 27575 src: LazySrcLoc, 27576 union_byval: Air.Inst.Ref, 27577 field_name: InternPool.NullTerminatedString, 27578 field_name_src: LazySrcLoc, 27579 union_ty: Type, 27580 ) CompileError!Air.Inst.Ref { 27581 const pt = sema.pt; 27582 const zcu = pt.zcu; 27583 const ip = &zcu.intern_pool; 27584 assert(union_ty.zigTypeTag(zcu) == .@"union"); 27585 27586 try union_ty.resolveFields(pt); 27587 const union_obj = zcu.typeToUnion(union_ty).?; 27588 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 27589 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 27590 const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?); 27591 27592 if (try sema.resolveValue(union_byval)) |union_val| { 27593 if (union_val.isUndef(zcu)) return pt.undefRef(field_ty); 27594 27595 const un = ip.indexToKey(union_val.toIntern()).un; 27596 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27597 const tag_matches = un.tag == field_tag.toIntern(); 27598 switch (union_obj.flagsUnordered(ip).layout) { 27599 .auto => { 27600 if (tag_matches) { 27601 return Air.internedToRef(un.val); 27602 } else { 27603 const msg = msg: { 27604 const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?; 27605 const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu); 27606 const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ 27607 field_name.fmt(ip), active_field_name.fmt(ip), 27608 }); 27609 errdefer msg.destroy(sema.gpa); 27610 try sema.addDeclaredHereNote(msg, union_ty); 27611 break :msg msg; 27612 }; 27613 return sema.failWithOwnedErrorMsg(block, msg); 27614 } 27615 }, 27616 .@"extern" => if (tag_matches) { 27617 // Fast path - no need to use bitcast logic. 27618 return Air.internedToRef(un.val); 27619 } else if (try sema.bitCastVal(union_val, field_ty, 0, 0, 0)) |field_val| { 27620 return Air.internedToRef(field_val.toIntern()); 27621 }, 27622 .@"packed" => if (tag_matches) { 27623 // Fast path - no need to use bitcast logic. 27624 return Air.internedToRef(un.val); 27625 } else if (try sema.bitCastVal(union_val, field_ty, 0, try union_ty.bitSizeSema(pt), 0)) |field_val| { 27626 return Air.internedToRef(field_val.toIntern()); 27627 }, 27628 } 27629 } 27630 27631 if (union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and 27632 union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1) 27633 { 27634 const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27635 const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); 27636 const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_byval); 27637 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag); 27638 } 27639 27640 if (field_ty.zigTypeTag(zcu) == .noreturn) { 27641 _ = try block.addNoOp(.unreach); 27642 return .unreachable_value; 27643 } 27644 27645 if (try sema.typeHasOnePossibleValue(field_ty)) |field_only_value| { 27646 return Air.internedToRef(field_only_value.toIntern()); 27647 } 27648 27649 try field_ty.resolveLayout(pt); 27650 return block.addStructFieldVal(union_byval, field_index, field_ty); 27651 } 27652 27653 fn elemPtr( 27654 sema: *Sema, 27655 block: *Block, 27656 src: LazySrcLoc, 27657 indexable_ptr: Air.Inst.Ref, 27658 elem_index: Air.Inst.Ref, 27659 elem_index_src: LazySrcLoc, 27660 init: bool, 27661 oob_safety: bool, 27662 ) CompileError!Air.Inst.Ref { 27663 const pt = sema.pt; 27664 const zcu = pt.zcu; 27665 const indexable_ptr_src = src; // TODO better source location 27666 const indexable_ptr_ty = sema.typeOf(indexable_ptr); 27667 27668 const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(zcu)) { 27669 .pointer => indexable_ptr_ty.childType(zcu), 27670 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}), 27671 }; 27672 try sema.checkIndexable(block, src, indexable_ty); 27673 27674 const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) { 27675 .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), 27676 .@"struct" => blk: { 27677 // Tuple field access. 27678 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); 27679 const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); 27680 break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); 27681 }, 27682 else => { 27683 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); 27684 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); 27685 }, 27686 }; 27687 27688 try sema.checkKnownAllocPtr(block, indexable_ptr, elem_ptr); 27689 return elem_ptr; 27690 } 27691 27692 /// Asserts that the type of indexable is pointer. 27693 fn elemPtrOneLayerOnly( 27694 sema: *Sema, 27695 block: *Block, 27696 src: LazySrcLoc, 27697 indexable: Air.Inst.Ref, 27698 elem_index: Air.Inst.Ref, 27699 elem_index_src: LazySrcLoc, 27700 init: bool, 27701 oob_safety: bool, 27702 ) CompileError!Air.Inst.Ref { 27703 const indexable_src = src; // TODO better source location 27704 const indexable_ty = sema.typeOf(indexable); 27705 const pt = sema.pt; 27706 const zcu = pt.zcu; 27707 27708 try sema.checkIndexable(block, src, indexable_ty); 27709 27710 switch (indexable_ty.ptrSize(zcu)) { 27711 .slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27712 .many, .c => { 27713 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 27714 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 27715 ct: { 27716 const ptr_val = maybe_ptr_val orelse break :ct; 27717 const index_val = maybe_index_val orelse break :ct; 27718 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 27719 const elem_ptr = try ptr_val.ptrElem(index, pt); 27720 return Air.internedToRef(elem_ptr.toIntern()); 27721 } 27722 27723 try sema.checkLogicalPtrOperation(block, src, indexable_ty); 27724 const result_ty = try indexable_ty.elemPtrType(null, pt); 27725 27726 try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_ty, indexable_src); 27727 try sema.validateRuntimeValue(block, indexable_src, indexable); 27728 27729 if (!try result_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) { 27730 // zero-bit child type; just bitcast the pointer 27731 return block.addBitCast(result_ty, indexable); 27732 } 27733 27734 return block.addPtrElemPtr(indexable, elem_index, result_ty); 27735 }, 27736 .one => { 27737 const child_ty = indexable_ty.childType(zcu); 27738 const elem_ptr = switch (child_ty.zigTypeTag(zcu)) { 27739 .array, .vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety), 27740 .@"struct" => blk: { 27741 assert(child_ty.isTuple(zcu)); 27742 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); 27743 const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); 27744 break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); 27745 }, 27746 else => unreachable, // Guaranteed by checkIndexable 27747 }; 27748 try sema.checkKnownAllocPtr(block, indexable, elem_ptr); 27749 return elem_ptr; 27750 }, 27751 } 27752 } 27753 27754 fn elemVal( 27755 sema: *Sema, 27756 block: *Block, 27757 src: LazySrcLoc, 27758 indexable: Air.Inst.Ref, 27759 elem_index_uncasted: Air.Inst.Ref, 27760 elem_index_src: LazySrcLoc, 27761 oob_safety: bool, 27762 ) CompileError!Air.Inst.Ref { 27763 const indexable_src = src; // TODO better source location 27764 const indexable_ty = sema.typeOf(indexable); 27765 const pt = sema.pt; 27766 const zcu = pt.zcu; 27767 27768 try sema.checkIndexable(block, src, indexable_ty); 27769 27770 // TODO in case of a vector of pointers, we need to detect whether the element 27771 // index is a scalar or vector instead of unconditionally casting to usize. 27772 const elem_index = try sema.coerce(block, .usize, elem_index_uncasted, elem_index_src); 27773 27774 switch (indexable_ty.zigTypeTag(zcu)) { 27775 .pointer => switch (indexable_ty.ptrSize(zcu)) { 27776 .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27777 .many, .c => { 27778 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 27779 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 27780 const elem_ty = indexable_ty.elemType2(zcu); 27781 27782 ct: { 27783 const indexable_val = maybe_indexable_val orelse break :ct; 27784 const index_val = maybe_index_val orelse break :ct; 27785 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 27786 const many_ptr_ty = try pt.manyConstPtrType(elem_ty); 27787 const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty); 27788 const elem_ptr_ty = try pt.singleConstPtrType(elem_ty); 27789 const elem_ptr_val = try many_ptr_val.ptrElem(index, pt); 27790 const elem_val = try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty) orelse break :ct; 27791 return Air.internedToRef((try pt.getCoerced(elem_val, elem_ty)).toIntern()); 27792 } 27793 27794 if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| { 27795 return Air.internedToRef(elem_only_value.toIntern()); 27796 } 27797 27798 try sema.checkLogicalPtrOperation(block, src, indexable_ty); 27799 return block.addBinOp(.ptr_elem_val, indexable, elem_index); 27800 }, 27801 .one => { 27802 arr_sent: { 27803 const inner_ty = indexable_ty.childType(zcu); 27804 if (inner_ty.zigTypeTag(zcu) != .array) break :arr_sent; 27805 const sentinel = inner_ty.sentinel(zcu) orelse break :arr_sent; 27806 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; 27807 const index = try sema.usizeCast(block, src, try index_val.toUnsignedIntSema(pt)); 27808 if (index != inner_ty.arrayLen(zcu)) break :arr_sent; 27809 return Air.internedToRef(sentinel.toIntern()); 27810 } 27811 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); 27812 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); 27813 }, 27814 }, 27815 .array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27816 .vector => { 27817 // TODO: If the index is a vector, the result should be a vector. 27818 return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety); 27819 }, 27820 .@"struct" => { 27821 // Tuple field access. 27822 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); 27823 const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt)); 27824 return sema.tupleField(block, indexable_src, indexable, elem_index_src, index); 27825 }, 27826 else => unreachable, 27827 } 27828 } 27829 27830 fn validateRuntimeElemAccess( 27831 sema: *Sema, 27832 block: *Block, 27833 elem_index_src: LazySrcLoc, 27834 elem_ty: Type, 27835 parent_ty: Type, 27836 parent_src: LazySrcLoc, 27837 ) CompileError!void { 27838 const pt = sema.pt; 27839 const zcu = pt.zcu; 27840 27841 if (try elem_ty.comptimeOnlySema(sema.pt)) { 27842 const msg = msg: { 27843 const msg = try sema.errMsg( 27844 elem_index_src, 27845 "values of type '{f}' must be comptime-known, but index value is runtime-known", 27846 .{parent_ty.fmt(sema.pt)}, 27847 ); 27848 errdefer msg.destroy(sema.gpa); 27849 27850 try sema.explainWhyTypeIsComptime(msg, parent_src, parent_ty); 27851 27852 break :msg msg; 27853 }; 27854 return sema.failWithOwnedErrorMsg(block, msg); 27855 } 27856 27857 if (zcu.intern_pool.indexToKey(parent_ty.toIntern()) == .ptr_type) { 27858 const target = zcu.getTarget(); 27859 const as = parent_ty.ptrAddressSpace(zcu); 27860 if (target_util.arePointersLogical(target, as)) { 27861 return sema.fail(block, elem_index_src, "cannot access element of logical pointer '{f}'", .{parent_ty.fmt(pt)}); 27862 } 27863 } 27864 } 27865 27866 fn tupleFieldPtr( 27867 sema: *Sema, 27868 block: *Block, 27869 tuple_ptr_src: LazySrcLoc, 27870 tuple_ptr: Air.Inst.Ref, 27871 field_index_src: LazySrcLoc, 27872 field_index: u32, 27873 init: bool, 27874 ) CompileError!Air.Inst.Ref { 27875 const pt = sema.pt; 27876 const zcu = pt.zcu; 27877 const tuple_ptr_ty = sema.typeOf(tuple_ptr); 27878 const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu); 27879 const tuple_ty: Type = .fromInterned(tuple_ptr_info.child); 27880 try tuple_ty.resolveFields(pt); 27881 const field_count = tuple_ty.structFieldCount(zcu); 27882 27883 if (field_count == 0) { 27884 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); 27885 } 27886 27887 if (field_index >= field_count) { 27888 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 27889 field_index, field_count, 27890 }); 27891 } 27892 27893 const field_ty = tuple_ty.fieldType(field_index, zcu); 27894 const ptr_field_ty = try pt.ptrTypeSema(.{ 27895 .child = field_ty.toIntern(), 27896 .flags = .{ 27897 .is_const = tuple_ptr_info.flags.is_const, 27898 .is_volatile = tuple_ptr_info.flags.is_volatile, 27899 .address_space = tuple_ptr_info.flags.address_space, 27900 .alignment = a: { 27901 if (tuple_ptr_info.flags.alignment == .none) break :a .none; 27902 // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned. 27903 const tuple_align = tuple_ptr_info.flags.alignment; 27904 const field_align = try field_ty.abiAlignmentSema(pt); 27905 break :a tuple_align.min(field_align); 27906 }, 27907 }, 27908 }); 27909 27910 if (tuple_ty.structFieldIsComptime(field_index, zcu)) 27911 try tuple_ty.resolveStructFieldInits(pt); 27912 27913 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_val| { 27914 return Air.internedToRef((try pt.intern(.{ .ptr = .{ 27915 .ty = ptr_field_ty.toIntern(), 27916 .base_addr = .{ .comptime_field = default_val.toIntern() }, 27917 .byte_offset = 0, 27918 } }))); 27919 } 27920 27921 if (try sema.resolveValue(tuple_ptr)) |tuple_ptr_val| { 27922 const field_ptr_val = try tuple_ptr_val.ptrField(field_index, pt); 27923 return Air.internedToRef(field_ptr_val.toIntern()); 27924 } 27925 27926 if (!init) { 27927 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src); 27928 } 27929 27930 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); 27931 } 27932 27933 fn tupleField( 27934 sema: *Sema, 27935 block: *Block, 27936 tuple_src: LazySrcLoc, 27937 tuple: Air.Inst.Ref, 27938 field_index_src: LazySrcLoc, 27939 field_index: u32, 27940 ) CompileError!Air.Inst.Ref { 27941 const pt = sema.pt; 27942 const zcu = pt.zcu; 27943 const tuple_ty = sema.typeOf(tuple); 27944 try tuple_ty.resolveFields(pt); 27945 const field_count = tuple_ty.structFieldCount(zcu); 27946 27947 if (field_count == 0) { 27948 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); 27949 } 27950 27951 if (field_index >= field_count) { 27952 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 27953 field_index, field_count, 27954 }); 27955 } 27956 27957 const field_ty = tuple_ty.fieldType(field_index, zcu); 27958 27959 if (tuple_ty.structFieldIsComptime(field_index, zcu)) 27960 try tuple_ty.resolveStructFieldInits(pt); 27961 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| { 27962 return Air.internedToRef(default_value.toIntern()); // comptime field 27963 } 27964 27965 if (try sema.resolveValue(tuple)) |tuple_val| { 27966 if (tuple_val.isUndef(zcu)) return pt.undefRef(field_ty); 27967 return Air.internedToRef((try tuple_val.fieldValue(pt, field_index)).toIntern()); 27968 } 27969 27970 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); 27971 27972 try field_ty.resolveLayout(pt); 27973 return block.addStructFieldVal(tuple, field_index, field_ty); 27974 } 27975 27976 fn elemValArray( 27977 sema: *Sema, 27978 block: *Block, 27979 src: LazySrcLoc, 27980 array_src: LazySrcLoc, 27981 array: Air.Inst.Ref, 27982 elem_index_src: LazySrcLoc, 27983 elem_index: Air.Inst.Ref, 27984 oob_safety: bool, 27985 ) CompileError!Air.Inst.Ref { 27986 const pt = sema.pt; 27987 const zcu = pt.zcu; 27988 const array_ty = sema.typeOf(array); 27989 const array_sent = array_ty.sentinel(zcu); 27990 const array_len = array_ty.arrayLen(zcu); 27991 const array_len_s = array_len + @intFromBool(array_sent != null); 27992 const elem_ty = array_ty.childType(zcu); 27993 27994 if (array_len_s == 0) { 27995 return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); 27996 } 27997 27998 const maybe_undef_array_val = try sema.resolveValue(array); 27999 // index must be defined since it can access out of bounds 28000 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 28001 28002 if (maybe_index_val) |index_val| { 28003 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 28004 if (array_sent) |s| { 28005 if (index == array_len) { 28006 return Air.internedToRef(s.toIntern()); 28007 } 28008 } 28009 if (index >= array_len_s) { 28010 const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else ""; 28011 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 28012 } 28013 } 28014 if (maybe_undef_array_val) |array_val| { 28015 if (array_val.isUndef(zcu)) { 28016 return pt.undefRef(elem_ty); 28017 } 28018 if (maybe_index_val) |index_val| { 28019 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 28020 const elem_val = try array_val.elemValue(pt, index); 28021 return Air.internedToRef(elem_val.toIntern()); 28022 } 28023 } 28024 28025 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src); 28026 try sema.validateRuntimeValue(block, array_src, array); 28027 28028 if (oob_safety and block.wantSafety()) { 28029 // Runtime check is only needed if unable to comptime check. 28030 if (maybe_index_val == null) { 28031 const len_inst = try pt.intRef(.usize, array_len); 28032 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt; 28033 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); 28034 } 28035 } 28036 28037 if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_val| 28038 return Air.internedToRef(elem_val.toIntern()); 28039 28040 return block.addBinOp(.array_elem_val, array, elem_index); 28041 } 28042 28043 fn elemPtrArray( 28044 sema: *Sema, 28045 block: *Block, 28046 src: LazySrcLoc, 28047 array_ptr_src: LazySrcLoc, 28048 array_ptr: Air.Inst.Ref, 28049 elem_index_src: LazySrcLoc, 28050 elem_index: Air.Inst.Ref, 28051 init: bool, 28052 oob_safety: bool, 28053 ) CompileError!Air.Inst.Ref { 28054 const pt = sema.pt; 28055 const zcu = pt.zcu; 28056 const array_ptr_ty = sema.typeOf(array_ptr); 28057 const array_ty = array_ptr_ty.childType(zcu); 28058 const array_sent = array_ty.sentinel(zcu) != null; 28059 const array_len = array_ty.arrayLen(zcu); 28060 const array_len_s = array_len + @intFromBool(array_sent); 28061 28062 if (array_len_s == 0) { 28063 return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{}); 28064 } 28065 28066 const maybe_undef_array_ptr_val = try sema.resolveValue(array_ptr); 28067 // The index must not be undefined since it can be out of bounds. 28068 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 28069 const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt)); 28070 if (index >= array_len_s) { 28071 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; 28072 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 28073 } 28074 break :o index; 28075 } else null; 28076 28077 const elem_ptr_ty = try array_ptr_ty.elemPtrType(offset, pt); 28078 28079 if (maybe_undef_array_ptr_val) |array_ptr_val| { 28080 if (array_ptr_val.isUndef(zcu)) { 28081 return pt.undefRef(elem_ptr_ty); 28082 } 28083 if (offset) |index| { 28084 const elem_ptr = try array_ptr_val.ptrElem(index, pt); 28085 return Air.internedToRef(elem_ptr.toIntern()); 28086 } 28087 } 28088 28089 if (!init) { 28090 try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(zcu), array_ty, array_ptr_src); 28091 try sema.validateRuntimeValue(block, array_ptr_src, array_ptr); 28092 } 28093 28094 // Runtime check is only needed if unable to comptime check. 28095 if (oob_safety and block.wantSafety() and offset == null) { 28096 const len_inst = try pt.intRef(.usize, array_len); 28097 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; 28098 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); 28099 } 28100 28101 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); 28102 } 28103 28104 fn elemValSlice( 28105 sema: *Sema, 28106 block: *Block, 28107 src: LazySrcLoc, 28108 slice_src: LazySrcLoc, 28109 slice: Air.Inst.Ref, 28110 elem_index_src: LazySrcLoc, 28111 elem_index: Air.Inst.Ref, 28112 oob_safety: bool, 28113 ) CompileError!Air.Inst.Ref { 28114 const pt = sema.pt; 28115 const zcu = pt.zcu; 28116 const slice_ty = sema.typeOf(slice); 28117 const slice_sent = slice_ty.sentinel(zcu) != null; 28118 const elem_ty = slice_ty.elemType2(zcu); 28119 var runtime_src = slice_src; 28120 28121 // slice must be defined since it can dereferenced as null 28122 const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); 28123 // index must be defined since it can index out of bounds 28124 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 28125 28126 if (maybe_slice_val) |slice_val| { 28127 runtime_src = elem_index_src; 28128 const slice_len = try slice_val.sliceLen(pt); 28129 const slice_len_s = slice_len + @intFromBool(slice_sent); 28130 if (slice_len_s == 0) { 28131 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 28132 } 28133 if (maybe_index_val) |index_val| { 28134 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt)); 28135 if (index >= slice_len_s) { 28136 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 28137 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 28138 } 28139 const elem_ptr_ty = try slice_ty.elemPtrType(index, pt); 28140 const elem_ptr_val = try slice_val.ptrElem(index, pt); 28141 if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { 28142 return Air.internedToRef(elem_val.toIntern()); 28143 } 28144 runtime_src = slice_src; 28145 } 28146 } 28147 28148 if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| { 28149 return Air.internedToRef(elem_only_value.toIntern()); 28150 } 28151 28152 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src); 28153 try sema.validateRuntimeValue(block, slice_src, slice); 28154 28155 if (oob_safety and block.wantSafety()) { 28156 const len_inst = if (maybe_slice_val) |slice_val| 28157 try pt.intRef(.usize, try slice_val.sliceLen(pt)) 28158 else 28159 try block.addTyOp(.slice_len, .usize, slice); 28160 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 28161 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); 28162 } 28163 return block.addBinOp(.slice_elem_val, slice, elem_index); 28164 } 28165 28166 fn elemPtrSlice( 28167 sema: *Sema, 28168 block: *Block, 28169 src: LazySrcLoc, 28170 slice_src: LazySrcLoc, 28171 slice: Air.Inst.Ref, 28172 elem_index_src: LazySrcLoc, 28173 elem_index: Air.Inst.Ref, 28174 oob_safety: bool, 28175 ) CompileError!Air.Inst.Ref { 28176 const pt = sema.pt; 28177 const zcu = pt.zcu; 28178 const slice_ty = sema.typeOf(slice); 28179 const slice_sent = slice_ty.sentinel(zcu) != null; 28180 28181 const maybe_undef_slice_val = try sema.resolveValue(slice); 28182 // The index must not be undefined since it can be out of bounds. 28183 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 28184 const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt)); 28185 break :o index; 28186 } else null; 28187 28188 const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt); 28189 28190 if (maybe_undef_slice_val) |slice_val| { 28191 if (slice_val.isUndef(zcu)) { 28192 return pt.undefRef(elem_ptr_ty); 28193 } 28194 const slice_len = try slice_val.sliceLen(pt); 28195 const slice_len_s = slice_len + @intFromBool(slice_sent); 28196 if (slice_len_s == 0) { 28197 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 28198 } 28199 if (offset) |index| { 28200 if (index >= slice_len_s) { 28201 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 28202 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 28203 } 28204 const elem_ptr_val = try slice_val.ptrElem(index, pt); 28205 return Air.internedToRef(elem_ptr_val.toIntern()); 28206 } 28207 } 28208 28209 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src); 28210 try sema.validateRuntimeValue(block, slice_src, slice); 28211 28212 if (oob_safety and block.wantSafety()) { 28213 const len_inst = len: { 28214 if (maybe_undef_slice_val) |slice_val| 28215 if (!slice_val.isUndef(zcu)) 28216 break :len try pt.intRef(.usize, try slice_val.sliceLen(pt)); 28217 break :len try block.addTyOp(.slice_len, .usize, slice); 28218 }; 28219 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 28220 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); 28221 } 28222 if (!try slice_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) { 28223 // zero-bit child type; just extract the pointer and bitcast it 28224 const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice); 28225 return block.addBitCast(elem_ptr_ty, slice_ptr); 28226 } 28227 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); 28228 } 28229 28230 pub fn coerce( 28231 sema: *Sema, 28232 block: *Block, 28233 dest_ty_unresolved: Type, 28234 inst: Air.Inst.Ref, 28235 inst_src: LazySrcLoc, 28236 ) CompileError!Air.Inst.Ref { 28237 return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) { 28238 error.NotCoercible => unreachable, 28239 else => |e| return e, 28240 }; 28241 } 28242 28243 const CoersionError = CompileError || error{ 28244 /// When coerce is called recursively, this error should be returned instead of using `fail` 28245 /// to ensure correct types in compile errors. 28246 NotCoercible, 28247 }; 28248 28249 const CoerceOpts = struct { 28250 /// Should coerceExtra emit error messages. 28251 report_err: bool = true, 28252 /// Ignored if `report_err == false`. 28253 is_ret: bool = false, 28254 /// Should coercion to comptime_int emit an error message. 28255 no_cast_to_comptime_int: bool = false, 28256 28257 param_src: struct { 28258 func_inst: Air.Inst.Ref = .none, 28259 param_i: u32 = undefined, 28260 28261 fn get(info: @This(), sema: *Sema) !?LazySrcLoc { 28262 if (info.func_inst == .none) return null; 28263 const func_inst = try sema.funcDeclSrcInst(info.func_inst) orelse return null; 28264 return .{ 28265 .base_node_inst = func_inst, 28266 .offset = .{ .fn_proto_param_type = .{ 28267 .fn_proto_node_offset = .zero, 28268 .param_index = info.param_i, 28269 } }, 28270 }; 28271 } 28272 } = .{ .func_inst = .none, .param_i = undefined }, 28273 }; 28274 28275 fn coerceExtra( 28276 sema: *Sema, 28277 block: *Block, 28278 dest_ty: Type, 28279 inst: Air.Inst.Ref, 28280 inst_src: LazySrcLoc, 28281 opts: CoerceOpts, 28282 ) CoersionError!Air.Inst.Ref { 28283 if (dest_ty.isGenericPoison()) return inst; 28284 const pt = sema.pt; 28285 const zcu = pt.zcu; 28286 const ip = &zcu.intern_pool; 28287 const dest_ty_src = inst_src; // TODO better source location 28288 try dest_ty.resolveFields(pt); 28289 const inst_ty = sema.typeOf(inst); 28290 try inst_ty.resolveFields(pt); 28291 const target = zcu.getTarget(); 28292 // If the types are the same, we can return the operand. 28293 if (dest_ty.eql(inst_ty, zcu)) 28294 return inst; 28295 28296 const maybe_inst_val = try sema.resolveValue(inst); 28297 28298 var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); 28299 if (in_memory_result == .ok) { 28300 if (maybe_inst_val) |val| { 28301 return sema.coerceInMemory(val, dest_ty); 28302 } 28303 try sema.requireRuntimeBlock(block, inst_src, null); 28304 const new_val = try block.addBitCast(dest_ty, inst); 28305 try sema.checkKnownAllocPtr(block, inst, new_val); 28306 return new_val; 28307 } 28308 28309 switch (dest_ty.zigTypeTag(zcu)) { 28310 .optional => optional: { 28311 if (maybe_inst_val) |val| { 28312 // undefined sets the optional bit also to undefined. 28313 if (val.toIntern() == .undef) { 28314 return pt.undefRef(dest_ty); 28315 } 28316 28317 // null to ?T 28318 if (val.toIntern() == .null_value) { 28319 return Air.internedToRef((try pt.intern(.{ .opt = .{ 28320 .ty = dest_ty.toIntern(), 28321 .val = .none, 28322 } }))); 28323 } 28324 } 28325 28326 // cast from ?*T and ?[*]T to ?*anyopaque 28327 // but don't do it if the source type is a double pointer 28328 if (dest_ty.isPtrLikeOptional(zcu) and 28329 dest_ty.elemType2(zcu).toIntern() == .anyopaque_type and 28330 inst_ty.isPtrAtRuntime(zcu)) 28331 anyopaque_check: { 28332 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; 28333 const elem_ty = inst_ty.elemType2(zcu); 28334 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) { 28335 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 28336 .actual = inst_ty, 28337 .wanted = dest_ty, 28338 } }; 28339 break :optional; 28340 } 28341 // Let the logic below handle wrapping the optional now that 28342 // it has been checked to correctly coerce. 28343 if (!inst_ty.isPtrLikeOptional(zcu)) break :anyopaque_check; 28344 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28345 } 28346 28347 // T to ?T 28348 const child_type = dest_ty.optionalChild(zcu); 28349 const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 28350 error.NotCoercible => { 28351 if (in_memory_result == .no_match) { 28352 // Try to give more useful notes 28353 in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); 28354 } 28355 break :optional; 28356 }, 28357 else => |e| return e, 28358 }; 28359 return try sema.wrapOptional(block, dest_ty, intermediate, inst_src); 28360 }, 28361 .pointer => pointer: { 28362 const dest_info = dest_ty.ptrInfo(zcu); 28363 28364 // Function body to function pointer. 28365 if (inst_ty.zigTypeTag(zcu) == .@"fn") { 28366 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); 28367 const fn_nav = switch (zcu.intern_pool.indexToKey(fn_val.toIntern())) { 28368 .func => |f| f.owner_nav, 28369 .@"extern" => |e| e.owner_nav, 28370 else => unreachable, 28371 }; 28372 const inst_as_ptr = try sema.analyzeNavRef(block, inst_src, fn_nav); 28373 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); 28374 } 28375 28376 // *T to *[1]T 28377 single_item: { 28378 if (dest_info.flags.size != .one) break :single_item; 28379 if (!inst_ty.isSinglePointer(zcu)) break :single_item; 28380 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 28381 const ptr_elem_ty = inst_ty.childType(zcu); 28382 const array_ty: Type = .fromInterned(dest_info.child); 28383 if (array_ty.zigTypeTag(zcu) != .array) break :single_item; 28384 const array_elem_ty = array_ty.childType(zcu); 28385 if (array_ty.arrayLen(zcu) != 1) break :single_item; 28386 const dest_is_mut = !dest_info.flags.is_const; 28387 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) { 28388 .ok => {}, 28389 else => break :single_item, 28390 } 28391 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28392 } 28393 28394 // Coercions where the source is a single pointer to an array. 28395 src_array_ptr: { 28396 if (!inst_ty.isSinglePointer(zcu)) break :src_array_ptr; 28397 if (dest_info.flags.size == .one) break :src_array_ptr; // `*[n]T` -> `*T` isn't valid 28398 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 28399 const array_ty = inst_ty.childType(zcu); 28400 if (array_ty.zigTypeTag(zcu) != .array) break :src_array_ptr; 28401 const array_elem_type = array_ty.childType(zcu); 28402 const dest_is_mut = !dest_info.flags.is_const; 28403 28404 const dst_elem_type: Type = .fromInterned(dest_info.child); 28405 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val); 28406 switch (elem_res) { 28407 .ok => {}, 28408 else => { 28409 in_memory_result = .{ .ptr_child = .{ 28410 .child = try elem_res.dupe(sema.arena), 28411 .actual = array_elem_type, 28412 .wanted = dst_elem_type, 28413 } }; 28414 break :src_array_ptr; 28415 }, 28416 } 28417 28418 if (dest_info.sentinel != .none) { 28419 if (array_ty.sentinel(zcu)) |inst_sent| { 28420 if (Air.internedToRef(dest_info.sentinel) != 28421 try sema.coerceInMemory(inst_sent, dst_elem_type)) 28422 { 28423 in_memory_result = .{ .ptr_sentinel = .{ 28424 .actual = inst_sent, 28425 .wanted = Value.fromInterned(dest_info.sentinel), 28426 .ty = dst_elem_type, 28427 } }; 28428 break :src_array_ptr; 28429 } 28430 } else { 28431 in_memory_result = .{ .ptr_sentinel = .{ 28432 .actual = Value.@"unreachable", 28433 .wanted = Value.fromInterned(dest_info.sentinel), 28434 .ty = dst_elem_type, 28435 } }; 28436 break :src_array_ptr; 28437 } 28438 } 28439 28440 switch (dest_info.flags.size) { 28441 .slice => { 28442 // *[N]T to []T 28443 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src); 28444 }, 28445 .c => { 28446 // *[N]T to [*c]T 28447 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28448 }, 28449 .many => { 28450 // *[N]T to [*]T 28451 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28452 }, 28453 .one => unreachable, // early exit at top of block 28454 } 28455 } 28456 28457 // coercion from C pointer 28458 if (inst_ty.isCPtr(zcu)) src_c_ptr: { 28459 if (dest_info.flags.size == .slice) break :src_c_ptr; 28460 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr; 28461 // In this case we must add a safety check because the C pointer 28462 // could be null. 28463 const src_elem_ty = inst_ty.childType(zcu); 28464 const dest_is_mut = !dest_info.flags.is_const; 28465 const dst_elem_type: Type = .fromInterned(dest_info.child); 28466 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) { 28467 .ok => {}, 28468 else => break :src_c_ptr, 28469 } 28470 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28471 } 28472 28473 // cast from *T and [*]T to *anyopaque 28474 // but don't do it if the source type is a double pointer 28475 if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(zcu) == .pointer) to_anyopaque: { 28476 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 28477 const elem_ty = inst_ty.elemType2(zcu); 28478 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) { 28479 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 28480 .actual = inst_ty, 28481 .wanted = dest_ty, 28482 } }; 28483 break :pointer; 28484 } 28485 if (dest_ty.isSlice(zcu)) break :to_anyopaque; 28486 if (inst_ty.isSlice(zcu)) { 28487 in_memory_result = .{ .slice_to_anyopaque = .{ 28488 .actual = inst_ty, 28489 .wanted = dest_ty, 28490 } }; 28491 break :pointer; 28492 } 28493 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28494 } 28495 28496 switch (dest_info.flags.size) { 28497 // coercion to C pointer 28498 .c => switch (inst_ty.zigTypeTag(zcu)) { 28499 .null => return Air.internedToRef(try pt.intern(.{ .ptr = .{ 28500 .ty = dest_ty.toIntern(), 28501 .base_addr = .int, 28502 .byte_offset = 0, 28503 } })), 28504 .comptime_int => { 28505 const addr = sema.coerceExtra(block, .usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 28506 error.NotCoercible => break :pointer, 28507 else => |e| return e, 28508 }; 28509 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 28510 }, 28511 .int => { 28512 const ptr_size_ty: Type = switch (inst_ty.intInfo(zcu).signedness) { 28513 .signed => .isize, 28514 .unsigned => .usize, 28515 }; 28516 const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 28517 error.NotCoercible => { 28518 // Try to give more useful notes 28519 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); 28520 break :pointer; 28521 }, 28522 else => |e| return e, 28523 }; 28524 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 28525 }, 28526 .pointer => p: { 28527 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 28528 const inst_info = inst_ty.ptrInfo(zcu); 28529 switch (try sema.coerceInMemoryAllowed( 28530 block, 28531 .fromInterned(dest_info.child), 28532 .fromInterned(inst_info.child), 28533 !dest_info.flags.is_const, 28534 target, 28535 dest_ty_src, 28536 inst_src, 28537 maybe_inst_val, 28538 )) { 28539 .ok => {}, 28540 else => break :p, 28541 } 28542 if (inst_info.flags.size == .slice) { 28543 assert(dest_info.sentinel == .none); 28544 if (inst_info.sentinel == .none or 28545 inst_info.sentinel != (try pt.intValue(.fromInterned(inst_info.child), 0)).toIntern()) 28546 break :p; 28547 28548 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 28549 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 28550 } 28551 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28552 }, 28553 else => {}, 28554 }, 28555 .one => {}, 28556 .slice => to_slice: { 28557 if (inst_ty.zigTypeTag(zcu) == .array) { 28558 return sema.fail( 28559 block, 28560 inst_src, 28561 "array literal requires address-of operator (&) to coerce to slice type '{f}'", 28562 .{dest_ty.fmt(pt)}, 28563 ); 28564 } 28565 28566 if (!inst_ty.isSinglePointer(zcu)) break :to_slice; 28567 const inst_child_ty = inst_ty.childType(zcu); 28568 if (!inst_child_ty.isTuple(zcu)) break :to_slice; 28569 28570 // empty tuple to zero-length slice 28571 // note that this allows coercing to a mutable slice. 28572 if (inst_child_ty.structFieldCount(zcu) == 0) { 28573 const align_val = try dest_ty.ptrAlignmentSema(pt); 28574 return Air.internedToRef(try pt.intern(.{ .slice = .{ 28575 .ty = dest_ty.toIntern(), 28576 .ptr = try pt.intern(.{ .ptr = .{ 28577 .ty = dest_ty.slicePtrFieldType(zcu).toIntern(), 28578 .base_addr = .int, 28579 .byte_offset = align_val.toByteUnits().?, 28580 } }), 28581 .len = .zero_usize, 28582 } })); 28583 } 28584 28585 // pointer to tuple to slice 28586 if (!dest_info.flags.is_const) { 28587 const err_msg = err_msg: { 28588 const err_msg = try sema.errMsg(inst_src, "cannot cast pointer to tuple to '{f}'", .{dest_ty.fmt(pt)}); 28589 errdefer err_msg.destroy(sema.gpa); 28590 try sema.errNote(dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{}); 28591 break :err_msg err_msg; 28592 }; 28593 return sema.failWithOwnedErrorMsg(block, err_msg); 28594 } 28595 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); 28596 }, 28597 .many => p: { 28598 if (!inst_ty.isSlice(zcu)) break :p; 28599 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 28600 const inst_info = inst_ty.ptrInfo(zcu); 28601 28602 switch (try sema.coerceInMemoryAllowed( 28603 block, 28604 .fromInterned(dest_info.child), 28605 .fromInterned(inst_info.child), 28606 !dest_info.flags.is_const, 28607 target, 28608 dest_ty_src, 28609 inst_src, 28610 maybe_inst_val, 28611 )) { 28612 .ok => {}, 28613 else => break :p, 28614 } 28615 28616 if (dest_info.sentinel == .none or inst_info.sentinel == .none or 28617 Air.internedToRef(dest_info.sentinel) != 28618 try sema.coerceInMemory(Value.fromInterned(inst_info.sentinel), .fromInterned(dest_info.child))) 28619 break :p; 28620 28621 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 28622 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 28623 }, 28624 } 28625 }, 28626 .int, .comptime_int => switch (inst_ty.zigTypeTag(zcu)) { 28627 .float, .comptime_float => float: { 28628 const val = maybe_inst_val orelse { 28629 if (dest_ty.zigTypeTag(zcu) == .comptime_int) { 28630 if (!opts.report_err) return error.NotCoercible; 28631 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int }); 28632 } 28633 break :float; 28634 }; 28635 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact); 28636 return Air.internedToRef(result_val.toIntern()); 28637 }, 28638 .int, .comptime_int => { 28639 if (maybe_inst_val) |val| { 28640 // comptime-known integer to other number 28641 if (!(try sema.intFitsInType(val, dest_ty, null))) { 28642 if (!opts.report_err) return error.NotCoercible; 28643 return sema.fail(block, inst_src, "type '{f}' cannot represent integer value '{f}'", .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) }); 28644 } 28645 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 28646 .undef => try pt.undefRef(dest_ty), 28647 .int => |int| Air.internedToRef( 28648 try zcu.intern_pool.getCoercedInts(zcu.gpa, pt.tid, int, dest_ty.toIntern()), 28649 ), 28650 else => unreachable, 28651 }; 28652 } 28653 if (dest_ty.zigTypeTag(zcu) == .comptime_int) { 28654 if (!opts.report_err) return error.NotCoercible; 28655 if (opts.no_cast_to_comptime_int) return inst; 28656 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int }); 28657 } 28658 28659 // integer widening 28660 const dst_info = dest_ty.intInfo(zcu); 28661 const src_info = inst_ty.intInfo(zcu); 28662 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or 28663 // small enough unsigned ints can get casted to large enough signed ints 28664 (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) 28665 { 28666 try sema.requireRuntimeBlock(block, inst_src, null); 28667 return block.addTyOp(.intcast, dest_ty, inst); 28668 } 28669 }, 28670 else => {}, 28671 }, 28672 .float, .comptime_float => switch (inst_ty.zigTypeTag(zcu)) { 28673 .comptime_float => { 28674 const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); 28675 const result_val = try val.floatCast(dest_ty, pt); 28676 return Air.internedToRef(result_val.toIntern()); 28677 }, 28678 .float => { 28679 if (maybe_inst_val) |val| { 28680 const result_val = try val.floatCast(dest_ty, pt); 28681 if (!val.eql(try result_val.floatCast(inst_ty, pt), inst_ty, zcu)) { 28682 return sema.fail( 28683 block, 28684 inst_src, 28685 "type '{f}' cannot represent float value '{f}'", 28686 .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) }, 28687 ); 28688 } 28689 return Air.internedToRef(result_val.toIntern()); 28690 } else if (dest_ty.zigTypeTag(zcu) == .comptime_float) { 28691 if (!opts.report_err) return error.NotCoercible; 28692 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float }); 28693 } 28694 28695 // float widening 28696 const src_bits = inst_ty.floatBits(target); 28697 const dst_bits = dest_ty.floatBits(target); 28698 if (dst_bits >= src_bits) { 28699 try sema.requireRuntimeBlock(block, inst_src, null); 28700 return block.addTyOp(.fpext, dest_ty, inst); 28701 } 28702 }, 28703 .int, .comptime_int => int: { 28704 const val = maybe_inst_val orelse { 28705 if (dest_ty.zigTypeTag(zcu) == .comptime_float) { 28706 if (!opts.report_err) return error.NotCoercible; 28707 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float }); 28708 } 28709 break :int; 28710 }; 28711 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema); 28712 const fits: bool = switch (ip.indexToKey(result_val.toIntern())) { 28713 else => unreachable, 28714 .undef => true, 28715 .float => |float| fits: { 28716 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; 28717 const operand_big_int = val.toBigInt(&buffer, zcu); 28718 switch (float.storage) { 28719 inline else => |x| { 28720 if (!std.math.isFinite(x)) break :fits false; 28721 var result_big_int: std.math.big.int.Mutable = .{ 28722 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)), 28723 .len = undefined, 28724 .positive = undefined, 28725 }; 28726 switch (result_big_int.setFloat(x, .nearest_even)) { 28727 .inexact => break :fits false, 28728 .exact => {}, 28729 } 28730 break :fits result_big_int.toConst().eql(operand_big_int); 28731 }, 28732 } 28733 }, 28734 }; 28735 if (!fits) return sema.fail( 28736 block, 28737 inst_src, 28738 "type '{f}' cannot represent integer value '{f}'", 28739 .{ dest_ty.fmt(pt), val.fmtValue(pt) }, 28740 ); 28741 return .fromValue(result_val); 28742 }, 28743 else => {}, 28744 }, 28745 .@"enum" => switch (inst_ty.zigTypeTag(zcu)) { 28746 .enum_literal => { 28747 // enum literal to enum 28748 const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); 28749 const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal; 28750 const field_index = dest_ty.enumFieldIndex(string, zcu) orelse { 28751 return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{ 28752 string.fmt(&zcu.intern_pool), dest_ty.fmt(pt), 28753 }); 28754 }; 28755 return Air.internedToRef((try pt.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern()); 28756 }, 28757 .@"union" => blk: { 28758 // union to its own tag type 28759 const union_tag_ty = inst_ty.unionTagType(zcu) orelse break :blk; 28760 if (union_tag_ty.eql(dest_ty, zcu)) { 28761 return sema.unionToTag(block, dest_ty, inst, inst_src); 28762 } 28763 }, 28764 else => {}, 28765 }, 28766 .error_union => switch (inst_ty.zigTypeTag(zcu)) { 28767 .error_union => eu: { 28768 if (maybe_inst_val) |inst_val| { 28769 switch (inst_val.toIntern()) { 28770 .undef => return pt.undefRef(dest_ty), 28771 else => switch (zcu.intern_pool.indexToKey(inst_val.toIntern())) { 28772 .error_union => |error_union| switch (error_union.val) { 28773 .err_name => |err_name| { 28774 const error_set_ty = inst_ty.errorUnionSet(zcu); 28775 const error_set_val = Air.internedToRef((try pt.intern(.{ .err = .{ 28776 .ty = error_set_ty.toIntern(), 28777 .name = err_name, 28778 } }))); 28779 return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src); 28780 }, 28781 .payload => |payload| { 28782 const payload_val = Air.internedToRef(payload); 28783 return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) { 28784 error.NotCoercible => break :eu, 28785 else => |e| return e, 28786 }; 28787 }, 28788 }, 28789 else => unreachable, 28790 }, 28791 } 28792 } 28793 }, 28794 .error_set => { 28795 // E to E!T 28796 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src); 28797 }, 28798 else => eu: { 28799 // T to E!T 28800 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) { 28801 error.NotCoercible => { 28802 if (in_memory_result == .no_match) { 28803 const payload_type = dest_ty.errorUnionPayload(zcu); 28804 // Try to give more useful notes 28805 in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); 28806 } 28807 break :eu; 28808 }, 28809 else => |e| return e, 28810 }; 28811 }, 28812 }, 28813 .@"union" => switch (inst_ty.zigTypeTag(zcu)) { 28814 .@"enum", .enum_literal => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), 28815 else => {}, 28816 }, 28817 .array => switch (inst_ty.zigTypeTag(zcu)) { 28818 .array => array_to_array: { 28819 // Array coercions are allowed only if the child is IMC and the sentinel is unchanged or removed. 28820 if (.ok != try sema.coerceInMemoryAllowed( 28821 block, 28822 dest_ty.childType(zcu), 28823 inst_ty.childType(zcu), 28824 false, 28825 target, 28826 dest_ty_src, 28827 inst_src, 28828 maybe_inst_val, 28829 )) { 28830 break :array_to_array; 28831 } 28832 28833 if (dest_ty.sentinel(zcu)) |dest_sent| { 28834 const src_sent = inst_ty.sentinel(zcu) orelse break :array_to_array; 28835 if (dest_sent.toIntern() != (try pt.getCoerced(src_sent, dest_ty.childType(zcu))).toIntern()) { 28836 break :array_to_array; 28837 } 28838 } 28839 28840 return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src); 28841 }, 28842 .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 28843 .@"struct" => { 28844 if (inst_ty.isTuple(zcu)) { 28845 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 28846 } 28847 }, 28848 else => {}, 28849 }, 28850 .vector => switch (inst_ty.zigTypeTag(zcu)) { 28851 .array, .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 28852 .@"struct" => { 28853 if (inst_ty.isTuple(zcu)) { 28854 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 28855 } 28856 }, 28857 else => {}, 28858 }, 28859 .@"struct" => blk: { 28860 if (dest_ty.isTuple(zcu) and inst_ty.isTuple(zcu)) { 28861 return sema.coerceTupleToTuple(block, dest_ty, inst, inst_src) catch |err| switch (err) { 28862 error.NotCoercible => break :blk, 28863 else => |e| return e, 28864 }; 28865 } 28866 }, 28867 else => {}, 28868 } 28869 28870 const can_coerce_to = switch (dest_ty.zigTypeTag(zcu)) { 28871 .noreturn, .@"opaque" => false, 28872 else => true, 28873 }; 28874 28875 if (can_coerce_to) { 28876 // undefined to anything. We do this after the big switch above so that 28877 // special logic has a chance to run first, such as `*[N]T` to `[]T` which 28878 // should initialize the length field of the slice. 28879 if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty); 28880 } 28881 28882 if (!opts.report_err) return error.NotCoercible; 28883 28884 if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) { 28885 const msg = msg: { 28886 const msg = try sema.errMsg(inst_src, "function declared 'noreturn' returns", .{}); 28887 errdefer msg.destroy(sema.gpa); 28888 28889 const ret_ty_src: LazySrcLoc = .{ 28890 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), 28891 .offset = .{ .node_offset_fn_type_ret_ty = .zero }, 28892 }; 28893 try sema.errNote(ret_ty_src, msg, "'noreturn' declared here", .{}); 28894 break :msg msg; 28895 }; 28896 return sema.failWithOwnedErrorMsg(block, msg); 28897 } 28898 28899 const msg = msg: { 28900 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), inst_ty.fmt(pt) }); 28901 errdefer msg.destroy(sema.gpa); 28902 28903 if (!can_coerce_to) { 28904 try sema.errNote(inst_src, msg, "cannot coerce to '{f}'", .{dest_ty.fmt(pt)}); 28905 } 28906 28907 // E!T to T 28908 if (inst_ty.zigTypeTag(zcu) == .error_union and 28909 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok) 28910 { 28911 try sema.errNote(inst_src, msg, "cannot convert error union to payload type", .{}); 28912 try sema.errNote(inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); 28913 } 28914 28915 // ?T to T 28916 if (inst_ty.zigTypeTag(zcu) == .optional and 28917 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok) 28918 { 28919 try sema.errNote(inst_src, msg, "cannot convert optional to payload type", .{}); 28920 try sema.errNote(inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 28921 } 28922 28923 try in_memory_result.report(sema, inst_src, msg); 28924 28925 // Add notes about function return type 28926 if (opts.is_ret and 28927 !zcu.test_functions.contains(zcu.funcInfo(sema.func_index).owner_nav)) 28928 { 28929 const ret_ty_src: LazySrcLoc = .{ 28930 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), 28931 .offset = .{ .node_offset_fn_type_ret_ty = .zero }, 28932 }; 28933 if (inst_ty.isError(zcu) and !dest_ty.isError(zcu)) { 28934 try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{}); 28935 } else { 28936 try sema.errNote(ret_ty_src, msg, "function return type declared here", .{}); 28937 } 28938 } 28939 28940 if (try opts.param_src.get(sema)) |param_src| { 28941 try sema.errNote(param_src, msg, "parameter type declared here", .{}); 28942 } 28943 28944 // TODO maybe add "cannot store an error in type '{f}'" note 28945 28946 break :msg msg; 28947 }; 28948 return sema.failWithOwnedErrorMsg(block, msg); 28949 } 28950 28951 fn coerceInMemory( 28952 sema: *Sema, 28953 val: Value, 28954 dst_ty: Type, 28955 ) CompileError!Air.Inst.Ref { 28956 return Air.internedToRef((try sema.pt.getCoerced(val, dst_ty)).toIntern()); 28957 } 28958 28959 const InMemoryCoercionResult = union(enum) { 28960 ok, 28961 no_match: Pair, 28962 int_not_coercible: Int, 28963 comptime_int_not_coercible: TypeValuePair, 28964 error_union_payload: PairAndChild, 28965 array_len: IntPair, 28966 array_sentinel: Sentinel, 28967 array_elem: PairAndChild, 28968 vector_len: IntPair, 28969 vector_elem: PairAndChild, 28970 optional_shape: Pair, 28971 optional_child: PairAndChild, 28972 from_anyerror, 28973 missing_error: []const InternPool.NullTerminatedString, 28974 /// true if wanted is var args 28975 fn_var_args: bool, 28976 /// true if wanted is generic 28977 fn_generic: bool, 28978 fn_param_count: IntPair, 28979 fn_param_noalias: IntPair, 28980 fn_param_comptime: ComptimeParam, 28981 fn_param: Param, 28982 fn_cc: CC, 28983 fn_return_type: PairAndChild, 28984 ptr_child: PairAndChild, 28985 ptr_addrspace: AddressSpace, 28986 ptr_sentinel: Sentinel, 28987 ptr_size: Size, 28988 ptr_const: Pair, 28989 ptr_volatile: Pair, 28990 ptr_allowzero: Pair, 28991 ptr_bit_range: BitRange, 28992 ptr_alignment: AlignPair, 28993 double_ptr_to_anyopaque: Pair, 28994 slice_to_anyopaque: Pair, 28995 28996 const Pair = struct { 28997 actual: Type, 28998 wanted: Type, 28999 }; 29000 29001 const TypeValuePair = struct { 29002 actual: Value, 29003 wanted: Type, 29004 }; 29005 29006 const PairAndChild = struct { 29007 child: *InMemoryCoercionResult, 29008 actual: Type, 29009 wanted: Type, 29010 }; 29011 29012 const Param = struct { 29013 child: *InMemoryCoercionResult, 29014 actual: Type, 29015 wanted: Type, 29016 index: u64, 29017 }; 29018 29019 const ComptimeParam = struct { 29020 index: u64, 29021 wanted: bool, 29022 }; 29023 29024 const Sentinel = struct { 29025 // unreachable_value indicates no sentinel 29026 actual: Value, 29027 wanted: Value, 29028 ty: Type, 29029 }; 29030 29031 const Int = struct { 29032 actual_signedness: std.builtin.Signedness, 29033 wanted_signedness: std.builtin.Signedness, 29034 actual_bits: u16, 29035 wanted_bits: u16, 29036 }; 29037 29038 const IntPair = struct { 29039 actual: u64, 29040 wanted: u64, 29041 }; 29042 29043 const AlignPair = struct { 29044 actual: Alignment, 29045 wanted: Alignment, 29046 }; 29047 29048 const Size = struct { 29049 actual: std.builtin.Type.Pointer.Size, 29050 wanted: std.builtin.Type.Pointer.Size, 29051 }; 29052 29053 const AddressSpace = struct { 29054 actual: std.builtin.AddressSpace, 29055 wanted: std.builtin.AddressSpace, 29056 }; 29057 29058 const CC = struct { 29059 actual: std.builtin.CallingConvention, 29060 wanted: std.builtin.CallingConvention, 29061 }; 29062 29063 const BitRange = struct { 29064 actual_host: u16, 29065 wanted_host: u16, 29066 actual_offset: u16, 29067 wanted_offset: u16, 29068 }; 29069 29070 fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult { 29071 const res = try arena.create(InMemoryCoercionResult); 29072 res.* = child.*; 29073 return res; 29074 } 29075 29076 fn report(res: *const InMemoryCoercionResult, sema: *Sema, src: LazySrcLoc, msg: *Zcu.ErrorMsg) !void { 29077 const pt = sema.pt; 29078 var cur = res; 29079 while (true) switch (cur.*) { 29080 .ok => unreachable, 29081 .no_match => |types| { 29082 try sema.addDeclaredHereNote(msg, types.wanted); 29083 try sema.addDeclaredHereNote(msg, types.actual); 29084 break; 29085 }, 29086 .int_not_coercible => |int| { 29087 try sema.errNote(src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{ 29088 @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits, 29089 }); 29090 break; 29091 }, 29092 .comptime_int_not_coercible => |int| { 29093 try sema.errNote(src, msg, "type '{f}' cannot represent value '{f}'", .{ 29094 int.wanted.fmt(pt), int.actual.fmtValueSema(pt, sema), 29095 }); 29096 break; 29097 }, 29098 .error_union_payload => |pair| { 29099 try sema.errNote(src, msg, "error union payload '{f}' cannot cast into error union payload '{f}'", .{ 29100 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29101 }); 29102 cur = pair.child; 29103 }, 29104 .array_len => |lens| { 29105 try sema.errNote(src, msg, "array of length {d} cannot cast into an array of length {d}", .{ 29106 lens.actual, lens.wanted, 29107 }); 29108 break; 29109 }, 29110 .array_sentinel => |sentinel| { 29111 if (sentinel.actual.toIntern() != .unreachable_value) { 29112 try sema.errNote(src, msg, "array sentinel '{f}' cannot cast into array sentinel '{f}'", .{ 29113 sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema), 29114 }); 29115 } else { 29116 try sema.errNote(src, msg, "destination array requires '{f}' sentinel", .{ 29117 sentinel.wanted.fmtValueSema(pt, sema), 29118 }); 29119 } 29120 break; 29121 }, 29122 .array_elem => |pair| { 29123 try sema.errNote(src, msg, "array element type '{f}' cannot cast into array element type '{f}'", .{ 29124 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29125 }); 29126 cur = pair.child; 29127 }, 29128 .vector_len => |lens| { 29129 try sema.errNote(src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{ 29130 lens.actual, lens.wanted, 29131 }); 29132 break; 29133 }, 29134 .vector_elem => |pair| { 29135 try sema.errNote(src, msg, "vector element type '{f}' cannot cast into vector element type '{f}'", .{ 29136 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29137 }); 29138 cur = pair.child; 29139 }, 29140 .optional_shape => |pair| { 29141 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{ 29142 pair.actual.optionalChild(pt.zcu).fmt(pt), pair.wanted.optionalChild(pt.zcu).fmt(pt), 29143 }); 29144 break; 29145 }, 29146 .optional_child => |pair| { 29147 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{ 29148 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29149 }); 29150 cur = pair.child; 29151 }, 29152 .from_anyerror => { 29153 try sema.errNote(src, msg, "global error set cannot cast into a smaller set", .{}); 29154 break; 29155 }, 29156 .missing_error => |missing_errors| { 29157 for (missing_errors) |err| { 29158 try sema.errNote(src, msg, "'error.{f}' not a member of destination error set", .{err.fmt(&pt.zcu.intern_pool)}); 29159 } 29160 break; 29161 }, 29162 .fn_var_args => |wanted_var_args| { 29163 if (wanted_var_args) { 29164 try sema.errNote(src, msg, "non-variadic function cannot cast into a variadic function", .{}); 29165 } else { 29166 try sema.errNote(src, msg, "variadic function cannot cast into a non-variadic function", .{}); 29167 } 29168 break; 29169 }, 29170 .fn_generic => |wanted_generic| { 29171 if (wanted_generic) { 29172 try sema.errNote(src, msg, "non-generic function cannot cast into a generic function", .{}); 29173 } else { 29174 try sema.errNote(src, msg, "generic function cannot cast into a non-generic function", .{}); 29175 } 29176 break; 29177 }, 29178 .fn_param_count => |lens| { 29179 try sema.errNote(src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{ 29180 lens.actual, lens.wanted, 29181 }); 29182 break; 29183 }, 29184 .fn_param_noalias => |param| { 29185 var index: u6 = 0; 29186 var actual_noalias = false; 29187 while (true) : (index += 1) { 29188 const actual: u1 = @truncate(param.actual >> index); 29189 const wanted: u1 = @truncate(param.wanted >> index); 29190 if (actual != wanted) { 29191 actual_noalias = actual == 1; 29192 break; 29193 } 29194 } 29195 if (!actual_noalias) { 29196 try sema.errNote(src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index}); 29197 } else { 29198 try sema.errNote(src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index}); 29199 } 29200 break; 29201 }, 29202 .fn_param_comptime => |param| { 29203 if (param.wanted) { 29204 try sema.errNote(src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index}); 29205 } else { 29206 try sema.errNote(src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index}); 29207 } 29208 break; 29209 }, 29210 .fn_param => |param| { 29211 try sema.errNote(src, msg, "parameter {d} '{f}' cannot cast into '{f}'", .{ 29212 param.index, param.actual.fmt(pt), param.wanted.fmt(pt), 29213 }); 29214 cur = param.child; 29215 }, 29216 .fn_cc => |cc| { 29217 try sema.errNote(src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) }); 29218 break; 29219 }, 29220 .fn_return_type => |pair| { 29221 try sema.errNote(src, msg, "return type '{f}' cannot cast into return type '{f}'", .{ 29222 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29223 }); 29224 cur = pair.child; 29225 }, 29226 .ptr_child => |pair| { 29227 try sema.errNote(src, msg, "pointer type child '{f}' cannot cast into pointer type child '{f}'", .{ 29228 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29229 }); 29230 cur = pair.child; 29231 }, 29232 .ptr_addrspace => |@"addrspace"| { 29233 try sema.errNote(src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) }); 29234 break; 29235 }, 29236 .ptr_sentinel => |sentinel| { 29237 if (sentinel.actual.toIntern() != .unreachable_value) { 29238 try sema.errNote(src, msg, "pointer sentinel '{f}' cannot cast into pointer sentinel '{f}'", .{ 29239 sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema), 29240 }); 29241 } else { 29242 try sema.errNote(src, msg, "destination pointer requires '{f}' sentinel", .{ 29243 sentinel.wanted.fmtValueSema(pt, sema), 29244 }); 29245 } 29246 break; 29247 }, 29248 .ptr_size => |size| { 29249 try sema.errNote(src, msg, "a {s} cannot cast into a {s}", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) }); 29250 break; 29251 }, 29252 .ptr_allowzero => |pair| { 29253 const wanted_allow_zero = pair.wanted.ptrAllowsZero(pt.zcu); 29254 const actual_allow_zero = pair.actual.ptrAllowsZero(pt.zcu); 29255 if (actual_allow_zero and !wanted_allow_zero) { 29256 try sema.errNote(src, msg, "'{f}' could have null values which are illegal in type '{f}'", .{ 29257 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29258 }); 29259 } else { 29260 try sema.errNote(src, msg, "mutable '{f}' would allow illegal null values stored to type '{f}'", .{ 29261 pair.wanted.fmt(pt), pair.actual.fmt(pt), 29262 }); 29263 } 29264 break; 29265 }, 29266 .ptr_const => |pair| { 29267 const wanted_const = pair.wanted.isConstPtr(pt.zcu); 29268 const actual_const = pair.actual.isConstPtr(pt.zcu); 29269 if (actual_const and !wanted_const) { 29270 try sema.errNote(src, msg, "cast discards const qualifier", .{}); 29271 } else { 29272 try sema.errNote(src, msg, "mutable '{f}' would allow illegal const pointers stored to type '{f}'", .{ 29273 pair.wanted.fmt(pt), pair.actual.fmt(pt), 29274 }); 29275 } 29276 break; 29277 }, 29278 .ptr_volatile => |pair| { 29279 const wanted_volatile = pair.wanted.isVolatilePtr(pt.zcu); 29280 const actual_volatile = pair.actual.isVolatilePtr(pt.zcu); 29281 if (actual_volatile and !wanted_volatile) { 29282 try sema.errNote(src, msg, "cast discards volatile qualifier", .{}); 29283 } else { 29284 try sema.errNote(src, msg, "mutable '{f}' would allow illegal volatile pointers stored to type '{f}'", .{ 29285 pair.wanted.fmt(pt), pair.actual.fmt(pt), 29286 }); 29287 } 29288 break; 29289 }, 29290 .ptr_bit_range => |bit_range| { 29291 if (bit_range.actual_host != bit_range.wanted_host) { 29292 try sema.errNote(src, msg, "pointer host size '{d}' cannot cast into pointer host size '{d}'", .{ 29293 bit_range.actual_host, bit_range.wanted_host, 29294 }); 29295 } 29296 if (bit_range.actual_offset != bit_range.wanted_offset) { 29297 try sema.errNote(src, msg, "pointer bit offset '{d}' cannot cast into pointer bit offset '{d}'", .{ 29298 bit_range.actual_offset, bit_range.wanted_offset, 29299 }); 29300 } 29301 break; 29302 }, 29303 .ptr_alignment => |pair| { 29304 try sema.errNote(src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{ 29305 pair.actual.toByteUnits() orelse 0, pair.wanted.toByteUnits() orelse 0, 29306 }); 29307 break; 29308 }, 29309 .double_ptr_to_anyopaque => |pair| { 29310 try sema.errNote(src, msg, "cannot implicitly cast double pointer '{f}' to anyopaque pointer '{f}'", .{ 29311 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29312 }); 29313 break; 29314 }, 29315 .slice_to_anyopaque => |pair| { 29316 try sema.errNote(src, msg, "cannot implicitly cast slice '{f}' to anyopaque pointer '{f}'", .{ 29317 pair.actual.fmt(pt), pair.wanted.fmt(pt), 29318 }); 29319 try sema.errNote(src, msg, "consider using '.ptr'", .{}); 29320 break; 29321 }, 29322 }; 29323 } 29324 }; 29325 29326 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 { 29327 return switch (size) { 29328 .one => "single pointer", 29329 .many => "many pointer", 29330 .c => "C pointer", 29331 .slice => "slice", 29332 }; 29333 } 29334 29335 /// If types `A` and `B` have identical representations in runtime memory, they are considered 29336 /// "in-memory coercible". This is a subset of normal coercions. Not only can `A` coerce to `B`, but 29337 /// also, coercions can happen through pointers. For instance, `*const A` can coerce to `*const B`. 29338 /// 29339 /// If this function is called, the coercion must be applied, or a compile error emitted if `.ok` 29340 /// is not returned. This is because this function may modify inferred error sets to make a 29341 /// coercion possible, even if `.ok` is not returned. 29342 pub fn coerceInMemoryAllowed( 29343 sema: *Sema, 29344 block: *Block, 29345 dest_ty: Type, 29346 src_ty: Type, 29347 /// If `true`, this query comes from an attempted coercion of the form `*Src` -> `*Dest`, where 29348 /// both pointers are mutable. If this coercion is allowed, one could store to the `*Dest` and 29349 /// load from the `*Src` to effectively perform an in-memory coercion from `Dest` to `Src`. 29350 /// Therefore, when `dest_is_mut`, the in-memory coercion must be valid in *both directions*. 29351 dest_is_mut: bool, 29352 target: *const std.Target, 29353 dest_src: LazySrcLoc, 29354 src_src: LazySrcLoc, 29355 src_val: ?Value, 29356 ) CompileError!InMemoryCoercionResult { 29357 const pt = sema.pt; 29358 const zcu = pt.zcu; 29359 29360 if (dest_ty.eql(src_ty, zcu)) 29361 return .ok; 29362 29363 const dest_tag = dest_ty.zigTypeTag(zcu); 29364 const src_tag = src_ty.zigTypeTag(zcu); 29365 29366 // Differently-named integers with the same number of bits. 29367 if (dest_tag == .int and src_tag == .int) { 29368 const dest_info = dest_ty.intInfo(zcu); 29369 const src_info = src_ty.intInfo(zcu); 29370 29371 if (dest_info.signedness == src_info.signedness and 29372 dest_info.bits == src_info.bits) 29373 { 29374 return .ok; 29375 } 29376 29377 if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or 29378 // small enough unsigned ints can get casted to large enough signed ints 29379 (dest_info.signedness == .signed and src_info.signedness == .unsigned and dest_info.bits <= src_info.bits) or 29380 (dest_info.signedness == .unsigned and src_info.signedness == .signed)) 29381 { 29382 return InMemoryCoercionResult{ .int_not_coercible = .{ 29383 .actual_signedness = src_info.signedness, 29384 .wanted_signedness = dest_info.signedness, 29385 .actual_bits = src_info.bits, 29386 .wanted_bits = dest_info.bits, 29387 } }; 29388 } 29389 } 29390 29391 // Comptime int to regular int. 29392 if (dest_tag == .int and src_tag == .comptime_int) { 29393 if (src_val) |val| { 29394 if (!(try sema.intFitsInType(val, dest_ty, null))) { 29395 return .{ .comptime_int_not_coercible = .{ .wanted = dest_ty, .actual = val } }; 29396 } 29397 } 29398 } 29399 29400 // Differently-named floats with the same number of bits. 29401 if (dest_tag == .float and src_tag == .float) { 29402 const dest_bits = dest_ty.floatBits(target); 29403 const src_bits = src_ty.floatBits(target); 29404 if (dest_bits == src_bits) { 29405 return .ok; 29406 } 29407 } 29408 29409 // Pointers / Pointer-like Optionals 29410 const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty); 29411 const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty); 29412 if (maybe_dest_ptr_ty) |dest_ptr_ty| { 29413 if (maybe_src_ptr_ty) |src_ptr_ty| { 29414 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src); 29415 } 29416 } 29417 29418 // Slices 29419 if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) { 29420 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); 29421 } 29422 29423 // Functions 29424 if (dest_tag == .@"fn" and src_tag == .@"fn") { 29425 return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); 29426 } 29427 29428 // Error Unions 29429 if (dest_tag == .error_union and src_tag == .error_union) { 29430 const dest_payload = dest_ty.errorUnionPayload(zcu); 29431 const src_payload = src_ty.errorUnionPayload(zcu); 29432 const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src, null); 29433 if (child != .ok) { 29434 return .{ .error_union_payload = .{ 29435 .child = try child.dupe(sema.arena), 29436 .actual = src_payload, 29437 .wanted = dest_payload, 29438 } }; 29439 } 29440 return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(zcu), src_ty.errorUnionSet(zcu), dest_is_mut, target, dest_src, src_src, null); 29441 } 29442 29443 // Error Sets 29444 if (dest_tag == .error_set and src_tag == .error_set) { 29445 const res1 = try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src); 29446 if (!dest_is_mut or res1 != .ok) return res1; 29447 // src -> dest is okay, but `dest_is_mut`, so it needs to be allowed in the other direction. 29448 const res2 = try sema.coerceInMemoryAllowedErrorSets(block, src_ty, dest_ty, src_src, dest_src); 29449 return res2; 29450 } 29451 29452 // Arrays 29453 if (dest_tag == .array and src_tag == .array) { 29454 const dest_info = dest_ty.arrayInfo(zcu); 29455 const src_info = src_ty.arrayInfo(zcu); 29456 if (dest_info.len != src_info.len) { 29457 return .{ .array_len = .{ 29458 .actual = src_info.len, 29459 .wanted = dest_info.len, 29460 } }; 29461 } 29462 29463 const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src, null); 29464 switch (child) { 29465 .ok => {}, 29466 .no_match => return child, 29467 else => { 29468 return .{ .array_elem = .{ 29469 .child = try child.dupe(sema.arena), 29470 .actual = src_info.elem_type, 29471 .wanted = dest_info.elem_type, 29472 } }; 29473 }, 29474 } 29475 const ok_sent = (dest_info.sentinel == null and src_info.sentinel == null) or 29476 (src_info.sentinel != null and 29477 dest_info.sentinel != null and 29478 dest_info.sentinel.?.eql( 29479 try pt.getCoerced(src_info.sentinel.?, dest_info.elem_type), 29480 dest_info.elem_type, 29481 zcu, 29482 )); 29483 if (!ok_sent) { 29484 return .{ .array_sentinel = .{ 29485 .actual = src_info.sentinel orelse Value.@"unreachable", 29486 .wanted = dest_info.sentinel orelse Value.@"unreachable", 29487 .ty = dest_info.elem_type, 29488 } }; 29489 } 29490 return .ok; 29491 } 29492 29493 // Vectors 29494 if (dest_tag == .vector and src_tag == .vector) { 29495 const dest_len = dest_ty.vectorLen(zcu); 29496 const src_len = src_ty.vectorLen(zcu); 29497 if (dest_len != src_len) { 29498 return .{ .vector_len = .{ 29499 .actual = src_len, 29500 .wanted = dest_len, 29501 } }; 29502 } 29503 29504 const dest_elem_ty = dest_ty.scalarType(zcu); 29505 const src_elem_ty = src_ty.scalarType(zcu); 29506 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null); 29507 if (child != .ok) { 29508 return .{ .vector_elem = .{ 29509 .child = try child.dupe(sema.arena), 29510 .actual = src_elem_ty, 29511 .wanted = dest_elem_ty, 29512 } }; 29513 } 29514 29515 return .ok; 29516 } 29517 29518 // Arrays <-> Vectors 29519 if ((dest_tag == .vector and src_tag == .array) or 29520 (dest_tag == .array and src_tag == .vector)) 29521 { 29522 const dest_len = dest_ty.arrayLen(zcu); 29523 const src_len = src_ty.arrayLen(zcu); 29524 if (dest_len != src_len) { 29525 return .{ .array_len = .{ 29526 .actual = src_len, 29527 .wanted = dest_len, 29528 } }; 29529 } 29530 29531 const dest_elem_ty = dest_ty.childType(zcu); 29532 const src_elem_ty = src_ty.childType(zcu); 29533 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null); 29534 if (child != .ok) { 29535 return .{ .array_elem = .{ 29536 .child = try child.dupe(sema.arena), 29537 .actual = src_elem_ty, 29538 .wanted = dest_elem_ty, 29539 } }; 29540 } 29541 29542 if (dest_tag == .array) { 29543 const dest_info = dest_ty.arrayInfo(zcu); 29544 if (dest_info.sentinel != null) { 29545 return .{ .array_sentinel = .{ 29546 .actual = Value.@"unreachable", 29547 .wanted = dest_info.sentinel.?, 29548 .ty = dest_info.elem_type, 29549 } }; 29550 } 29551 } 29552 29553 // The memory layout of @Vector(N, iM) is the same as the integer type i(N*M), 29554 // that is to say, the padding bits are not in the same place as the array [N]iM. 29555 // If there's no padding, the bitcast is possible. 29556 const elem_bit_size = dest_elem_ty.bitSize(zcu); 29557 const elem_abi_byte_size = dest_elem_ty.abiSize(zcu); 29558 if (elem_abi_byte_size * 8 == elem_bit_size) 29559 return .ok; 29560 } 29561 29562 // Optionals 29563 if (dest_tag == .optional and src_tag == .optional) { 29564 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) { 29565 return .{ .optional_shape = .{ 29566 .actual = src_ty, 29567 .wanted = dest_ty, 29568 } }; 29569 } 29570 const dest_child_type = dest_ty.optionalChild(zcu); 29571 const src_child_type = src_ty.optionalChild(zcu); 29572 29573 const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src, null); 29574 if (child != .ok) { 29575 return .{ .optional_child = .{ 29576 .child = try child.dupe(sema.arena), 29577 .actual = src_child_type, 29578 .wanted = dest_child_type, 29579 } }; 29580 } 29581 29582 return .ok; 29583 } 29584 29585 // Tuples (with in-memory-coercible fields) 29586 if (dest_ty.isTuple(zcu) and src_ty.isTuple(zcu)) tuple: { 29587 if (dest_ty.structFieldCount(zcu) != src_ty.structFieldCount(zcu)) break :tuple; 29588 const field_count = dest_ty.structFieldCount(zcu); 29589 for (0..field_count) |field_idx| { 29590 if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple; 29591 if (dest_ty.fieldAlignment(field_idx, zcu) != src_ty.fieldAlignment(field_idx, zcu)) break :tuple; 29592 const dest_field_ty = dest_ty.fieldType(field_idx, zcu); 29593 const src_field_ty = src_ty.fieldType(field_idx, zcu); 29594 const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null); 29595 if (field != .ok) break :tuple; 29596 } 29597 return .ok; 29598 } 29599 29600 return .{ .no_match = .{ 29601 .actual = dest_ty, 29602 .wanted = src_ty, 29603 } }; 29604 } 29605 29606 fn coerceInMemoryAllowedErrorSets( 29607 sema: *Sema, 29608 block: *Block, 29609 dest_ty: Type, 29610 src_ty: Type, 29611 dest_src: LazySrcLoc, 29612 src_src: LazySrcLoc, 29613 ) !InMemoryCoercionResult { 29614 const pt = sema.pt; 29615 const zcu = pt.zcu; 29616 const gpa = sema.gpa; 29617 const ip = &zcu.intern_pool; 29618 29619 // Coercion to `anyerror`. Note that this check can return false negatives 29620 // in case the error sets did not get resolved. 29621 if (dest_ty.isAnyError(zcu)) { 29622 return .ok; 29623 } 29624 29625 if (dest_ty.toIntern() == .adhoc_inferred_error_set_type) { 29626 // We are trying to coerce an error set to the current function's 29627 // inferred error set. 29628 const dst_ies = sema.fn_ret_ty_ies.?; 29629 try dst_ies.addErrorSet(src_ty, ip, sema.arena); 29630 return .ok; 29631 } 29632 29633 if (ip.isInferredErrorSetType(dest_ty.toIntern())) { 29634 const dst_ies_func_index = ip.iesFuncIndex(dest_ty.toIntern()); 29635 if (sema.fn_ret_ty_ies) |dst_ies| { 29636 if (dst_ies.func == dst_ies_func_index) { 29637 // We are trying to coerce an error set to the current function's 29638 // inferred error set. 29639 try dst_ies.addErrorSet(src_ty, ip, sema.arena); 29640 return .ok; 29641 } 29642 } 29643 switch (try sema.resolveInferredErrorSet(block, dest_src, dest_ty.toIntern())) { 29644 // isAnyError might have changed from a false negative to a true 29645 // positive after resolution. 29646 .anyerror_type => return .ok, 29647 else => {}, 29648 } 29649 } 29650 29651 var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa); 29652 defer missing_error_buf.deinit(); 29653 29654 switch (src_ty.toIntern()) { 29655 .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { 29656 .simple_type => unreachable, // filtered out above 29657 .error_set_type, .inferred_error_set_type => return .from_anyerror, 29658 else => unreachable, 29659 }, 29660 29661 else => switch (ip.indexToKey(src_ty.toIntern())) { 29662 .inferred_error_set_type => { 29663 const resolved_src_ty = try sema.resolveInferredErrorSet(block, src_src, src_ty.toIntern()); 29664 // src anyerror status might have changed after the resolution. 29665 if (resolved_src_ty == .anyerror_type) { 29666 // dest_ty.isAnyError(zcu) == true is already checked for at this point. 29667 return .from_anyerror; 29668 } 29669 29670 for (ip.indexToKey(resolved_src_ty).error_set_type.names.get(ip)) |key| { 29671 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) { 29672 try missing_error_buf.append(key); 29673 } 29674 } 29675 29676 if (missing_error_buf.items.len != 0) { 29677 return InMemoryCoercionResult{ 29678 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 29679 }; 29680 } 29681 29682 return .ok; 29683 }, 29684 .error_set_type => |error_set_type| { 29685 for (error_set_type.names.get(ip)) |name| { 29686 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) { 29687 try missing_error_buf.append(name); 29688 } 29689 } 29690 29691 if (missing_error_buf.items.len != 0) { 29692 return InMemoryCoercionResult{ 29693 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 29694 }; 29695 } 29696 29697 return .ok; 29698 }, 29699 else => unreachable, 29700 }, 29701 } 29702 } 29703 29704 fn coerceInMemoryAllowedFns( 29705 sema: *Sema, 29706 block: *Block, 29707 dest_ty: Type, 29708 src_ty: Type, 29709 /// If set, the coercion must be valid in both directions. 29710 dest_is_mut: bool, 29711 target: *const std.Target, 29712 dest_src: LazySrcLoc, 29713 src_src: LazySrcLoc, 29714 ) !InMemoryCoercionResult { 29715 const pt = sema.pt; 29716 const zcu = pt.zcu; 29717 const ip = &zcu.intern_pool; 29718 29719 const dest_info = zcu.typeToFunc(dest_ty).?; 29720 const src_info = zcu.typeToFunc(src_ty).?; 29721 29722 { 29723 if (dest_info.is_var_args != src_info.is_var_args) { 29724 return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; 29725 } 29726 29727 if (dest_info.is_generic != src_info.is_generic) { 29728 return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; 29729 } 29730 29731 const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and 29732 (!dest_is_mut or callconvCoerceAllowed(target, dest_info.cc, src_info.cc)); 29733 29734 if (!callconv_ok) { 29735 return .{ .fn_cc = .{ 29736 .actual = src_info.cc, 29737 .wanted = dest_info.cc, 29738 } }; 29739 } 29740 29741 if (!switch (src_info.return_type) { 29742 .generic_poison_type => true, 29743 .noreturn_type => !dest_is_mut, 29744 else => false, 29745 }) { 29746 const rt = try sema.coerceInMemoryAllowed( 29747 block, 29748 .fromInterned(dest_info.return_type), 29749 .fromInterned(src_info.return_type), 29750 dest_is_mut, 29751 target, 29752 dest_src, 29753 src_src, 29754 null, 29755 ); 29756 if (rt != .ok) return .{ .fn_return_type = .{ 29757 .child = try rt.dupe(sema.arena), 29758 .actual = .fromInterned(src_info.return_type), 29759 .wanted = .fromInterned(dest_info.return_type), 29760 } }; 29761 } 29762 } 29763 29764 const params_len = params_len: { 29765 if (dest_info.param_types.len != src_info.param_types.len) { 29766 return .{ .fn_param_count = .{ 29767 .actual = src_info.param_types.len, 29768 .wanted = dest_info.param_types.len, 29769 } }; 29770 } 29771 29772 if (dest_info.noalias_bits != src_info.noalias_bits) { 29773 return .{ .fn_param_noalias = .{ 29774 .actual = src_info.noalias_bits, 29775 .wanted = dest_info.noalias_bits, 29776 } }; 29777 } 29778 29779 break :params_len dest_info.param_types.len; 29780 }; 29781 29782 for (0..params_len) |param_i| { 29783 const dest_param_ty: Type = .fromInterned(dest_info.param_types.get(ip)[param_i]); 29784 const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]); 29785 29786 comptime_param: { 29787 const src_is_comptime = src_info.paramIsComptime(@intCast(param_i)); 29788 const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i)); 29789 if (src_is_comptime == dest_is_comptime) break :comptime_param; 29790 if (!dest_is_mut and src_is_comptime and !dest_is_comptime and try dest_param_ty.comptimeOnlySema(pt)) { 29791 // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only. 29792 // The function remains generic, and the parameter is going to be comptime-resolved either way, 29793 // so this just affects whether or not the argument is comptime-evaluated at the call site. 29794 break :comptime_param; 29795 } 29796 return .{ .fn_param_comptime = .{ 29797 .index = param_i, 29798 .wanted = dest_is_comptime, 29799 } }; 29800 } 29801 29802 if (!src_param_ty.isGenericPoison() and !dest_param_ty.isGenericPoison()) { 29803 // Note: Cast direction is reversed here. 29804 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, dest_is_mut, target, dest_src, src_src, null); 29805 if (param != .ok) { 29806 return .{ .fn_param = .{ 29807 .child = try param.dupe(sema.arena), 29808 .actual = src_param_ty, 29809 .wanted = dest_param_ty, 29810 .index = param_i, 29811 } }; 29812 } 29813 } 29814 } 29815 29816 return .ok; 29817 } 29818 29819 fn callconvCoerceAllowed( 29820 target: *const std.Target, 29821 src_cc: std.builtin.CallingConvention, 29822 dest_cc: std.builtin.CallingConvention, 29823 ) bool { 29824 const Tag = std.builtin.CallingConvention.Tag; 29825 if (@as(Tag, src_cc) != @as(Tag, dest_cc)) return false; 29826 29827 switch (src_cc) { 29828 inline else => |src_data, tag| { 29829 const dest_data = @field(dest_cc, @tagName(tag)); 29830 if (@TypeOf(src_data) != void) { 29831 const default_stack_align = target.stackAlignment(); 29832 const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align; 29833 const dest_stack_align = src_data.incoming_stack_alignment orelse default_stack_align; 29834 if (dest_stack_align < src_stack_align) return false; 29835 } 29836 switch (@TypeOf(src_data)) { 29837 void, std.builtin.CallingConvention.CommonOptions => {}, 29838 std.builtin.CallingConvention.X86RegparmOptions => { 29839 if (src_data.register_params != dest_data.register_params) return false; 29840 }, 29841 std.builtin.CallingConvention.ArmInterruptOptions => { 29842 if (src_data.type != dest_data.type) return false; 29843 }, 29844 std.builtin.CallingConvention.MipsInterruptOptions => { 29845 if (src_data.mode != dest_data.mode) return false; 29846 }, 29847 std.builtin.CallingConvention.RiscvInterruptOptions => { 29848 if (src_data.mode != dest_data.mode) return false; 29849 }, 29850 else => comptime unreachable, 29851 } 29852 }, 29853 } 29854 return true; 29855 } 29856 29857 fn coerceInMemoryAllowedPtrs( 29858 sema: *Sema, 29859 block: *Block, 29860 dest_ty: Type, 29861 src_ty: Type, 29862 dest_ptr_ty: Type, 29863 src_ptr_ty: Type, 29864 /// If set, the coercion must be valid in both directions. 29865 dest_is_mut: bool, 29866 target: *const std.Target, 29867 dest_src: LazySrcLoc, 29868 src_src: LazySrcLoc, 29869 ) !InMemoryCoercionResult { 29870 const pt = sema.pt; 29871 const zcu = pt.zcu; 29872 const dest_info = dest_ptr_ty.ptrInfo(zcu); 29873 const src_info = src_ptr_ty.ptrInfo(zcu); 29874 29875 const ok_ptr_size = src_info.flags.size == dest_info.flags.size or 29876 src_info.flags.size == .c or dest_info.flags.size == .c; 29877 if (!ok_ptr_size) { 29878 return InMemoryCoercionResult{ .ptr_size = .{ 29879 .actual = src_info.flags.size, 29880 .wanted = dest_info.flags.size, 29881 } }; 29882 } 29883 29884 const ok_const = src_info.flags.is_const == dest_info.flags.is_const or 29885 (!dest_is_mut and dest_info.flags.is_const); 29886 29887 if (!ok_const) return .{ .ptr_const = .{ 29888 .actual = src_ty, 29889 .wanted = dest_ty, 29890 } }; 29891 29892 const ok_volatile = src_info.flags.is_volatile == dest_info.flags.is_volatile or 29893 (!dest_is_mut and dest_info.flags.is_volatile); 29894 29895 if (!ok_volatile) return .{ .ptr_volatile = .{ 29896 .actual = src_ty, 29897 .wanted = dest_ty, 29898 } }; 29899 29900 const dest_allowzero = dest_ty.ptrAllowsZero(zcu); 29901 const src_allowzero = src_ty.ptrAllowsZero(zcu); 29902 const ok_allowzero = src_allowzero == dest_allowzero or 29903 (!dest_is_mut and dest_allowzero); 29904 29905 if (!ok_allowzero) return .{ .ptr_allowzero = .{ 29906 .actual = src_ty, 29907 .wanted = dest_ty, 29908 } }; 29909 29910 if (dest_info.flags.address_space != src_info.flags.address_space) { 29911 return .{ .ptr_addrspace = .{ 29912 .actual = src_info.flags.address_space, 29913 .wanted = dest_info.flags.address_space, 29914 } }; 29915 } 29916 29917 const dest_child: Type = .fromInterned(dest_info.child); 29918 const src_child: Type = .fromInterned(src_info.child); 29919 const child = try sema.coerceInMemoryAllowed( 29920 block, 29921 dest_child, 29922 src_child, 29923 // We must also include `dest_is_mut`. 29924 // Otherwise, this code is valid: 29925 // 29926 // const b: B = ...; 29927 // var pa: *const A = undefined; 29928 // const ppa: **const A = &pa; 29929 // const ppb: **const B = ppa; // <-- this is what that allows 29930 // ppb.* = &b; 29931 // const a: A = pa.*; 29932 // 29933 // ...effectively performing an in-memory coercion from B to A. 29934 dest_is_mut or !dest_info.flags.is_const, 29935 target, 29936 dest_src, 29937 src_src, 29938 null, 29939 ); 29940 if (child != .ok and !dest_is_mut) allow: { 29941 // As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice. 29942 // `*[n:s]T` cannot coerce in memory to `*[n]T` since they have different sizes. 29943 if (src_child.zigTypeTag(zcu) == .array and dest_child.zigTypeTag(zcu) == .array and 29944 src_child.arrayLen(zcu) == dest_child.arrayLen(zcu) and 29945 src_child.sentinel(zcu) != null and dest_child.sentinel(zcu) == null and 29946 .ok == try sema.coerceInMemoryAllowed(block, dest_child.childType(zcu), src_child.childType(zcu), !dest_info.flags.is_const, target, dest_src, src_src, null)) 29947 { 29948 break :allow; 29949 } 29950 return .{ .ptr_child = .{ 29951 .child = try child.dupe(sema.arena), 29952 .actual = .fromInterned(src_info.child), 29953 .wanted = .fromInterned(dest_info.child), 29954 } }; 29955 } 29956 29957 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or 29958 src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) 29959 { 29960 return .{ .ptr_bit_range = .{ 29961 .actual_host = src_info.packed_offset.host_size, 29962 .wanted_host = dest_info.packed_offset.host_size, 29963 .actual_offset = src_info.packed_offset.bit_offset, 29964 .wanted_offset = dest_info.packed_offset.bit_offset, 29965 } }; 29966 } 29967 29968 const sentinel_ok = ok: { 29969 const ss = src_info.sentinel; 29970 const ds = dest_info.sentinel; 29971 if (ss == .none and ds == .none) break :ok true; 29972 if (ss != .none and ds != .none) { 29973 if (ds == try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, ss, dest_info.child)) break :ok true; 29974 } 29975 if (src_info.flags.size == .c) break :ok true; 29976 if (!dest_is_mut and dest_info.sentinel == .none) break :ok true; 29977 break :ok false; 29978 }; 29979 29980 if (!sentinel_ok) { 29981 return .{ .ptr_sentinel = .{ 29982 .actual = switch (src_info.sentinel) { 29983 .none => Value.@"unreachable", 29984 else => Value.fromInterned(src_info.sentinel), 29985 }, 29986 .wanted = switch (dest_info.sentinel) { 29987 .none => Value.@"unreachable", 29988 else => Value.fromInterned(dest_info.sentinel), 29989 }, 29990 .ty = .fromInterned(dest_info.child), 29991 } }; 29992 } 29993 29994 // If both pointers have alignment 0, it means they both want ABI alignment. 29995 // In this case, if they share the same child type, no need to resolve 29996 // pointee type alignment. Otherwise both pointee types must have their alignment 29997 // resolved and we compare the alignment numerically. 29998 if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or 29999 dest_info.child != src_info.child) 30000 { 30001 const src_align = if (src_info.flags.alignment != .none) 30002 src_info.flags.alignment 30003 else 30004 try Type.fromInterned(src_info.child).abiAlignmentSema(pt); 30005 30006 const dest_align = if (dest_info.flags.alignment != .none) 30007 dest_info.flags.alignment 30008 else 30009 try Type.fromInterned(dest_info.child).abiAlignmentSema(pt); 30010 30011 if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) { 30012 return InMemoryCoercionResult{ .ptr_alignment = .{ 30013 .actual = src_align, 30014 .wanted = dest_align, 30015 } }; 30016 } 30017 } 30018 30019 return .ok; 30020 } 30021 30022 fn coerceVarArgParam( 30023 sema: *Sema, 30024 block: *Block, 30025 inst: Air.Inst.Ref, 30026 inst_src: LazySrcLoc, 30027 ) !Air.Inst.Ref { 30028 if (block.is_typeof) return inst; 30029 30030 const pt = sema.pt; 30031 const zcu = pt.zcu; 30032 const uncasted_ty = sema.typeOf(inst); 30033 const coerced = switch (uncasted_ty.zigTypeTag(zcu)) { 30034 // TODO consider casting to c_int/f64 if they fit 30035 .comptime_int, .comptime_float => return sema.fail( 30036 block, 30037 inst_src, 30038 "integer and float literals passed to variadic function must be casted to a fixed-size number type", 30039 .{}, 30040 ), 30041 .@"fn" => fn_ptr: { 30042 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); 30043 const fn_nav = zcu.funcInfo(fn_val.toIntern()).owner_nav; 30044 break :fn_ptr try sema.analyzeNavRef(block, inst_src, fn_nav); 30045 }, 30046 .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), 30047 .float => float: { 30048 const target = zcu.getTarget(); 30049 const double_bits = target.cTypeBitSize(.double); 30050 const inst_bits = uncasted_ty.floatBits(target); 30051 if (inst_bits >= double_bits) break :float inst; 30052 switch (double_bits) { 30053 32 => break :float try sema.coerce(block, .f32, inst, inst_src), 30054 64 => break :float try sema.coerce(block, .f64, inst, inst_src), 30055 else => unreachable, 30056 } 30057 }, 30058 else => if (uncasted_ty.isAbiInt(zcu)) int: { 30059 if (!try sema.validateExternType(uncasted_ty, .param_ty)) break :int inst; 30060 const target = zcu.getTarget(); 30061 const uncasted_info = uncasted_ty.intInfo(zcu); 30062 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { 30063 .signed => .int, 30064 .unsigned => .uint, 30065 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 30066 .signed => .c_int, 30067 .unsigned => .c_uint, 30068 }, inst, inst_src); 30069 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { 30070 .signed => .long, 30071 .unsigned => .ulong, 30072 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 30073 .signed => .c_long, 30074 .unsigned => .c_ulong, 30075 }, inst, inst_src); 30076 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { 30077 .signed => .longlong, 30078 .unsigned => .ulonglong, 30079 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 30080 .signed => .c_longlong, 30081 .unsigned => .c_ulonglong, 30082 }, inst, inst_src); 30083 break :int inst; 30084 } else inst, 30085 }; 30086 30087 const coerced_ty = sema.typeOf(coerced); 30088 if (!try sema.validateExternType(coerced_ty, .param_ty)) { 30089 const msg = msg: { 30090 const msg = try sema.errMsg(inst_src, "cannot pass '{f}' to variadic function", .{coerced_ty.fmt(pt)}); 30091 errdefer msg.destroy(sema.gpa); 30092 30093 try sema.explainWhyTypeIsNotExtern(msg, inst_src, coerced_ty, .param_ty); 30094 30095 try sema.addDeclaredHereNote(msg, coerced_ty); 30096 break :msg msg; 30097 }; 30098 return sema.failWithOwnedErrorMsg(block, msg); 30099 } 30100 return coerced; 30101 } 30102 30103 // TODO migrate callsites to use storePtr2 instead. 30104 fn storePtr( 30105 sema: *Sema, 30106 block: *Block, 30107 src: LazySrcLoc, 30108 ptr: Air.Inst.Ref, 30109 uncasted_operand: Air.Inst.Ref, 30110 ) CompileError!void { 30111 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store; 30112 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag); 30113 } 30114 30115 fn storePtr2( 30116 sema: *Sema, 30117 block: *Block, 30118 src: LazySrcLoc, 30119 ptr: Air.Inst.Ref, 30120 ptr_src: LazySrcLoc, 30121 uncasted_operand: Air.Inst.Ref, 30122 operand_src: LazySrcLoc, 30123 air_tag: Air.Inst.Tag, 30124 ) CompileError!void { 30125 const pt = sema.pt; 30126 const zcu = pt.zcu; 30127 const ptr_ty = sema.typeOf(ptr); 30128 if (ptr_ty.isConstPtr(zcu)) 30129 return sema.fail(block, ptr_src, "cannot assign to constant", .{}); 30130 30131 const elem_ty = ptr_ty.childType(zcu); 30132 30133 // To generate better code for tuples, we detect a tuple operand here, and 30134 // analyze field loads and stores directly. This avoids an extra allocation + memcpy 30135 // which would occur if we used `coerce`. 30136 // However, we avoid this mechanism if the destination element type is a tuple, 30137 // because the regular store will be better for this case. 30138 // If the destination type is a struct we don't want this mechanism to trigger, because 30139 // this code does not handle tuple-to-struct coercion which requires dealing with missing 30140 // fields. 30141 const operand_ty = sema.typeOf(uncasted_operand); 30142 if (operand_ty.isTuple(zcu) and elem_ty.zigTypeTag(zcu) == .array) { 30143 const field_count = operand_ty.structFieldCount(zcu); 30144 var i: u32 = 0; 30145 while (i < field_count) : (i += 1) { 30146 const elem_src = operand_src; // TODO better source location 30147 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); 30148 const elem_index = try pt.intRef(.usize, i); 30149 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true); 30150 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); 30151 } 30152 return; 30153 } 30154 30155 // TODO do the same thing for anon structs as for tuples above. 30156 // However, beware of the need to handle missing/extra fields. 30157 30158 const is_ret = air_tag == .ret_ptr; 30159 30160 // Detect if we are storing an array operand to a bitcasted vector pointer. 30161 // If so, we instead reach through the bitcasted pointer to the vector pointer, 30162 // bitcast the array operand to a vector, and then lower this as a store of 30163 // a vector value to a vector pointer. This generally results in better code, 30164 // as well as working around an LLVM bug: 30165 // https://github.com/ziglang/zig/issues/11154 30166 if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { 30167 const vector_ty = sema.typeOf(vector_ptr).childType(zcu); 30168 const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 30169 error.NotCoercible => unreachable, 30170 else => |e| return e, 30171 }; 30172 try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store); 30173 return; 30174 } 30175 30176 const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 30177 error.NotCoercible => unreachable, 30178 else => |e| return e, 30179 }; 30180 const maybe_operand_val = try sema.resolveValue(operand); 30181 30182 const runtime_src = rs: { 30183 const ptr_val = try sema.resolveDefinedValue(block, ptr_src, ptr) orelse break :rs ptr_src; 30184 if (!sema.isComptimeMutablePtr(ptr_val)) break :rs ptr_src; 30185 const operand_val = maybe_operand_val orelse return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); 30186 return sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); 30187 }; 30188 30189 // We're performing the store at runtime; as such, we need to make sure the pointee type 30190 // is not comptime-only. We can hit this case with a `@ptrFromInt` pointer. 30191 if (try elem_ty.comptimeOnlySema(pt)) { 30192 return sema.failWithOwnedErrorMsg(block, msg: { 30193 const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)}); 30194 errdefer msg.destroy(sema.gpa); 30195 try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{}); 30196 break :msg msg; 30197 }); 30198 } 30199 30200 // We do this after the possible comptime store above, for the case of field_ptr stores 30201 // to unions because we want the comptime tag to be set, even if the field type is void. 30202 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { 30203 return; 30204 } 30205 30206 try sema.requireRuntimeBlock(block, src, runtime_src); 30207 30208 if (ptr_ty.ptrInfo(zcu).flags.vector_index == .runtime) { 30209 const ptr_inst = ptr.toIndex().?; 30210 const air_tags = sema.air_instructions.items(.tag); 30211 if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) { 30212 const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl; 30213 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 30214 _ = try block.addInst(.{ 30215 .tag = .vector_store_elem, 30216 .data = .{ .vector_store_elem = .{ 30217 .vector_ptr = bin_op.lhs, 30218 .payload = try block.sema.addExtra(Air.Bin{ 30219 .lhs = bin_op.rhs, 30220 .rhs = operand, 30221 }), 30222 } }, 30223 }); 30224 return; 30225 } 30226 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{f}'", .{ 30227 ptr_ty.fmt(pt), 30228 }); 30229 } 30230 30231 const store_inst = if (is_ret) 30232 try block.addBinOp(.store, ptr, operand) 30233 else 30234 try block.addBinOp(air_tag, ptr, operand); 30235 30236 try sema.checkComptimeKnownStore(block, store_inst, operand_src); 30237 30238 return; 30239 } 30240 30241 /// Given an AIR store instruction, checks whether we are performing a 30242 /// comptime-known store to a local alloc, and updates `maybe_comptime_allocs` 30243 /// accordingly. 30244 /// Handles calling `validateRuntimeValue` if the store is runtime for any reason. 30245 fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref, store_src: LazySrcLoc) !void { 30246 const store_inst = store_inst_ref.toIndex().?; 30247 const inst_data = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 30248 const ptr = inst_data.lhs.toIndex() orelse return; 30249 const operand = inst_data.rhs; 30250 30251 known: { 30252 const maybe_base_alloc = sema.base_allocs.get(ptr) orelse break :known; 30253 const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse break :known; 30254 30255 if ((try sema.resolveValue(operand)) != null and 30256 block.runtime_index == maybe_comptime_alloc.runtime_index) 30257 { 30258 try maybe_comptime_alloc.stores.append(sema.arena, .{ 30259 .inst = store_inst, 30260 .src = store_src, 30261 }); 30262 return; 30263 } 30264 30265 // We're newly discovering that this alloc is runtime-known. 30266 try sema.markMaybeComptimeAllocRuntime(block, maybe_base_alloc); 30267 } 30268 30269 try sema.validateRuntimeValue(block, store_src, operand); 30270 } 30271 30272 /// Given an AIR instruction transforming a pointer (struct_field_ptr, 30273 /// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a 30274 /// local alloc, and updates `base_allocs` accordingly. 30275 fn checkKnownAllocPtr(sema: *Sema, block: *Block, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void { 30276 const base_ptr_inst = base_ptr.toIndex() orelse return; 30277 const new_ptr_inst = new_ptr.toIndex() orelse return; 30278 const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return; 30279 try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst); 30280 30281 switch (sema.air_instructions.items(.tag)[@intFromEnum(new_ptr_inst)]) { 30282 .optional_payload_ptr_set, .errunion_payload_ptr_set => { 30283 const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return; 30284 30285 // This is functionally a store, since it writes the optional payload bit. 30286 // Thus, if it is behind a runtime condition, we must mark the alloc as runtime appropriately. 30287 if (block.runtime_index != maybe_comptime_alloc.runtime_index) { 30288 return sema.markMaybeComptimeAllocRuntime(block, alloc_inst); 30289 } 30290 30291 try maybe_comptime_alloc.stores.append(sema.arena, .{ 30292 .inst = new_ptr_inst, 30293 .src = LazySrcLoc.unneeded, 30294 }); 30295 }, 30296 .ptr_elem_ptr => { 30297 const tmp_air = sema.getTmpAir(); 30298 const pl_idx = tmp_air.instructions.items(.data)[@intFromEnum(new_ptr_inst)].ty_pl.payload; 30299 const bin = tmp_air.extraData(Air.Bin, pl_idx).data; 30300 const index_ref = bin.rhs; 30301 30302 // If the index value is runtime-known, this pointer is also runtime-known, so 30303 // we must in turn make the alloc value runtime-known. 30304 if (null == try sema.resolveValue(index_ref)) { 30305 try sema.markMaybeComptimeAllocRuntime(block, alloc_inst); 30306 } 30307 }, 30308 else => {}, 30309 } 30310 } 30311 30312 fn markMaybeComptimeAllocRuntime(sema: *Sema, block: *Block, alloc_inst: Air.Inst.Index) CompileError!void { 30313 const maybe_comptime_alloc = (sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return).value; 30314 // Since the alloc has been determined to be runtime, we must check that 30315 // all other stores to it are permitted to be runtime values. 30316 const slice = maybe_comptime_alloc.stores.slice(); 30317 for (slice.items(.inst), slice.items(.src)) |other_inst, other_src| { 30318 if (other_src.offset == .unneeded) { 30319 switch (sema.air_instructions.items(.tag)[@intFromEnum(other_inst)]) { 30320 .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => continue, 30321 else => unreachable, // assertion failure 30322 } 30323 } 30324 const other_data = sema.air_instructions.items(.data)[@intFromEnum(other_inst)].bin_op; 30325 const other_operand = other_data.rhs; 30326 try sema.validateRuntimeValue(block, other_src, other_operand); 30327 } 30328 } 30329 30330 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector 30331 /// pointer. Only if the final element type matches the vector element type, and the 30332 /// lengths match. 30333 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { 30334 const pt = sema.pt; 30335 const zcu = pt.zcu; 30336 const array_ty = sema.typeOf(ptr).childType(zcu); 30337 if (array_ty.zigTypeTag(zcu) != .array) return null; 30338 var ptr_ref = ptr; 30339 var ptr_inst = ptr_ref.toIndex() orelse return null; 30340 const air_datas = sema.air_instructions.items(.data); 30341 const air_tags = sema.air_instructions.items(.tag); 30342 const vector_ty = while (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 30343 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 30344 if (!sema.isKnownZigType(ptr_ref, .pointer)) return null; 30345 const child_ty = sema.typeOf(ptr_ref).childType(zcu); 30346 if (child_ty.zigTypeTag(zcu) == .vector) break child_ty; 30347 ptr_inst = ptr_ref.toIndex() orelse return null; 30348 } else return null; 30349 30350 // We have a pointer-to-array and a pointer-to-vector. If the elements and 30351 // lengths match, return the result. 30352 if (array_ty.childType(zcu).eql(vector_ty.childType(zcu), zcu) and 30353 array_ty.arrayLen(zcu) == vector_ty.vectorLen(zcu)) 30354 { 30355 return ptr_ref; 30356 } else { 30357 return null; 30358 } 30359 } 30360 30361 /// Call when you have Value objects rather than Air instructions, and you want to 30362 /// assert the store must be done at comptime. 30363 fn storePtrVal( 30364 sema: *Sema, 30365 block: *Block, 30366 src: LazySrcLoc, 30367 ptr_val: Value, 30368 operand_val: Value, 30369 operand_ty: Type, 30370 ) !void { 30371 const pt = sema.pt; 30372 const zcu = pt.zcu; 30373 const ip = &zcu.intern_pool; 30374 // TODO: audit use sites to eliminate this coercion 30375 const coerced_operand_val = try pt.getCoerced(operand_val, operand_ty); 30376 // TODO: audit use sites to eliminate this coercion 30377 const ptr_ty = try pt.ptrType(info: { 30378 var info = ptr_val.typeOf(zcu).ptrInfo(zcu); 30379 info.child = operand_ty.toIntern(); 30380 break :info info; 30381 }); 30382 const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty); 30383 30384 switch (try sema.storeComptimePtr(block, src, coerced_ptr_val, coerced_operand_val)) { 30385 .success => {}, 30386 .runtime_store => unreachable, // use sites check this 30387 // TODO use failWithInvalidComptimeFieldStore 30388 .comptime_field_mismatch => return sema.fail( 30389 block, 30390 src, 30391 "value stored in comptime field does not match the default value of the field", 30392 .{}, 30393 ), 30394 .undef => return sema.failWithUseOfUndef(block, src), 30395 .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}), 30396 .null_payload => return sema.fail(block, src, "attempt to use null value", .{}), 30397 .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}), 30398 .needed_well_defined => |ty| return sema.fail( 30399 block, 30400 src, 30401 "comptime dereference requires '{f}' to have a well-defined layout", 30402 .{ty.fmt(pt)}, 30403 ), 30404 .out_of_bounds => |ty| return sema.fail( 30405 block, 30406 src, 30407 "dereference of '{f}' exceeds bounds of containing decl of type '{f}'", 30408 .{ ptr_ty.fmt(pt), ty.fmt(pt) }, 30409 ), 30410 .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}), 30411 } 30412 } 30413 30414 fn bitCast( 30415 sema: *Sema, 30416 block: *Block, 30417 dest_ty: Type, 30418 inst: Air.Inst.Ref, 30419 inst_src: LazySrcLoc, 30420 operand_src: ?LazySrcLoc, 30421 ) CompileError!Air.Inst.Ref { 30422 const pt = sema.pt; 30423 const zcu = pt.zcu; 30424 try dest_ty.resolveLayout(pt); 30425 30426 const old_ty = sema.typeOf(inst); 30427 try old_ty.resolveLayout(pt); 30428 30429 const dest_bits = dest_ty.bitSize(zcu); 30430 const old_bits = old_ty.bitSize(zcu); 30431 30432 if (old_bits != dest_bits) { 30433 return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{ 30434 dest_ty.fmt(pt), 30435 dest_bits, 30436 old_ty.fmt(pt), 30437 old_bits, 30438 }); 30439 } 30440 30441 if (try sema.resolveValue(inst)) |val| { 30442 if (val.isUndef(zcu)) 30443 return pt.undefRef(dest_ty); 30444 if (old_ty.zigTypeTag(zcu) == .error_set and dest_ty.zigTypeTag(zcu) == .error_set) { 30445 // Special case: we sometimes call `bitCast` on error set values, but they 30446 // don't have a well-defined layout, so we can't use `bitCastVal` on them. 30447 return Air.internedToRef((try pt.getCoerced(val, dest_ty)).toIntern()); 30448 } 30449 if (try sema.bitCastVal(val, dest_ty, 0, 0, 0)) |result_val| { 30450 return Air.internedToRef(result_val.toIntern()); 30451 } 30452 } 30453 try sema.requireRuntimeBlock(block, inst_src, operand_src); 30454 try sema.validateRuntimeValue(block, inst_src, inst); 30455 return block.addBitCast(dest_ty, inst); 30456 } 30457 30458 fn coerceArrayPtrToSlice( 30459 sema: *Sema, 30460 block: *Block, 30461 dest_ty: Type, 30462 inst: Air.Inst.Ref, 30463 inst_src: LazySrcLoc, 30464 ) CompileError!Air.Inst.Ref { 30465 const pt = sema.pt; 30466 const zcu = pt.zcu; 30467 if (try sema.resolveValue(inst)) |val| { 30468 const ptr_array_ty = sema.typeOf(inst); 30469 const array_ty = ptr_array_ty.childType(zcu); 30470 const slice_ptr_ty = dest_ty.slicePtrFieldType(zcu); 30471 const slice_ptr = try pt.getCoerced(val, slice_ptr_ty); 30472 const slice_val = try pt.intern(.{ .slice = .{ 30473 .ty = dest_ty.toIntern(), 30474 .ptr = slice_ptr.toIntern(), 30475 .len = (try pt.intValue(.usize, array_ty.arrayLen(zcu))).toIntern(), 30476 } }); 30477 return Air.internedToRef(slice_val); 30478 } 30479 try sema.requireRuntimeBlock(block, inst_src, null); 30480 return block.addTyOp(.array_to_slice, dest_ty, inst); 30481 } 30482 30483 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool { 30484 const pt = sema.pt; 30485 const zcu = pt.zcu; 30486 const dest_info = dest_ty.ptrInfo(zcu); 30487 const inst_info = inst_ty.ptrInfo(zcu); 30488 const len0 = (Type.fromInterned(inst_info.child).zigTypeTag(zcu) == .array and (Type.fromInterned(inst_info.child).arrayLenIncludingSentinel(zcu) == 0 or 30489 (Type.fromInterned(inst_info.child).arrayLen(zcu) == 0 and dest_info.sentinel == .none and dest_info.flags.size != .c and dest_info.flags.size != .many))) or 30490 (Type.fromInterned(inst_info.child).isTuple(zcu) and Type.fromInterned(inst_info.child).structFieldCount(zcu) == 0); 30491 30492 const ok_const = (!inst_info.flags.is_const or dest_info.flags.is_const) or len0; 30493 const ok_volatile = !inst_info.flags.is_volatile or dest_info.flags.is_volatile; 30494 if (!ok_const) { 30495 in_memory_result.* = .{ .ptr_const = .{ 30496 .actual = inst_ty, 30497 .wanted = dest_ty, 30498 } }; 30499 return false; 30500 } 30501 if (!ok_volatile) { 30502 in_memory_result.* = .{ .ptr_volatile = .{ 30503 .actual = inst_ty, 30504 .wanted = dest_ty, 30505 } }; 30506 return false; 30507 } 30508 30509 if (dest_info.flags.address_space != inst_info.flags.address_space) { 30510 in_memory_result.* = .{ .ptr_addrspace = .{ 30511 .actual = inst_info.flags.address_space, 30512 .wanted = dest_info.flags.address_space, 30513 } }; 30514 return false; 30515 } 30516 if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true; 30517 if (len0) return true; 30518 30519 const inst_align = if (inst_info.flags.alignment != .none) 30520 inst_info.flags.alignment 30521 else 30522 Type.fromInterned(inst_info.child).abiAlignment(zcu); 30523 30524 const dest_align = if (dest_info.flags.alignment != .none) 30525 dest_info.flags.alignment 30526 else 30527 Type.fromInterned(dest_info.child).abiAlignment(zcu); 30528 30529 if (dest_align.compare(.gt, inst_align)) { 30530 in_memory_result.* = .{ .ptr_alignment = .{ 30531 .actual = inst_align, 30532 .wanted = dest_align, 30533 } }; 30534 return false; 30535 } 30536 return true; 30537 } 30538 30539 fn coerceCompatiblePtrs( 30540 sema: *Sema, 30541 block: *Block, 30542 dest_ty: Type, 30543 inst: Air.Inst.Ref, 30544 inst_src: LazySrcLoc, 30545 ) !Air.Inst.Ref { 30546 const pt = sema.pt; 30547 const zcu = pt.zcu; 30548 const inst_ty = sema.typeOf(inst); 30549 if (try sema.resolveValue(inst)) |val| { 30550 if (!val.isUndef(zcu) and val.isNull(zcu) and !dest_ty.isAllowzeroPtr(zcu)) { 30551 return sema.fail(block, inst_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)}); 30552 } 30553 // The comptime Value representation is compatible with both types. 30554 return Air.internedToRef( 30555 (try pt.getCoerced(val, dest_ty)).toIntern(), 30556 ); 30557 } 30558 try sema.requireRuntimeBlock(block, inst_src, null); 30559 const inst_allows_zero = inst_ty.zigTypeTag(zcu) != .pointer or inst_ty.ptrAllowsZero(zcu); 30560 if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu) and 30561 (try dest_ty.elemType2(zcu).hasRuntimeBitsSema(pt) or dest_ty.elemType2(zcu).zigTypeTag(zcu) == .@"fn")) 30562 { 30563 try sema.checkLogicalPtrOperation(block, inst_src, inst_ty); 30564 const actual_ptr = if (inst_ty.isSlice(zcu)) 30565 try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) 30566 else 30567 inst; 30568 const ptr_int = try block.addBitCast(.usize, actual_ptr); 30569 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); 30570 const ok = if (inst_ty.isSlice(zcu)) ok: { 30571 const len = try sema.analyzeSliceLen(block, inst_src, inst); 30572 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 30573 break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero); 30574 } else is_non_zero; 30575 try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null); 30576 } 30577 const new_ptr = try sema.bitCast(block, dest_ty, inst, inst_src, null); 30578 try sema.checkKnownAllocPtr(block, inst, new_ptr); 30579 return new_ptr; 30580 } 30581 30582 fn coerceEnumToUnion( 30583 sema: *Sema, 30584 block: *Block, 30585 union_ty: Type, 30586 union_ty_src: LazySrcLoc, 30587 inst: Air.Inst.Ref, 30588 inst_src: LazySrcLoc, 30589 ) !Air.Inst.Ref { 30590 const pt = sema.pt; 30591 const zcu = pt.zcu; 30592 const ip = &zcu.intern_pool; 30593 const inst_ty = sema.typeOf(inst); 30594 30595 const tag_ty = union_ty.unionTagType(zcu) orelse { 30596 const msg = msg: { 30597 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ 30598 union_ty.fmt(pt), inst_ty.fmt(pt), 30599 }); 30600 errdefer msg.destroy(sema.gpa); 30601 try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{}); 30602 try sema.addDeclaredHereNote(msg, union_ty); 30603 break :msg msg; 30604 }; 30605 return sema.failWithOwnedErrorMsg(block, msg); 30606 }; 30607 30608 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src); 30609 if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { 30610 const field_index = union_ty.unionTagFieldIndex(val, pt.zcu) orelse { 30611 return sema.fail(block, inst_src, "union '{f}' has no tag with value '{f}'", .{ 30612 union_ty.fmt(pt), val.fmtValueSema(pt, sema), 30613 }); 30614 }; 30615 30616 const union_obj = zcu.typeToUnion(union_ty).?; 30617 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 30618 try field_ty.resolveFields(pt); 30619 if (field_ty.zigTypeTag(zcu) == .noreturn) { 30620 const msg = msg: { 30621 const msg = try sema.errMsg(inst_src, "cannot initialize 'noreturn' field of union", .{}); 30622 errdefer msg.destroy(sema.gpa); 30623 30624 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; 30625 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ 30626 field_name.fmt(ip), 30627 }); 30628 try sema.addDeclaredHereNote(msg, union_ty); 30629 break :msg msg; 30630 }; 30631 return sema.failWithOwnedErrorMsg(block, msg); 30632 } 30633 const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { 30634 const msg = msg: { 30635 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; 30636 const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{ 30637 inst_ty.fmt(pt), union_ty.fmt(pt), 30638 field_ty.fmt(pt), field_name.fmt(ip), 30639 }); 30640 errdefer msg.destroy(sema.gpa); 30641 30642 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ 30643 field_name.fmt(ip), 30644 }); 30645 try sema.addDeclaredHereNote(msg, union_ty); 30646 break :msg msg; 30647 }; 30648 return sema.failWithOwnedErrorMsg(block, msg); 30649 }; 30650 30651 return Air.internedToRef((try pt.unionValue(union_ty, val, opv)).toIntern()); 30652 } 30653 30654 try sema.requireRuntimeBlock(block, inst_src, null); 30655 30656 if (tag_ty.isNonexhaustiveEnum(zcu)) { 30657 const msg = msg: { 30658 const msg = try sema.errMsg(inst_src, "runtime coercion to union '{f}' from non-exhaustive enum", .{ 30659 union_ty.fmt(pt), 30660 }); 30661 errdefer msg.destroy(sema.gpa); 30662 try sema.addDeclaredHereNote(msg, tag_ty); 30663 break :msg msg; 30664 }; 30665 return sema.failWithOwnedErrorMsg(block, msg); 30666 } 30667 30668 const union_obj = zcu.typeToUnion(union_ty).?; 30669 { 30670 var msg: ?*Zcu.ErrorMsg = null; 30671 errdefer if (msg) |some| some.destroy(sema.gpa); 30672 30673 for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { 30674 if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .noreturn) { 30675 const err_msg = msg orelse try sema.errMsg( 30676 inst_src, 30677 "runtime coercion from enum '{f}' to union '{f}' which has a 'noreturn' field", 30678 .{ tag_ty.fmt(pt), union_ty.fmt(pt) }, 30679 ); 30680 msg = err_msg; 30681 30682 try sema.addFieldErrNote(union_ty, field_index, err_msg, "'noreturn' field here", .{}); 30683 } 30684 } 30685 if (msg) |some| { 30686 msg = null; 30687 try sema.addDeclaredHereNote(some, union_ty); 30688 return sema.failWithOwnedErrorMsg(block, some); 30689 } 30690 } 30691 30692 // If the union has all fields 0 bits, the union value is just the enum value. 30693 if (union_ty.unionHasAllZeroBitFieldTypes(zcu)) { 30694 return block.addBitCast(union_ty, enum_tag); 30695 } 30696 30697 const msg = msg: { 30698 const msg = try sema.errMsg( 30699 inst_src, 30700 "runtime coercion from enum '{f}' to union '{f}' which has non-void fields", 30701 .{ tag_ty.fmt(pt), union_ty.fmt(pt) }, 30702 ); 30703 errdefer msg.destroy(sema.gpa); 30704 30705 for (0..union_obj.field_types.len) |field_index| { 30706 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; 30707 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 30708 if (!(try field_ty.hasRuntimeBitsSema(pt))) continue; 30709 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has type '{f}'", .{ 30710 field_name.fmt(ip), 30711 field_ty.fmt(pt), 30712 }); 30713 } 30714 try sema.addDeclaredHereNote(msg, union_ty); 30715 break :msg msg; 30716 }; 30717 return sema.failWithOwnedErrorMsg(block, msg); 30718 } 30719 30720 /// If the lengths match, coerces element-wise. 30721 fn coerceArrayLike( 30722 sema: *Sema, 30723 block: *Block, 30724 dest_ty: Type, 30725 dest_ty_src: LazySrcLoc, 30726 inst: Air.Inst.Ref, 30727 inst_src: LazySrcLoc, 30728 ) !Air.Inst.Ref { 30729 const pt = sema.pt; 30730 const zcu = pt.zcu; 30731 const inst_ty = sema.typeOf(inst); 30732 const target = zcu.getTarget(); 30733 30734 // try coercion of the whole array 30735 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, null); 30736 if (in_memory_result == .ok) { 30737 if (try sema.resolveValue(inst)) |inst_val| { 30738 // These types share the same comptime value representation. 30739 return sema.coerceInMemory(inst_val, dest_ty); 30740 } 30741 try sema.requireRuntimeBlock(block, inst_src, null); 30742 return block.addBitCast(dest_ty, inst); 30743 } 30744 30745 // otherwise, try element by element 30746 const inst_len = inst_ty.arrayLen(zcu); 30747 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu)); 30748 if (dest_len != inst_len) { 30749 const msg = msg: { 30750 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ 30751 dest_ty.fmt(pt), inst_ty.fmt(pt), 30752 }); 30753 errdefer msg.destroy(sema.gpa); 30754 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len}); 30755 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len}); 30756 break :msg msg; 30757 }; 30758 return sema.failWithOwnedErrorMsg(block, msg); 30759 } 30760 30761 const dest_elem_ty = dest_ty.childType(zcu); 30762 if (dest_ty.isVector(zcu) and inst_ty.isVector(zcu) and (try sema.resolveValue(inst)) == null) { 30763 const inst_elem_ty = inst_ty.childType(zcu); 30764 switch (dest_elem_ty.zigTypeTag(zcu)) { 30765 .int => if (inst_elem_ty.isInt(zcu)) { 30766 // integer widening 30767 const dst_info = dest_elem_ty.intInfo(zcu); 30768 const src_info = inst_elem_ty.intInfo(zcu); 30769 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or 30770 // small enough unsigned ints can get casted to large enough signed ints 30771 (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) 30772 { 30773 try sema.requireRuntimeBlock(block, inst_src, null); 30774 return block.addTyOp(.intcast, dest_ty, inst); 30775 } 30776 }, 30777 .float => if (inst_elem_ty.isRuntimeFloat()) { 30778 // float widening 30779 const src_bits = inst_elem_ty.floatBits(target); 30780 const dst_bits = dest_elem_ty.floatBits(target); 30781 if (dst_bits >= src_bits) { 30782 try sema.requireRuntimeBlock(block, inst_src, null); 30783 return block.addTyOp(.fpext, dest_ty, inst); 30784 } 30785 }, 30786 else => {}, 30787 } 30788 } 30789 30790 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); 30791 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); 30792 var runtime_src: ?LazySrcLoc = null; 30793 30794 for (element_vals, element_refs, 0..) |*val, *ref, i| { 30795 const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern()); 30796 const src = inst_src; // TODO better source location 30797 const elem_src = inst_src; // TODO better source location 30798 const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true); 30799 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 30800 ref.* = coerced; 30801 if (runtime_src == null) { 30802 if (try sema.resolveValue(coerced)) |elem_val| { 30803 val.* = elem_val.toIntern(); 30804 } else { 30805 runtime_src = elem_src; 30806 } 30807 } 30808 } 30809 30810 if (runtime_src) |rs| { 30811 try sema.requireRuntimeBlock(block, inst_src, rs); 30812 return block.addAggregateInit(dest_ty, element_refs); 30813 } 30814 30815 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 30816 .ty = dest_ty.toIntern(), 30817 .storage = .{ .elems = element_vals }, 30818 } }))); 30819 } 30820 30821 /// If the lengths match, coerces element-wise. 30822 fn coerceTupleToArray( 30823 sema: *Sema, 30824 block: *Block, 30825 dest_ty: Type, 30826 dest_ty_src: LazySrcLoc, 30827 inst: Air.Inst.Ref, 30828 inst_src: LazySrcLoc, 30829 ) !Air.Inst.Ref { 30830 const pt = sema.pt; 30831 const zcu = pt.zcu; 30832 const inst_ty = sema.typeOf(inst); 30833 const inst_len = inst_ty.arrayLen(zcu); 30834 const dest_len = dest_ty.arrayLen(zcu); 30835 30836 if (dest_len != inst_len) { 30837 const msg = msg: { 30838 const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ 30839 dest_ty.fmt(pt), inst_ty.fmt(pt), 30840 }); 30841 errdefer msg.destroy(sema.gpa); 30842 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len}); 30843 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len}); 30844 break :msg msg; 30845 }; 30846 return sema.failWithOwnedErrorMsg(block, msg); 30847 } 30848 30849 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len); 30850 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems); 30851 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); 30852 const dest_elem_ty = dest_ty.childType(zcu); 30853 30854 var runtime_src: ?LazySrcLoc = null; 30855 for (element_vals, element_refs, 0..) |*val, *ref, i_usize| { 30856 const i: u32 = @intCast(i_usize); 30857 if (i_usize == inst_len) { 30858 const sentinel_val = dest_ty.sentinel(zcu).?; 30859 val.* = sentinel_val.toIntern(); 30860 ref.* = Air.internedToRef(sentinel_val.toIntern()); 30861 break; 30862 } 30863 const elem_src = inst_src; // TODO better source location 30864 const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i); 30865 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 30866 ref.* = coerced; 30867 if (runtime_src == null) { 30868 if (try sema.resolveValue(coerced)) |elem_val| { 30869 val.* = elem_val.toIntern(); 30870 } else { 30871 runtime_src = elem_src; 30872 } 30873 } 30874 } 30875 30876 if (runtime_src) |rs| { 30877 try sema.requireRuntimeBlock(block, inst_src, rs); 30878 return block.addAggregateInit(dest_ty, element_refs); 30879 } 30880 30881 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 30882 .ty = dest_ty.toIntern(), 30883 .storage = .{ .elems = element_vals }, 30884 } }))); 30885 } 30886 30887 /// If the lengths match, coerces element-wise. 30888 fn coerceTupleToSlicePtrs( 30889 sema: *Sema, 30890 block: *Block, 30891 slice_ty: Type, 30892 slice_ty_src: LazySrcLoc, 30893 ptr_tuple: Air.Inst.Ref, 30894 tuple_src: LazySrcLoc, 30895 ) !Air.Inst.Ref { 30896 const pt = sema.pt; 30897 const zcu = pt.zcu; 30898 const tuple_ty = sema.typeOf(ptr_tuple).childType(zcu); 30899 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 30900 const slice_info = slice_ty.ptrInfo(zcu); 30901 const array_ty = try pt.arrayType(.{ 30902 .len = tuple_ty.structFieldCount(zcu), 30903 .sentinel = slice_info.sentinel, 30904 .child = slice_info.child, 30905 }); 30906 const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); 30907 if (slice_info.flags.alignment != .none) { 30908 return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 30909 } 30910 const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst); 30911 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src); 30912 } 30913 30914 /// If the lengths match, coerces element-wise. 30915 fn coerceTupleToArrayPtrs( 30916 sema: *Sema, 30917 block: *Block, 30918 ptr_array_ty: Type, 30919 array_ty_src: LazySrcLoc, 30920 ptr_tuple: Air.Inst.Ref, 30921 tuple_src: LazySrcLoc, 30922 ) !Air.Inst.Ref { 30923 const pt = sema.pt; 30924 const zcu = pt.zcu; 30925 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 30926 const ptr_info = ptr_array_ty.ptrInfo(zcu); 30927 const array_ty: Type = .fromInterned(ptr_info.child); 30928 const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src); 30929 if (ptr_info.flags.alignment != .none) { 30930 return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 30931 } 30932 const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst); 30933 return ptr_array; 30934 } 30935 30936 fn coerceTupleToTuple( 30937 sema: *Sema, 30938 block: *Block, 30939 tuple_ty: Type, 30940 inst: Air.Inst.Ref, 30941 inst_src: LazySrcLoc, 30942 ) !Air.Inst.Ref { 30943 const pt = sema.pt; 30944 const zcu = pt.zcu; 30945 const ip = &zcu.intern_pool; 30946 const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) { 30947 .tuple_type => |tuple_type| tuple_type.types.len, 30948 else => unreachable, 30949 }; 30950 const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); 30951 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); 30952 @memset(field_refs, .none); 30953 30954 const inst_ty = sema.typeOf(inst); 30955 const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) { 30956 .tuple_type => |tuple_type| tuple_type.types.len, 30957 else => unreachable, 30958 }; 30959 if (src_field_count > dest_field_count) return error.NotCoercible; 30960 30961 var runtime_src: ?LazySrcLoc = null; 30962 for (0..dest_field_count) |field_index_usize| { 30963 const field_i: u32 = @intCast(field_index_usize); 30964 const field_src = inst_src; // TODO better source location 30965 30966 const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { 30967 .tuple_type => |tuple_type| tuple_type.types.get(ip)[field_index_usize], 30968 .struct_type => ip.loadStructType(tuple_ty.toIntern()).field_types.get(ip)[field_index_usize], 30969 else => unreachable, 30970 }; 30971 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 30972 .tuple_type => |tuple_type| tuple_type.values.get(ip)[field_index_usize], 30973 .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, field_index_usize), 30974 else => unreachable, 30975 }; 30976 30977 const field_index: u32 = @intCast(field_index_usize); 30978 30979 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); 30980 const coerced = try sema.coerce(block, .fromInterned(field_ty), elem_ref, field_src); 30981 field_refs[field_index] = coerced; 30982 if (default_val != .none) { 30983 const init_val = (try sema.resolveValue(coerced)) orelse { 30984 return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); 30985 }; 30986 30987 if (!init_val.eql(Value.fromInterned(default_val), .fromInterned(field_ty), pt.zcu)) { 30988 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); 30989 } 30990 } 30991 if (runtime_src == null) { 30992 if (try sema.resolveValue(coerced)) |field_val| { 30993 field_vals[field_index] = field_val.toIntern(); 30994 } else { 30995 runtime_src = field_src; 30996 } 30997 } 30998 } 30999 31000 // Populate default field values and report errors for missing fields. 31001 var root_msg: ?*Zcu.ErrorMsg = null; 31002 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 31003 31004 for (field_refs, 0..) |*field_ref, i_usize| { 31005 const i: u32 = @intCast(i_usize); 31006 if (field_ref.* != .none) continue; 31007 31008 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 31009 .tuple_type => |tuple_type| tuple_type.values.get(ip)[i], 31010 .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, i), 31011 else => unreachable, 31012 }; 31013 31014 const field_src = inst_src; // TODO better source location 31015 if (default_val == .none) { 31016 const template = "missing tuple field: {d}"; 31017 if (root_msg) |msg| { 31018 try sema.errNote(field_src, msg, template, .{i}); 31019 } else { 31020 root_msg = try sema.errMsg(field_src, template, .{i}); 31021 } 31022 continue; 31023 } 31024 if (runtime_src == null) { 31025 field_vals[i] = default_val; 31026 } else { 31027 field_ref.* = Air.internedToRef(default_val); 31028 } 31029 } 31030 31031 if (root_msg) |msg| { 31032 try sema.addDeclaredHereNote(msg, tuple_ty); 31033 root_msg = null; 31034 return sema.failWithOwnedErrorMsg(block, msg); 31035 } 31036 31037 if (runtime_src) |rs| { 31038 try sema.requireRuntimeBlock(block, inst_src, rs); 31039 return block.addAggregateInit(tuple_ty, field_refs); 31040 } 31041 31042 return Air.internedToRef((try pt.intern(.{ .aggregate = .{ 31043 .ty = tuple_ty.toIntern(), 31044 .storage = .{ .elems = field_vals }, 31045 } }))); 31046 } 31047 31048 fn analyzeNavVal( 31049 sema: *Sema, 31050 block: *Block, 31051 src: LazySrcLoc, 31052 nav_index: InternPool.Nav.Index, 31053 ) CompileError!Air.Inst.Ref { 31054 const ref = try sema.analyzeNavRefInner(block, src, nav_index, false); 31055 return sema.analyzeLoad(block, src, ref, src); 31056 } 31057 31058 fn addReferenceEntry( 31059 sema: *Sema, 31060 opt_block: ?*Block, 31061 src: LazySrcLoc, 31062 referenced_unit: AnalUnit, 31063 ) !void { 31064 const zcu = sema.pt.zcu; 31065 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return; 31066 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit); 31067 if (gop.found_existing) return; 31068 try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: { 31069 const block = opt_block orelse break :inline_frame .none; 31070 const inlining = block.inlining orelse break :inline_frame .none; 31071 const frame = try inlining.refFrame(zcu); 31072 break :inline_frame frame.toOptional(); 31073 }); 31074 } 31075 31076 pub fn addTypeReferenceEntry( 31077 sema: *Sema, 31078 src: LazySrcLoc, 31079 referenced_type: InternPool.Index, 31080 ) !void { 31081 const zcu = sema.pt.zcu; 31082 if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return; 31083 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type); 31084 if (gop.found_existing) return; 31085 try zcu.addTypeReference(sema.owner, referenced_type, src); 31086 } 31087 31088 fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void { 31089 const pt = sema.pt; 31090 31091 const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); 31092 try sema.addReferenceEntry(null, src, unit); 31093 try sema.declareDependency(.{ .memoized_state = stage }); 31094 31095 if (pt.zcu.analysis_in_progress.contains(unit)) { 31096 return sema.failWithOwnedErrorMsg(null, try sema.errMsg(src, "dependency loop detected", .{})); 31097 } 31098 try pt.ensureMemoizedStateUpToDate(stage); 31099 } 31100 31101 pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void { 31102 const pt = sema.pt; 31103 const zcu = pt.zcu; 31104 const ip = &zcu.intern_pool; 31105 31106 const nav = ip.getNav(nav_index); 31107 if (nav.analysis == null) { 31108 assert(nav.status == .fully_resolved); 31109 return; 31110 } 31111 31112 try sema.declareDependency(switch (kind) { 31113 .type => .{ .nav_ty = nav_index }, 31114 .fully => .{ .nav_val = nav_index }, 31115 }); 31116 31117 // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate` 31118 // to make sure the value is up-to-date on incremental updates. 31119 31120 const anal_unit: AnalUnit = .wrap(switch (kind) { 31121 .type => .{ .nav_ty = nav_index }, 31122 .fully => .{ .nav_val = nav_index }, 31123 }); 31124 try sema.addReferenceEntry(block, src, anal_unit); 31125 31126 if (zcu.analysis_in_progress.contains(anal_unit)) { 31127 return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{ 31128 .base_node_inst = nav.analysis.?.zir_index, 31129 .offset = LazySrcLoc.Offset.nodeOffset(.zero), 31130 }, "dependency loop detected", .{})); 31131 } 31132 31133 switch (kind) { 31134 .type => { 31135 try zcu.ensureNavValAnalysisQueued(nav_index); 31136 return pt.ensureNavTypeUpToDate(nav_index); 31137 }, 31138 .fully => return pt.ensureNavValUpToDate(nav_index), 31139 } 31140 } 31141 31142 fn optRefValue(sema: *Sema, opt_val: ?Value) !Value { 31143 const pt = sema.pt; 31144 const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque); 31145 return Value.fromInterned(try pt.intern(.{ .opt = .{ 31146 .ty = (try pt.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(), 31147 .val = if (opt_val) |val| (try pt.getCoerced( 31148 Value.fromInterned(try pt.refValue(val.toIntern())), 31149 ptr_anyopaque_ty, 31150 )).toIntern() else .none, 31151 } })); 31152 } 31153 31154 fn analyzeNavRef(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!Air.Inst.Ref { 31155 return sema.analyzeNavRefInner(block, src, nav_index, true); 31156 } 31157 31158 /// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed. 31159 /// If this pointer will be used directly, `is_ref` must be `true`. 31160 /// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`. 31161 fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref { 31162 const pt = sema.pt; 31163 const zcu = pt.zcu; 31164 const ip = &zcu.intern_pool; 31165 31166 try sema.ensureNavResolved(block, src, orig_nav_index, if (is_ref) .type else .fully); 31167 31168 const nav_index = nav: { 31169 if (ip.getNav(orig_nav_index).isExternOrFn(ip)) { 31170 // Getting a pointer to this `Nav` might mean we actually get a pointer to something else! 31171 // We need to resolve the value to know for sure. 31172 if (is_ref) try sema.ensureNavResolved(block, src, orig_nav_index, .fully); 31173 switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) { 31174 .func => |f| break :nav f.owner_nav, 31175 .@"extern" => |e| break :nav e.owner_nav, 31176 else => {}, 31177 } 31178 } 31179 break :nav orig_nav_index; 31180 }; 31181 31182 const nav_status = ip.getNav(nav_index).status; 31183 31184 const is_runtime = switch (nav_status) { 31185 .unresolved => unreachable, 31186 // dllimports go straight to `fully_resolved`; the only option is threadlocal 31187 .type_resolved => |r| r.is_threadlocal, 31188 .fully_resolved => |r| switch (ip.indexToKey(r.val)) { 31189 .@"extern" => |e| e.is_threadlocal or e.is_dll_import or switch (e.relocation) { 31190 .any => false, 31191 .pcrel => true, 31192 }, 31193 .variable => |v| v.is_threadlocal, 31194 else => false, 31195 }, 31196 }; 31197 31198 const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) { 31199 .unresolved => unreachable, 31200 .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, 31201 .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const }, 31202 }; 31203 const ptr_ty = try pt.ptrTypeSema(.{ 31204 .child = ty, 31205 .flags = .{ 31206 .alignment = alignment, 31207 .is_const = is_const, 31208 .address_space = @"addrspace", 31209 }, 31210 }); 31211 31212 if (is_runtime) { 31213 // This pointer is runtime-known; we need to emit an AIR instruction to create it. 31214 return block.addInst(.{ 31215 .tag = .runtime_nav_ptr, 31216 .data = .{ .ty_nav = .{ 31217 .ty = ptr_ty.toIntern(), 31218 .nav = nav_index, 31219 } }, 31220 }); 31221 } 31222 31223 if (is_ref) { 31224 try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index); 31225 } 31226 31227 return Air.internedToRef((try pt.intern(.{ .ptr = .{ 31228 .ty = ptr_ty.toIntern(), 31229 .base_addr = .{ .nav = nav_index }, 31230 .byte_offset = 0, 31231 } }))); 31232 } 31233 31234 fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void { 31235 const pt = sema.pt; 31236 const zcu = pt.zcu; 31237 const ip = &zcu.intern_pool; 31238 31239 // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function. 31240 // If it is, we can resolve the *value*, and queue analysis as needed. 31241 31242 try sema.ensureNavResolved(block, src, nav_index, .type); 31243 const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip)); 31244 if (nav_ty.zigTypeTag(zcu) != .@"fn") return; 31245 if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return; 31246 31247 try sema.ensureNavResolved(block, src, nav_index, .fully); 31248 const nav_val = zcu.navValue(nav_index); 31249 if (!ip.isFuncBody(nav_val.toIntern())) return; 31250 31251 try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = nav_val.toIntern() })); 31252 try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern()); 31253 } 31254 31255 fn analyzeRef( 31256 sema: *Sema, 31257 block: *Block, 31258 src: LazySrcLoc, 31259 operand: Air.Inst.Ref, 31260 ) CompileError!Air.Inst.Ref { 31261 const pt = sema.pt; 31262 const zcu = pt.zcu; 31263 const operand_ty = sema.typeOf(operand); 31264 31265 if (try sema.resolveValue(operand)) |val| { 31266 switch (zcu.intern_pool.indexToKey(val.toIntern())) { 31267 .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav), 31268 .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav), 31269 else => return uavRef(sema, val.toIntern()), 31270 } 31271 } 31272 31273 // No `requireRuntimeBlock`; it's okay to `ref` to a runtime value in a comptime context, 31274 // it's just that we can only use the *type* of the result, since the value is runtime-known. 31275 31276 const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local); 31277 const ptr_type = try pt.ptrTypeSema(.{ 31278 .child = operand_ty.toIntern(), 31279 .flags = .{ 31280 .is_const = true, 31281 .address_space = address_space, 31282 }, 31283 }); 31284 const mut_ptr_type = try pt.ptrTypeSema(.{ 31285 .child = operand_ty.toIntern(), 31286 .flags = .{ .address_space = address_space }, 31287 }); 31288 const alloc = try block.addTy(.alloc, mut_ptr_type); 31289 31290 // In a comptime context, the store would fail, since the operand is runtime-known. But that's 31291 // okay; we don't actually need this store to succeed, since we're creating a runtime value in a 31292 // comptime scope, so the value can never be used aside from to get its type. 31293 if (!block.isComptime()) { 31294 try sema.storePtr(block, src, alloc, operand); 31295 } 31296 31297 // Cast to the constant pointer type. We do this directly rather than going via `coerce` to 31298 // avoid errors in the `block.isComptime()` case. 31299 return block.addBitCast(ptr_type, alloc); 31300 } 31301 31302 fn analyzeLoad( 31303 sema: *Sema, 31304 block: *Block, 31305 src: LazySrcLoc, 31306 ptr: Air.Inst.Ref, 31307 ptr_src: LazySrcLoc, 31308 ) CompileError!Air.Inst.Ref { 31309 const pt = sema.pt; 31310 const zcu = pt.zcu; 31311 const ptr_ty = sema.typeOf(ptr); 31312 const elem_ty = switch (ptr_ty.zigTypeTag(zcu)) { 31313 .pointer => ptr_ty.childType(zcu), 31314 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}), 31315 }; 31316 if (elem_ty.zigTypeTag(zcu) == .@"opaque") { 31317 return sema.fail(block, ptr_src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)}); 31318 } 31319 31320 if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| { 31321 return Air.internedToRef(opv.toIntern()); 31322 } 31323 31324 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 31325 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| { 31326 return Air.internedToRef(elem_val.toIntern()); 31327 } 31328 } 31329 31330 if (ptr_ty.ptrInfo(zcu).flags.vector_index == .runtime) { 31331 const ptr_inst = ptr.toIndex().?; 31332 const air_tags = sema.air_instructions.items(.tag); 31333 if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) { 31334 const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl; 31335 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 31336 return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs); 31337 } 31338 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{f}'", .{ 31339 ptr_ty.fmt(pt), 31340 }); 31341 } 31342 31343 return block.addTyOp(.load, elem_ty, ptr); 31344 } 31345 31346 fn analyzeSlicePtr( 31347 sema: *Sema, 31348 block: *Block, 31349 slice_src: LazySrcLoc, 31350 slice: Air.Inst.Ref, 31351 slice_ty: Type, 31352 ) CompileError!Air.Inst.Ref { 31353 const pt = sema.pt; 31354 const zcu = pt.zcu; 31355 const result_ty = slice_ty.slicePtrFieldType(zcu); 31356 if (try sema.resolveValue(slice)) |val| { 31357 if (val.isUndef(zcu)) return pt.undefRef(result_ty); 31358 return Air.internedToRef(val.slicePtr(zcu).toIntern()); 31359 } 31360 try sema.requireRuntimeBlock(block, slice_src, null); 31361 return block.addTyOp(.slice_ptr, result_ty, slice); 31362 } 31363 31364 fn analyzeOptionalSlicePtr( 31365 sema: *Sema, 31366 block: *Block, 31367 opt_slice_src: LazySrcLoc, 31368 opt_slice: Air.Inst.Ref, 31369 opt_slice_ty: Type, 31370 ) CompileError!Air.Inst.Ref { 31371 const pt = sema.pt; 31372 const zcu = pt.zcu; 31373 const slice_ty = opt_slice_ty.optionalChild(zcu); 31374 const result_ty = slice_ty.slicePtrFieldType(zcu); 31375 31376 if (try sema.resolveValue(opt_slice)) |opt_val| { 31377 if (opt_val.isUndef(zcu)) return pt.undefRef(result_ty); 31378 const slice_ptr: InternPool.Index = if (opt_val.optionalValue(zcu)) |val| 31379 val.slicePtr(zcu).toIntern() 31380 else 31381 .null_value; 31382 31383 return Air.internedToRef(slice_ptr); 31384 } 31385 31386 try sema.requireRuntimeBlock(block, opt_slice_src, null); 31387 31388 const slice = try block.addTyOp(.optional_payload, slice_ty, opt_slice); 31389 return block.addTyOp(.slice_ptr, result_ty, slice); 31390 } 31391 31392 fn analyzeSliceLen( 31393 sema: *Sema, 31394 block: *Block, 31395 src: LazySrcLoc, 31396 slice_inst: Air.Inst.Ref, 31397 ) CompileError!Air.Inst.Ref { 31398 const pt = sema.pt; 31399 const zcu = pt.zcu; 31400 if (try sema.resolveValue(slice_inst)) |slice_val| { 31401 if (slice_val.isUndef(zcu)) { 31402 return .undef_usize; 31403 } 31404 return pt.intRef(.usize, try slice_val.sliceLen(pt)); 31405 } 31406 try sema.requireRuntimeBlock(block, src, null); 31407 return block.addTyOp(.slice_len, .usize, slice_inst); 31408 } 31409 31410 fn analyzeIsNull( 31411 sema: *Sema, 31412 block: *Block, 31413 operand: Air.Inst.Ref, 31414 invert_logic: bool, 31415 ) CompileError!Air.Inst.Ref { 31416 const pt = sema.pt; 31417 const zcu = pt.zcu; 31418 const result_ty: Type = .bool; 31419 if (try sema.resolveValue(operand)) |opt_val| { 31420 if (opt_val.isUndef(zcu)) { 31421 return pt.undefRef(result_ty); 31422 } 31423 const is_null = opt_val.isNull(zcu); 31424 const bool_value = if (invert_logic) !is_null else is_null; 31425 return if (bool_value) .bool_true else .bool_false; 31426 } 31427 31428 if (sema.typeOf(operand).isNullFromType(zcu)) |is_null| { 31429 const result = is_null != invert_logic; 31430 return if (result) .bool_true else .bool_false; 31431 } 31432 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; 31433 return block.addUnOp(air_tag, operand); 31434 } 31435 31436 fn analyzePtrIsNonErrComptimeOnly( 31437 sema: *Sema, 31438 block: *Block, 31439 src: LazySrcLoc, 31440 operand: Air.Inst.Ref, 31441 ) CompileError!Air.Inst.Ref { 31442 const pt = sema.pt; 31443 const zcu = pt.zcu; 31444 const ptr_ty = sema.typeOf(operand); 31445 assert(ptr_ty.zigTypeTag(zcu) == .pointer); 31446 const child_ty = ptr_ty.childType(zcu); 31447 31448 const child_tag = child_ty.zigTypeTag(zcu); 31449 if (child_tag != .error_set and child_tag != .error_union) return .bool_true; 31450 if (child_tag == .error_set) return .bool_false; 31451 assert(child_tag == .error_union); 31452 31453 _ = block; 31454 _ = src; 31455 31456 return .none; 31457 } 31458 31459 fn analyzeIsNonErrComptimeOnly( 31460 sema: *Sema, 31461 block: *Block, 31462 src: LazySrcLoc, 31463 operand: Air.Inst.Ref, 31464 ) CompileError!Air.Inst.Ref { 31465 const pt = sema.pt; 31466 const zcu = pt.zcu; 31467 const ip = &zcu.intern_pool; 31468 const operand_ty = sema.typeOf(operand); 31469 const ot = operand_ty.zigTypeTag(zcu); 31470 if (ot != .error_set and ot != .error_union) return .bool_true; 31471 if (ot == .error_set) return .bool_false; 31472 assert(ot == .error_union); 31473 31474 const payload_ty = operand_ty.errorUnionPayload(zcu); 31475 if (payload_ty.zigTypeTag(zcu) == .noreturn) { 31476 return .bool_false; 31477 } 31478 31479 if (operand.toIndex()) |operand_inst| { 31480 switch (sema.air_instructions.items(.tag)[@intFromEnum(operand_inst)]) { 31481 .wrap_errunion_payload => return .bool_true, 31482 .wrap_errunion_err => return .bool_false, 31483 else => {}, 31484 } 31485 } else if (operand == .undef) { 31486 return .undef_bool; 31487 } else if (@intFromEnum(operand) < InternPool.static_len) { 31488 // None of the ref tags can be errors. 31489 return .bool_true; 31490 } 31491 31492 const maybe_operand_val = try sema.resolveValue(operand); 31493 31494 // exception if the error union error set is known to be empty, 31495 // we allow the comparison but always make it comptime-known. 31496 const set_ty = ip.errorUnionSet(operand_ty.toIntern()); 31497 switch (set_ty) { 31498 .anyerror_type => {}, 31499 .adhoc_inferred_error_set_type => if (sema.fn_ret_ty_ies) |ies| blk: { 31500 // If the error set is empty, we must return a comptime true or false. 31501 // However we want to avoid unnecessarily resolving an inferred error set 31502 // in case it is already non-empty. 31503 switch (ies.resolved) { 31504 .anyerror_type => break :blk, 31505 .none => {}, 31506 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, 31507 } 31508 31509 if (maybe_operand_val != null) break :blk; 31510 31511 // Try to avoid resolving inferred error set if possible. 31512 if (ies.errors.count() != 0) return .none; 31513 switch (ies.resolved) { 31514 .anyerror_type => return .none, 31515 .none => {}, 31516 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) { 31517 0 => return .bool_true, 31518 else => return .none, 31519 }, 31520 } 31521 // We do not have a comptime answer because this inferred error 31522 // set is not resolved, and an instruction later in this function 31523 // body may or may not cause an error to be added to this set. 31524 return .none; 31525 }, 31526 else => switch (ip.indexToKey(set_ty)) { 31527 .error_set_type => |error_set_type| { 31528 if (error_set_type.names.len == 0) return .bool_true; 31529 }, 31530 .inferred_error_set_type => |func_index| blk: { 31531 // If the error set is empty, we must return a comptime true or false. 31532 // However we want to avoid unnecessarily resolving an inferred error set 31533 // in case it is already non-empty. 31534 try zcu.maybeUnresolveIes(func_index); 31535 switch (ip.funcIesResolvedUnordered(func_index)) { 31536 .anyerror_type => break :blk, 31537 .none => {}, 31538 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, 31539 } 31540 if (maybe_operand_val != null) break :blk; 31541 if (sema.fn_ret_ty_ies) |ies| { 31542 if (ies.func == func_index) { 31543 // Try to avoid resolving inferred error set if possible. 31544 if (ies.errors.count() != 0) return .none; 31545 switch (ies.resolved) { 31546 .anyerror_type => return .none, 31547 .none => {}, 31548 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) { 31549 0 => return .bool_true, 31550 else => return .none, 31551 }, 31552 } 31553 // We do not have a comptime answer because this inferred error 31554 // set is not resolved, and an instruction later in this function 31555 // body may or may not cause an error to be added to this set. 31556 return .none; 31557 } 31558 } 31559 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty); 31560 if (resolved_ty == .anyerror_type) 31561 break :blk; 31562 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0) 31563 return .bool_true; 31564 }, 31565 else => unreachable, 31566 }, 31567 } 31568 31569 if (maybe_operand_val) |err_union| { 31570 return if (err_union.isUndef(zcu)) .undef_bool else if (err_union.getErrorName(zcu) == .none) .bool_true else .bool_false; 31571 } 31572 return .none; 31573 } 31574 31575 fn analyzeIsNonErr( 31576 sema: *Sema, 31577 block: *Block, 31578 src: LazySrcLoc, 31579 operand: Air.Inst.Ref, 31580 ) CompileError!Air.Inst.Ref { 31581 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); 31582 if (result == .none) { 31583 try sema.requireRuntimeBlock(block, src, null); 31584 return block.addUnOp(.is_non_err, operand); 31585 } else { 31586 return result; 31587 } 31588 } 31589 31590 fn analyzePtrIsNonErr( 31591 sema: *Sema, 31592 block: *Block, 31593 src: LazySrcLoc, 31594 operand: Air.Inst.Ref, 31595 ) CompileError!Air.Inst.Ref { 31596 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand); 31597 if (result == .none) { 31598 try sema.requireRuntimeBlock(block, src, null); 31599 return block.addUnOp(.is_non_err_ptr, operand); 31600 } else { 31601 return result; 31602 } 31603 } 31604 31605 fn analyzeSlice( 31606 sema: *Sema, 31607 block: *Block, 31608 src: LazySrcLoc, 31609 ptr_ptr: Air.Inst.Ref, 31610 uncasted_start: Air.Inst.Ref, 31611 uncasted_end_opt: Air.Inst.Ref, 31612 sentinel_opt: Air.Inst.Ref, 31613 sentinel_src: LazySrcLoc, 31614 ptr_src: LazySrcLoc, 31615 start_src: LazySrcLoc, 31616 end_src: LazySrcLoc, 31617 by_length: bool, 31618 ) CompileError!Air.Inst.Ref { 31619 const pt = sema.pt; 31620 const zcu = pt.zcu; 31621 // Slice expressions can operate on a variable whose type is an array. This requires 31622 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. 31623 const ptr_ptr_ty = sema.typeOf(ptr_ptr); 31624 const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(zcu)) { 31625 .pointer => ptr_ptr_ty.childType(zcu), 31626 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ptr_ty.fmt(pt)}), 31627 }; 31628 31629 var array_ty = ptr_ptr_child_ty; 31630 var slice_ty = ptr_ptr_ty; 31631 var ptr_or_slice = ptr_ptr; 31632 var elem_ty: Type = undefined; 31633 var ptr_sentinel: ?Value = null; 31634 switch (ptr_ptr_child_ty.zigTypeTag(zcu)) { 31635 .array => { 31636 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); 31637 elem_ty = ptr_ptr_child_ty.childType(zcu); 31638 }, 31639 .pointer => switch (ptr_ptr_child_ty.ptrSize(zcu)) { 31640 .one => { 31641 const double_child_ty = ptr_ptr_child_ty.childType(zcu); 31642 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 31643 if (double_child_ty.zigTypeTag(zcu) == .array) { 31644 ptr_sentinel = double_child_ty.sentinel(zcu); 31645 slice_ty = ptr_ptr_child_ty; 31646 array_ty = double_child_ty; 31647 elem_ty = double_child_ty.childType(zcu); 31648 } else { 31649 if (uncasted_end_opt == .none) { 31650 return sema.fail(block, src, "slice of single-item pointer must be bounded", .{}); 31651 } 31652 const start_value = try sema.resolveConstDefinedValue( 31653 block, 31654 start_src, 31655 uncasted_start, 31656 .{ .simple = .slice_single_item_ptr_bounds }, 31657 ); 31658 31659 const end_value = try sema.resolveConstDefinedValue( 31660 block, 31661 end_src, 31662 uncasted_end_opt, 31663 .{ .simple = .slice_single_item_ptr_bounds }, 31664 ); 31665 31666 const bounds_error_message = "slice of single-item pointer must have bounds [0..0], [0..1], or [1..1]"; 31667 if (try sema.compareScalar(start_value, .neq, end_value, .comptime_int)) { 31668 if (try sema.compareScalar(start_value, .neq, Value.zero_comptime_int, .comptime_int)) { 31669 const msg = msg: { 31670 const msg = try sema.errMsg(start_src, bounds_error_message, .{}); 31671 errdefer msg.destroy(sema.gpa); 31672 try sema.errNote( 31673 start_src, 31674 msg, 31675 "expected '{f}', found '{f}'", 31676 .{ 31677 Value.zero_comptime_int.fmtValueSema(pt, sema), 31678 start_value.fmtValueSema(pt, sema), 31679 }, 31680 ); 31681 break :msg msg; 31682 }; 31683 return sema.failWithOwnedErrorMsg(block, msg); 31684 } else if (try sema.compareScalar(end_value, .neq, Value.one_comptime_int, .comptime_int)) { 31685 const msg = msg: { 31686 const msg = try sema.errMsg(end_src, bounds_error_message, .{}); 31687 errdefer msg.destroy(sema.gpa); 31688 try sema.errNote( 31689 end_src, 31690 msg, 31691 "expected '{f}', found '{f}'", 31692 .{ 31693 Value.one_comptime_int.fmtValueSema(pt, sema), 31694 end_value.fmtValueSema(pt, sema), 31695 }, 31696 ); 31697 break :msg msg; 31698 }; 31699 return sema.failWithOwnedErrorMsg(block, msg); 31700 } 31701 } else { 31702 if (try sema.compareScalar(end_value, .gt, Value.one_comptime_int, .comptime_int)) { 31703 return sema.fail( 31704 block, 31705 end_src, 31706 "end index {f} out of bounds for slice of single-item pointer", 31707 .{end_value.fmtValueSema(pt, sema)}, 31708 ); 31709 } 31710 } 31711 31712 array_ty = try pt.arrayType(.{ 31713 .len = 1, 31714 .child = double_child_ty.toIntern(), 31715 }); 31716 const ptr_info = ptr_ptr_child_ty.ptrInfo(zcu); 31717 slice_ty = try pt.ptrType(.{ 31718 .child = array_ty.toIntern(), 31719 .flags = .{ 31720 .alignment = ptr_info.flags.alignment, 31721 .is_const = ptr_info.flags.is_const, 31722 .is_allowzero = ptr_info.flags.is_allowzero, 31723 .is_volatile = ptr_info.flags.is_volatile, 31724 .address_space = ptr_info.flags.address_space, 31725 }, 31726 }); 31727 elem_ty = double_child_ty; 31728 } 31729 }, 31730 .many, .c => { 31731 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); 31732 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 31733 slice_ty = ptr_ptr_child_ty; 31734 array_ty = ptr_ptr_child_ty; 31735 elem_ty = ptr_ptr_child_ty.childType(zcu); 31736 31737 if (ptr_ptr_child_ty.ptrSize(zcu) == .c) { 31738 if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| { 31739 if (ptr_val.isNull(zcu)) { 31740 return sema.fail(block, src, "slice of null pointer", .{}); 31741 } 31742 } 31743 } 31744 }, 31745 .slice => { 31746 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); 31747 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 31748 slice_ty = ptr_ptr_child_ty; 31749 array_ty = ptr_ptr_child_ty; 31750 elem_ty = ptr_ptr_child_ty.childType(zcu); 31751 }, 31752 }, 31753 else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}), 31754 } 31755 31756 const ptr = if (slice_ty.isSlice(zcu)) 31757 try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) 31758 else if (array_ty.zigTypeTag(zcu) == .array) ptr: { 31759 var manyptr_ty_key = zcu.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type; 31760 assert(manyptr_ty_key.child == array_ty.toIntern()); 31761 assert(manyptr_ty_key.flags.size == .one); 31762 manyptr_ty_key.child = elem_ty.toIntern(); 31763 manyptr_ty_key.flags.size = .many; 31764 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(manyptr_ty_key), ptr_or_slice, ptr_src); 31765 } else ptr_or_slice; 31766 31767 const start = try sema.coerce(block, .usize, uncasted_start, start_src); 31768 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); 31769 const new_ptr_ty = sema.typeOf(new_ptr); 31770 31771 // true if and only if the end index of the slice, implicitly or explicitly, equals 31772 // the length of the underlying object being sliced. we might learn the length of the 31773 // underlying object because it is an array (which has the length in the type), or 31774 // we might learn of the length because it is a comptime-known slice value. 31775 var end_is_len = uncasted_end_opt == .none; 31776 const end = e: { 31777 if (array_ty.zigTypeTag(zcu) == .array) { 31778 const len_val = try pt.intValue(.usize, array_ty.arrayLen(zcu)); 31779 31780 if (!end_is_len) { 31781 const end = if (by_length) end: { 31782 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31783 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 31784 break :end try sema.coerce(block, .usize, uncasted_end, end_src); 31785 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31786 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 31787 const len_s_val = try pt.intValue( 31788 .usize, 31789 array_ty.arrayLenIncludingSentinel(zcu), 31790 ); 31791 if (!(try sema.compareAll(end_val, .lte, len_s_val, .usize))) { 31792 const sentinel_label: []const u8 = if (array_ty.sentinel(zcu) != null) 31793 " +1 (sentinel)" 31794 else 31795 ""; 31796 31797 return sema.fail( 31798 block, 31799 end_src, 31800 "end index {f} out of bounds for array of length {f}{s}", 31801 .{ 31802 end_val.fmtValueSema(pt, sema), 31803 len_val.fmtValueSema(pt, sema), 31804 sentinel_label, 31805 }, 31806 ); 31807 } 31808 31809 // end_is_len is only true if we are NOT using the sentinel 31810 // length. For sentinel-length, we don't want the type to 31811 // contain the sentinel. 31812 if (end_val.eql(len_val, .usize, zcu)) { 31813 end_is_len = true; 31814 } 31815 } 31816 break :e end; 31817 } 31818 31819 break :e Air.internedToRef(len_val.toIntern()); 31820 } else if (slice_ty.isSlice(zcu)) { 31821 if (!end_is_len) { 31822 const end = if (by_length) end: { 31823 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31824 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 31825 break :end try sema.coerce(block, .usize, uncasted_end, end_src); 31826 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31827 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 31828 if (try sema.resolveValue(ptr_or_slice)) |slice_val| { 31829 if (slice_val.isUndef(zcu)) { 31830 return sema.fail(block, src, "slice of undefined", .{}); 31831 } 31832 const has_sentinel = slice_ty.sentinel(zcu) != null; 31833 const slice_len = try slice_val.sliceLen(pt); 31834 const len_plus_sent = slice_len + @intFromBool(has_sentinel); 31835 const slice_len_val_with_sentinel = try pt.intValue(.usize, len_plus_sent); 31836 if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, .usize))) { 31837 const sentinel_label: []const u8 = if (has_sentinel) 31838 " +1 (sentinel)" 31839 else 31840 ""; 31841 31842 return sema.fail( 31843 block, 31844 end_src, 31845 "end index {f} out of bounds for slice of length {d}{s}", 31846 .{ 31847 end_val.fmtValueSema(pt, sema), 31848 try slice_val.sliceLen(pt), 31849 sentinel_label, 31850 }, 31851 ); 31852 } 31853 31854 // If the slice has a sentinel, we consider end_is_len 31855 // is only true if it equals the length WITHOUT the 31856 // sentinel, so we don't add a sentinel type. 31857 const slice_len_val = try pt.intValue(.usize, slice_len); 31858 if (end_val.eql(slice_len_val, .usize, zcu)) { 31859 end_is_len = true; 31860 } 31861 } 31862 } 31863 break :e end; 31864 } 31865 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); 31866 } 31867 if (!end_is_len) { 31868 if (by_length) { 31869 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31870 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 31871 break :e try sema.coerce(block, .usize, uncasted_end, end_src); 31872 } else break :e try sema.coerce(block, .usize, uncasted_end_opt, end_src); 31873 } 31874 31875 // when slicing a many-item pointer, if a sentinel `S` is provided as in `ptr[a.. :S]`, it 31876 // must match the sentinel of `@TypeOf(ptr)`. 31877 sentinel_check: { 31878 if (sentinel_opt == .none) break :sentinel_check; 31879 const provided = provided: { 31880 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); 31881 try checkSentinelType(sema, block, sentinel_src, elem_ty); 31882 break :provided try sema.resolveConstDefinedValue( 31883 block, 31884 sentinel_src, 31885 casted, 31886 .{ .simple = .slice_sentinel }, 31887 ); 31888 }; 31889 31890 if (ptr_sentinel) |current| { 31891 if (provided.toIntern() == current.toIntern()) break :sentinel_check; 31892 } 31893 31894 return sema.failWithOwnedErrorMsg(block, msg: { 31895 const msg = try sema.errMsg(sentinel_src, "sentinel-terminated slicing of many-item pointer must match existing sentinel", .{}); 31896 errdefer msg.destroy(sema.gpa); 31897 if (ptr_sentinel) |current| { 31898 try sema.errNote(sentinel_src, msg, "expected sentinel '{f}', found '{f}'", .{ current.fmtValue(pt), provided.fmtValue(pt) }); 31899 } else { 31900 try sema.errNote(ptr_src, msg, "type '{f}' does not have a sentinel", .{slice_ty.fmt(pt)}); 31901 } 31902 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{}); 31903 break :msg msg; 31904 }); 31905 } 31906 return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); 31907 }; 31908 31909 const sentinel = s: { 31910 if (sentinel_opt != .none) { 31911 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); 31912 try checkSentinelType(sema, block, sentinel_src, elem_ty); 31913 break :s try sema.resolveConstDefinedValue(block, sentinel_src, casted, .{ .simple = .slice_sentinel }); 31914 } 31915 // If we are slicing to the end of something that is sentinel-terminated 31916 // then the resulting slice type is also sentinel-terminated. 31917 if (end_is_len) { 31918 if (ptr_sentinel) |sent| { 31919 break :s sent; 31920 } 31921 } 31922 break :s null; 31923 }; 31924 const slice_sentinel = if (sentinel_opt != .none) sentinel else null; 31925 31926 var checked_start_lte_end = by_length; 31927 var runtime_src: ?LazySrcLoc = null; 31928 31929 // requirement: start <= end 31930 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 31931 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { 31932 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, .usize))) { 31933 return sema.fail( 31934 block, 31935 start_src, 31936 "start index {f} is larger than end index {f}", 31937 .{ 31938 start_val.fmtValueSema(pt, sema), 31939 end_val.fmtValueSema(pt, sema), 31940 }, 31941 ); 31942 } 31943 checked_start_lte_end = true; 31944 if (try sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: { 31945 const expected_sentinel = sentinel orelse break :sentinel_check; 31946 const start_int = start_val.toUnsignedInt(zcu); 31947 const end_int = end_val.toUnsignedInt(zcu); 31948 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); 31949 31950 const many_ptr_ty = try pt.manyConstPtrType(elem_ty); 31951 const many_ptr_val = try pt.getCoerced(ptr_val, many_ptr_ty); 31952 const elem_ptr = try many_ptr_val.ptrElem(sentinel_index, pt); 31953 const res = try sema.pointerDerefExtra(block, src, elem_ptr); 31954 const actual_sentinel = switch (res) { 31955 .runtime_load => break :sentinel_check, 31956 .val => |v| v, 31957 .needed_well_defined => |ty| return sema.fail( 31958 block, 31959 src, 31960 "comptime dereference requires '{f}' to have a well-defined layout", 31961 .{ty.fmt(pt)}, 31962 ), 31963 .out_of_bounds => |ty| return sema.fail( 31964 block, 31965 end_src, 31966 "slice end index {d} exceeds bounds of containing decl of type '{f}'", 31967 .{ end_int, ty.fmt(pt) }, 31968 ), 31969 }; 31970 31971 if (!actual_sentinel.eql(expected_sentinel, elem_ty, zcu)) { 31972 const msg = msg: { 31973 const msg = try sema.errMsg(src, "value in memory does not match slice sentinel", .{}); 31974 errdefer msg.destroy(sema.gpa); 31975 try sema.errNote(src, msg, "expected '{f}', found '{f}'", .{ 31976 expected_sentinel.fmtValueSema(pt, sema), 31977 actual_sentinel.fmtValueSema(pt, sema), 31978 }); 31979 31980 break :msg msg; 31981 }; 31982 return sema.failWithOwnedErrorMsg(block, msg); 31983 } 31984 } else { 31985 runtime_src = ptr_src; 31986 } 31987 } else { 31988 runtime_src = start_src; 31989 } 31990 } else { 31991 runtime_src = end_src; 31992 } 31993 31994 if (!checked_start_lte_end and block.wantSafety() and !block.isComptime()) { 31995 // requirement: start <= end 31996 assert(!block.isComptime()); 31997 try sema.requireRuntimeBlock(block, src, runtime_src.?); 31998 const ok = try block.addBinOp(.cmp_lte, start, end); 31999 try sema.addSafetyCheckCall(block, src, ok, .@"panic.startGreaterThanEnd", &.{ start, end }); 32000 } 32001 const new_len = if (by_length) 32002 try sema.coerce(block, .usize, uncasted_end_opt, end_src) 32003 else 32004 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); 32005 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); 32006 32007 const new_ptr_ty_info = new_ptr_ty.ptrInfo(zcu); 32008 const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(zcu) != .c; 32009 32010 if (opt_new_len_val) |new_len_val| { 32011 const new_len_int = try new_len_val.toUnsignedIntSema(pt); 32012 32013 const return_ty = try pt.ptrTypeSema(.{ 32014 .child = (try pt.arrayType(.{ 32015 .len = new_len_int, 32016 .sentinel = if (sentinel) |s| s.toIntern() else .none, 32017 .child = elem_ty.toIntern(), 32018 })).toIntern(), 32019 .flags = .{ 32020 .alignment = new_ptr_ty_info.flags.alignment, 32021 .is_const = new_ptr_ty_info.flags.is_const, 32022 .is_allowzero = new_allowzero, 32023 .is_volatile = new_ptr_ty_info.flags.is_volatile, 32024 .address_space = new_ptr_ty_info.flags.address_space, 32025 }, 32026 }); 32027 32028 const opt_new_ptr_val = try sema.resolveValue(new_ptr); 32029 const new_ptr_val = opt_new_ptr_val orelse { 32030 const result = try block.addBitCast(return_ty, new_ptr); 32031 if (block.wantSafety()) { 32032 // requirement: slicing C ptr is non-null 32033 if (ptr_ptr_child_ty.isCPtr(zcu)) { 32034 const is_non_null = try sema.analyzeIsNull(block, ptr, true); 32035 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 32036 } 32037 32038 bounds_check: { 32039 const actual_len = if (array_ty.zigTypeTag(zcu) == .array) 32040 try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu)) 32041 else if (slice_ty.isSlice(zcu)) l: { 32042 const slice_len = try sema.analyzeSliceLen(block, src, ptr_or_slice); 32043 break :l if (slice_ty.sentinel(zcu) == null) 32044 slice_len 32045 else 32046 try sema.analyzeArithmetic(block, .add, slice_len, .one, src, end_src, end_src, true); 32047 } else break :bounds_check; 32048 32049 const actual_end = if (slice_sentinel != null) 32050 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 32051 else 32052 end; 32053 32054 try sema.addSafetyCheckIndexOob(block, src, actual_end, actual_len, .cmp_lte); 32055 } 32056 32057 // requirement: result[new_len] == slice_sentinel 32058 try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); 32059 } 32060 return result; 32061 }; 32062 32063 if (!new_ptr_val.isUndef(zcu)) { 32064 return Air.internedToRef((try pt.getCoerced(new_ptr_val, return_ty)).toIntern()); 32065 } 32066 32067 // Special case: @as([]i32, undefined)[x..x] 32068 if (new_len_int == 0) { 32069 return pt.undefRef(return_ty); 32070 } 32071 32072 return sema.fail(block, src, "non-zero length slice of undefined pointer", .{}); 32073 } 32074 32075 const return_ty = try pt.ptrTypeSema(.{ 32076 .child = elem_ty.toIntern(), 32077 .sentinel = if (sentinel) |s| s.toIntern() else .none, 32078 .flags = .{ 32079 .size = .slice, 32080 .alignment = new_ptr_ty_info.flags.alignment, 32081 .is_const = new_ptr_ty_info.flags.is_const, 32082 .is_volatile = new_ptr_ty_info.flags.is_volatile, 32083 .is_allowzero = new_allowzero, 32084 .address_space = new_ptr_ty_info.flags.address_space, 32085 }, 32086 }); 32087 32088 try sema.requireRuntimeBlock(block, src, runtime_src.?); 32089 if (block.wantSafety()) { 32090 // requirement: slicing C ptr is non-null 32091 if (ptr_ptr_child_ty.isCPtr(zcu)) { 32092 const is_non_null = try sema.analyzeIsNull(block, ptr, true); 32093 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 32094 } 32095 32096 // requirement: end <= len 32097 const opt_len_inst = if (array_ty.zigTypeTag(zcu) == .array) 32098 try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu)) 32099 else if (slice_ty.isSlice(zcu)) blk: { 32100 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { 32101 // we don't need to add one for sentinels because the 32102 // underlying value data includes the sentinel 32103 break :blk try pt.intRef(.usize, try slice_val.sliceLen(pt)); 32104 } 32105 32106 const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice); 32107 if (slice_ty.sentinel(zcu) == null) break :blk slice_len_inst; 32108 32109 // we have to add one because slice lengths don't include the sentinel 32110 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); 32111 } else null; 32112 if (opt_len_inst) |len_inst| { 32113 const actual_end = if (slice_sentinel != null) 32114 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 32115 else 32116 end; 32117 try sema.addSafetyCheckIndexOob(block, src, actual_end, len_inst, .cmp_lte); 32118 } 32119 32120 // requirement: start <= end 32121 try sema.addSafetyCheckIndexOob(block, src, start, end, .cmp_lte); 32122 } 32123 const result = try block.addInst(.{ 32124 .tag = .slice, 32125 .data = .{ .ty_pl = .{ 32126 .ty = Air.internedToRef(return_ty.toIntern()), 32127 .payload = try sema.addExtra(Air.Bin{ 32128 .lhs = new_ptr, 32129 .rhs = new_len, 32130 }), 32131 } }, 32132 }); 32133 if (block.wantSafety()) { 32134 // requirement: result[new_len] == slice_sentinel 32135 try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); 32136 } 32137 return result; 32138 } 32139 32140 /// Asserts that lhs and rhs types are both numeric. 32141 fn cmpNumeric( 32142 sema: *Sema, 32143 block: *Block, 32144 src: LazySrcLoc, 32145 uncasted_lhs: Air.Inst.Ref, 32146 uncasted_rhs: Air.Inst.Ref, 32147 op: std.math.CompareOperator, 32148 lhs_src: LazySrcLoc, 32149 rhs_src: LazySrcLoc, 32150 ) CompileError!Air.Inst.Ref { 32151 const pt = sema.pt; 32152 const zcu = pt.zcu; 32153 const lhs_ty = sema.typeOf(uncasted_lhs); 32154 const rhs_ty = sema.typeOf(uncasted_rhs); 32155 32156 assert(lhs_ty.isNumeric(zcu)); 32157 assert(rhs_ty.isNumeric(zcu)); 32158 32159 const lhs_ty_tag = lhs_ty.zigTypeTag(zcu); 32160 const rhs_ty_tag = rhs_ty.zigTypeTag(zcu); 32161 const target = zcu.getTarget(); 32162 32163 // One exception to heterogeneous comparison: comptime_float needs to 32164 // coerce to fixed-width float. 32165 32166 const lhs = if (lhs_ty_tag == .comptime_float and rhs_ty_tag == .float) 32167 try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src) 32168 else 32169 uncasted_lhs; 32170 32171 const rhs = if (lhs_ty_tag == .float and rhs_ty_tag == .comptime_float) 32172 try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src) 32173 else 32174 uncasted_rhs; 32175 32176 const maybe_lhs_val = try sema.resolveValue(lhs); 32177 const maybe_rhs_val = try sema.resolveValue(rhs); 32178 32179 // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value. 32180 if (maybe_lhs_val) |lhs_val| { 32181 // Result based on comparison exceeding type bounds 32182 if (!lhs_val.isUndef(zcu) and (lhs_ty_tag == .int or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) { 32183 if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| { 32184 return if (res) .bool_true else .bool_false; 32185 } 32186 } 32187 // Result based on NaN comparison 32188 if (lhs_val.isNan(zcu)) { 32189 return if (op == .neq) .bool_true else .bool_false; 32190 } 32191 // Result based on inf comparison to int 32192 if (lhs_val.isInf(zcu) and rhs_ty_tag == .int) return switch (op) { 32193 .neq => .bool_true, 32194 .eq => .bool_false, 32195 .gt, .gte => if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true, 32196 .lt, .lte => if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false, 32197 }; 32198 } 32199 32200 // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS value. 32201 if (maybe_rhs_val) |rhs_val| { 32202 // Result based on comparison exceeding type bounds 32203 if (!rhs_val.isUndef(zcu) and (rhs_ty_tag == .int or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) { 32204 if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| { 32205 return if (res) .bool_true else .bool_false; 32206 } 32207 } 32208 // Result based on NaN comparison 32209 if (rhs_val.isNan(zcu)) { 32210 return if (op == .neq) .bool_true else .bool_false; 32211 } 32212 // Result based on inf comparison to int 32213 if (rhs_val.isInf(zcu) and lhs_ty_tag == .int) return switch (op) { 32214 .neq => .bool_true, 32215 .eq => .bool_false, 32216 .gt, .gte => if (rhs_val.isNegativeInf(zcu)) .bool_true else .bool_false, 32217 .lt, .lte => if (rhs_val.isNegativeInf(zcu)) .bool_false else .bool_true, 32218 }; 32219 } 32220 32221 // Any other comparison depends on both values, so the result is undef if either is undef. 32222 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; 32223 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; 32224 32225 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: { 32226 if (maybe_rhs_val) |rhs_val| { 32227 const res = try Value.compareHeteroSema(lhs_val, op, rhs_val, pt); 32228 return if (res) .bool_true else .bool_false; 32229 } else break :rs rhs_src; 32230 } else lhs_src; 32231 32232 // TODO handle comparisons against lazy zero values 32233 // Some values can be compared against zero without being runtime-known or without forcing 32234 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to 32235 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout 32236 // of this function if we don't need to. 32237 try sema.requireRuntimeBlock(block, src, runtime_src); 32238 32239 // For floats, emit a float comparison instruction. 32240 const lhs_is_float = switch (lhs_ty_tag) { 32241 .float, .comptime_float => true, 32242 else => false, 32243 }; 32244 const rhs_is_float = switch (rhs_ty_tag) { 32245 .float, .comptime_float => true, 32246 else => false, 32247 }; 32248 32249 if (lhs_is_float and rhs_is_float) { 32250 // Smaller fixed-width floats coerce to larger fixed-width floats. 32251 // comptime_float coerces to fixed-width float. 32252 const dest_ty = x: { 32253 if (lhs_ty_tag == .comptime_float) { 32254 break :x rhs_ty; 32255 } else if (rhs_ty_tag == .comptime_float) { 32256 break :x lhs_ty; 32257 } 32258 if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) { 32259 break :x lhs_ty; 32260 } else { 32261 break :x rhs_ty; 32262 } 32263 }; 32264 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 32265 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 32266 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs); 32267 } 32268 32269 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. 32270 // For mixed signed and unsigned integers, implicit cast both operands to a signed 32271 // integer with + 1 bit. 32272 // For mixed floats and integers, extract the integer part from the float, cast that to 32273 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, 32274 // add/subtract 1. 32275 const lhs_is_signed = if (maybe_lhs_val) |lhs_val| 32276 !(try lhs_val.compareAllWithZeroSema(.gte, pt)) 32277 else 32278 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu)); 32279 const rhs_is_signed = if (maybe_rhs_val) |rhs_val| 32280 !(try rhs_val.compareAllWithZeroSema(.gte, pt)) 32281 else 32282 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu)); 32283 const dest_int_is_signed = lhs_is_signed or rhs_is_signed; 32284 32285 var dest_float_type: ?Type = null; 32286 32287 var lhs_bits: usize = undefined; 32288 if (maybe_lhs_val) |unresolved_lhs_val| { 32289 const lhs_val = try sema.resolveLazyValue(unresolved_lhs_val); 32290 if (!rhs_is_signed) { 32291 switch (lhs_val.orderAgainstZero(zcu)) { 32292 .gt => {}, 32293 .eq => switch (op) { // LHS = 0, RHS is unsigned 32294 .lte => return .bool_true, 32295 .gt => return .bool_false, 32296 else => {}, 32297 }, 32298 .lt => switch (op) { // LHS < 0, RHS is unsigned 32299 .neq, .lt, .lte => return .bool_true, 32300 .eq, .gt, .gte => return .bool_false, 32301 }, 32302 } 32303 } 32304 if (lhs_is_float) { 32305 const float = lhs_val.toFloat(f128, zcu); 32306 var big_int: std.math.big.int.Mutable = .{ 32307 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), 32308 .len = undefined, 32309 .positive = undefined, 32310 }; 32311 switch (big_int.setFloat(float, .away)) { 32312 .inexact => switch (op) { 32313 .eq => return .bool_false, 32314 .neq => return .bool_true, 32315 else => {}, 32316 }, 32317 .exact => {}, 32318 } 32319 lhs_bits = big_int.toConst().bitCountTwosComp(); 32320 } else { 32321 lhs_bits = lhs_val.intBitCountTwosComp(zcu); 32322 } 32323 lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed); 32324 } else if (lhs_is_float) { 32325 dest_float_type = lhs_ty; 32326 } else { 32327 const int_info = lhs_ty.intInfo(zcu); 32328 lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 32329 } 32330 32331 var rhs_bits: usize = undefined; 32332 if (maybe_rhs_val) |unresolved_rhs_val| { 32333 const rhs_val = try sema.resolveLazyValue(unresolved_rhs_val); 32334 if (!lhs_is_signed) { 32335 switch (rhs_val.orderAgainstZero(zcu)) { 32336 .gt => {}, 32337 .eq => switch (op) { // RHS = 0, LHS is unsigned 32338 .gte => return .bool_true, 32339 .lt => return .bool_false, 32340 else => {}, 32341 }, 32342 .lt => switch (op) { // RHS < 0, LHS is unsigned 32343 .neq, .gt, .gte => return .bool_true, 32344 .eq, .lt, .lte => return .bool_false, 32345 }, 32346 } 32347 } 32348 if (rhs_is_float) { 32349 const float = rhs_val.toFloat(f128, zcu); 32350 var big_int: std.math.big.int.Mutable = .{ 32351 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), 32352 .len = undefined, 32353 .positive = undefined, 32354 }; 32355 switch (big_int.setFloat(float, .away)) { 32356 .inexact => switch (op) { 32357 .eq => return .bool_false, 32358 .neq => return .bool_true, 32359 else => {}, 32360 }, 32361 .exact => {}, 32362 } 32363 rhs_bits = big_int.toConst().bitCountTwosComp(); 32364 } else { 32365 rhs_bits = rhs_val.intBitCountTwosComp(zcu); 32366 } 32367 rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed); 32368 } else if (rhs_is_float) { 32369 dest_float_type = rhs_ty; 32370 } else { 32371 const int_info = rhs_ty.intInfo(zcu); 32372 rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 32373 } 32374 32375 const dest_ty = if (dest_float_type) |ft| ft else blk: { 32376 const max_bits = @max(lhs_bits, rhs_bits); 32377 const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}); 32378 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned; 32379 break :blk try pt.intType(signedness, casted_bits); 32380 }; 32381 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 32382 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 32383 32384 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs); 32385 } 32386 32387 /// Asserts that LHS value is an int or comptime int and not undefined, and 32388 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to 32389 /// determine whether `op` has a guaranteed result. 32390 /// If it cannot be determined, returns null. 32391 /// Otherwise returns a bool for the guaranteed comparison operation. 32392 fn compareIntsOnlyPossibleResult( 32393 sema: *Sema, 32394 lhs_val: Value, 32395 op: std.math.CompareOperator, 32396 rhs_ty: Type, 32397 ) SemaError!?bool { 32398 const pt = sema.pt; 32399 const zcu = pt.zcu; 32400 32401 const min_rhs = try rhs_ty.minInt(pt, rhs_ty); 32402 const max_rhs = try rhs_ty.maxInt(pt, rhs_ty); 32403 32404 if (min_rhs.toIntern() == max_rhs.toIntern()) { 32405 // RHS is effectively comptime-known. 32406 return try Value.compareHeteroSema(lhs_val, op, min_rhs, pt); 32407 } 32408 32409 const against_min = try lhs_val.orderAdvanced(min_rhs, .sema, zcu, pt.tid); 32410 const against_max = try lhs_val.orderAdvanced(max_rhs, .sema, zcu, pt.tid); 32411 32412 switch (op) { 32413 .eq => { 32414 if (against_min.compare(.lt)) return false; 32415 if (against_max.compare(.gt)) return false; 32416 }, 32417 .neq => { 32418 if (against_min.compare(.lt)) return true; 32419 if (against_max.compare(.gt)) return true; 32420 }, 32421 .lt => { 32422 if (against_min.compare(.lt)) return true; 32423 if (against_max.compare(.gte)) return false; 32424 }, 32425 .gt => { 32426 if (against_max.compare(.gt)) return true; 32427 if (against_min.compare(.lte)) return false; 32428 }, 32429 .lte => { 32430 if (against_min.compare(.lte)) return true; 32431 if (against_max.compare(.gt)) return false; 32432 }, 32433 .gte => { 32434 if (against_max.compare(.gte)) return true; 32435 if (against_min.compare(.lt)) return false; 32436 }, 32437 } 32438 32439 return null; 32440 } 32441 32442 /// Asserts that lhs and rhs types are both vectors. 32443 fn cmpVector( 32444 sema: *Sema, 32445 block: *Block, 32446 src: LazySrcLoc, 32447 lhs: Air.Inst.Ref, 32448 rhs: Air.Inst.Ref, 32449 op: std.math.CompareOperator, 32450 lhs_src: LazySrcLoc, 32451 rhs_src: LazySrcLoc, 32452 ) CompileError!Air.Inst.Ref { 32453 const pt = sema.pt; 32454 const zcu = pt.zcu; 32455 const lhs_ty = sema.typeOf(lhs); 32456 const rhs_ty = sema.typeOf(rhs); 32457 assert(lhs_ty.zigTypeTag(zcu) == .vector); 32458 assert(rhs_ty.zigTypeTag(zcu) == .vector); 32459 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 32460 32461 const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } }); 32462 const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src); 32463 const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src); 32464 32465 const result_ty = try pt.vectorType(.{ 32466 .len = lhs_ty.vectorLen(zcu), 32467 .child = .bool_type, 32468 }); 32469 32470 const maybe_lhs_val = try sema.resolveValue(casted_lhs); 32471 const maybe_rhs_val = try sema.resolveValue(casted_rhs); 32472 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty); 32473 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty); 32474 32475 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: { 32476 if (maybe_rhs_val) |rhs_val| { 32477 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty); 32478 return Air.internedToRef(cmp_val.toIntern()); 32479 } else break :src rhs_src; 32480 } else lhs_src; 32481 32482 try sema.requireRuntimeBlock(block, src, runtime_src); 32483 return block.addCmpVector(casted_lhs, casted_rhs, op); 32484 } 32485 32486 fn wrapOptional( 32487 sema: *Sema, 32488 block: *Block, 32489 dest_ty: Type, 32490 inst: Air.Inst.Ref, 32491 inst_src: LazySrcLoc, 32492 ) !Air.Inst.Ref { 32493 if (try sema.resolveValue(inst)) |val| { 32494 return Air.internedToRef((try sema.pt.intern(.{ .opt = .{ 32495 .ty = dest_ty.toIntern(), 32496 .val = val.toIntern(), 32497 } }))); 32498 } 32499 32500 try sema.requireRuntimeBlock(block, inst_src, null); 32501 return block.addTyOp(.wrap_optional, dest_ty, inst); 32502 } 32503 32504 fn wrapErrorUnionPayload( 32505 sema: *Sema, 32506 block: *Block, 32507 dest_ty: Type, 32508 inst: Air.Inst.Ref, 32509 inst_src: LazySrcLoc, 32510 ) !Air.Inst.Ref { 32511 const pt = sema.pt; 32512 const zcu = pt.zcu; 32513 const dest_payload_ty = dest_ty.errorUnionPayload(zcu); 32514 const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); 32515 if (try sema.resolveValue(coerced)) |val| { 32516 return Air.internedToRef((try pt.intern(.{ .error_union = .{ 32517 .ty = dest_ty.toIntern(), 32518 .val = .{ .payload = val.toIntern() }, 32519 } }))); 32520 } 32521 try sema.requireRuntimeBlock(block, inst_src, null); 32522 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); 32523 } 32524 32525 fn wrapErrorUnionSet( 32526 sema: *Sema, 32527 block: *Block, 32528 dest_ty: Type, 32529 inst: Air.Inst.Ref, 32530 inst_src: LazySrcLoc, 32531 ) !Air.Inst.Ref { 32532 const pt = sema.pt; 32533 const zcu = pt.zcu; 32534 const ip = &zcu.intern_pool; 32535 const inst_ty = sema.typeOf(inst); 32536 const dest_err_set_ty = dest_ty.errorUnionSet(zcu); 32537 if (try sema.resolveValue(inst)) |val| { 32538 const expected_name = zcu.intern_pool.indexToKey(val.toIntern()).err.name; 32539 switch (dest_err_set_ty.toIntern()) { 32540 .anyerror_type => {}, 32541 .adhoc_inferred_error_set_type => ok: { 32542 const ies = sema.fn_ret_ty_ies.?; 32543 switch (ies.resolved) { 32544 .anyerror_type => break :ok, 32545 .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { 32546 break :ok; 32547 }, 32548 else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) { 32549 break :ok; 32550 }, 32551 } 32552 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 32553 }, 32554 else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) { 32555 .error_set_type => |error_set_type| ok: { 32556 if (error_set_type.nameIndex(ip, expected_name) != null) break :ok; 32557 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 32558 }, 32559 .inferred_error_set_type => |func_index| ok: { 32560 // We carefully do this in an order that avoids unnecessarily 32561 // resolving the destination error set type. 32562 try zcu.maybeUnresolveIes(func_index); 32563 switch (ip.funcIesResolvedUnordered(func_index)) { 32564 .anyerror_type => break :ok, 32565 .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { 32566 break :ok; 32567 }, 32568 else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) { 32569 break :ok; 32570 }, 32571 } 32572 32573 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 32574 }, 32575 else => unreachable, 32576 }, 32577 } 32578 return Air.internedToRef((try pt.intern(.{ .error_union = .{ 32579 .ty = dest_ty.toIntern(), 32580 .val = .{ .err_name = expected_name }, 32581 } }))); 32582 } 32583 32584 try sema.requireRuntimeBlock(block, inst_src, null); 32585 const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src); 32586 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); 32587 } 32588 32589 fn unionToTag( 32590 sema: *Sema, 32591 block: *Block, 32592 enum_ty: Type, 32593 un: Air.Inst.Ref, 32594 un_src: LazySrcLoc, 32595 ) !Air.Inst.Ref { 32596 const pt = sema.pt; 32597 const zcu = pt.zcu; 32598 if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| { 32599 return Air.internedToRef(opv.toIntern()); 32600 } 32601 if (try sema.resolveValue(un)) |un_val| { 32602 const tag_val = un_val.unionTag(zcu).?; 32603 if (tag_val.isUndef(zcu)) 32604 return try pt.undefRef(enum_ty); 32605 return Air.internedToRef(tag_val.toIntern()); 32606 } 32607 try sema.requireRuntimeBlock(block, un_src, null); 32608 return block.addTyOp(.get_union_tag, enum_ty, un); 32609 } 32610 32611 const PeerResolveStrategy = enum { 32612 /// The type is not known. 32613 /// If refined no further, this is equivalent to `exact`. 32614 unknown, 32615 /// The type may be an error set or error union. 32616 /// If refined no further, it is an error set. 32617 error_set, 32618 /// The type must be some error union. 32619 error_union, 32620 /// The type may be @TypeOf(null), an optional or a C pointer. 32621 /// If refined no further, it is @TypeOf(null). 32622 nullable, 32623 /// The type must be some optional or a C pointer. 32624 /// If refined no further, it is an optional. 32625 optional, 32626 /// The type must be either an array or a vector. 32627 /// If refined no further, it is an array. 32628 array, 32629 /// The type must be a vector. 32630 vector, 32631 /// The type must be a C pointer. 32632 c_ptr, 32633 /// The type must be a pointer (C or not). 32634 /// If refined no further, it is a non-C pointer. 32635 ptr, 32636 /// The type must be a function or a pointer to a function. 32637 /// If refined no further, it is a function. 32638 func, 32639 /// The type must be an enum literal, or some specific enum or union. Which one is decided 32640 /// afterwards based on the types in question. 32641 enum_or_union, 32642 /// The type must be some integer or float type. 32643 /// If refined no further, it is `comptime_int`. 32644 comptime_int, 32645 /// The type must be some float type. 32646 /// If refined no further, it is `comptime_float`. 32647 comptime_float, 32648 /// The type must be some float or fixed-width integer type. 32649 /// If refined no further, it is some fixed-width integer type. 32650 fixed_int, 32651 /// The type must be some fixed-width float type. 32652 fixed_float, 32653 /// The type must be a tuple. 32654 tuple, 32655 /// The peers must all be of the same type. 32656 exact, 32657 32658 /// Given two strategies, find a strategy that satisfies both, if one exists. If no such 32659 /// strategy exists, any strategy may be returned; an error will be emitted when the caller 32660 /// attempts to use the strategy to resolve the type. 32661 /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at 32662 /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy. 32663 fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy { 32664 // Our merging should be order-independent. Thus, even though the union order is arbitrary, 32665 // by sorting the tags and switching first on the smaller, we have half as many cases to 32666 // worry about (since we avoid the duplicates). 32667 const s0_is_a = @intFromEnum(a) <= @intFromEnum(b); 32668 const s0 = if (s0_is_a) a else b; 32669 const s1 = if (s0_is_a) b else a; 32670 32671 const ReasonMethod = enum { 32672 all_s0, 32673 all_s1, 32674 either, 32675 }; 32676 32677 const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) { 32678 .unknown => .{ .all_s1, s1 }, 32679 .error_set => switch (s1) { 32680 .error_set => .{ .either, .error_set }, 32681 else => .{ .all_s0, .error_union }, 32682 }, 32683 .error_union => switch (s1) { 32684 .error_union => .{ .either, .error_union }, 32685 else => .{ .all_s0, .error_union }, 32686 }, 32687 .nullable => switch (s1) { 32688 .nullable => .{ .either, .nullable }, 32689 .c_ptr => .{ .all_s1, .c_ptr }, 32690 else => .{ .all_s0, .optional }, 32691 }, 32692 .optional => switch (s1) { 32693 .optional => .{ .either, .optional }, 32694 .c_ptr => .{ .all_s1, .c_ptr }, 32695 else => .{ .all_s0, .optional }, 32696 }, 32697 .array => switch (s1) { 32698 .array => .{ .either, .array }, 32699 .vector => .{ .all_s1, .vector }, 32700 else => .{ .all_s0, .array }, 32701 }, 32702 .vector => switch (s1) { 32703 .vector => .{ .either, .vector }, 32704 else => .{ .all_s0, .vector }, 32705 }, 32706 .c_ptr => switch (s1) { 32707 .c_ptr => .{ .either, .c_ptr }, 32708 else => .{ .all_s0, .c_ptr }, 32709 }, 32710 .ptr => switch (s1) { 32711 .ptr => .{ .either, .ptr }, 32712 else => .{ .all_s0, .ptr }, 32713 }, 32714 .func => switch (s1) { 32715 .func => .{ .either, .func }, 32716 else => .{ .all_s1, s1 }, // doesn't override anything later 32717 }, 32718 .enum_or_union => switch (s1) { 32719 .enum_or_union => .{ .either, .enum_or_union }, 32720 else => .{ .all_s0, .enum_or_union }, 32721 }, 32722 .comptime_int => switch (s1) { 32723 .comptime_int => .{ .either, .comptime_int }, 32724 else => .{ .all_s1, s1 }, // doesn't override anything later 32725 }, 32726 .comptime_float => switch (s1) { 32727 .comptime_float => .{ .either, .comptime_float }, 32728 else => .{ .all_s1, s1 }, // doesn't override anything later 32729 }, 32730 .fixed_int => switch (s1) { 32731 .fixed_int => .{ .either, .fixed_int }, 32732 else => .{ .all_s1, s1 }, // doesn't override anything later 32733 }, 32734 .fixed_float => switch (s1) { 32735 .fixed_float => .{ .either, .fixed_float }, 32736 else => .{ .all_s1, s1 }, // doesn't override anything later 32737 }, 32738 .tuple => switch (s1) { 32739 .exact => .{ .all_s1, .exact }, 32740 else => .{ .all_s0, .tuple }, 32741 }, 32742 .exact => .{ .all_s0, .exact }, 32743 }; 32744 32745 switch (reason_method) { 32746 .all_s0 => { 32747 if (!s0_is_a) { 32748 reason_peer.* = b_peer_idx; 32749 } 32750 }, 32751 .all_s1 => { 32752 if (s0_is_a) { 32753 reason_peer.* = b_peer_idx; 32754 } 32755 }, 32756 .either => { 32757 // Prefer the earliest peer 32758 reason_peer.* = @min(reason_peer.*, b_peer_idx); 32759 }, 32760 } 32761 32762 return strat; 32763 } 32764 32765 fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy { 32766 return switch (ty.zigTypeTag(zcu)) { 32767 .type, .void, .bool, .@"opaque", .frame, .@"anyframe" => .exact, 32768 .noreturn, .undefined => .unknown, 32769 .null => .nullable, 32770 .comptime_int => .comptime_int, 32771 .int => .fixed_int, 32772 .comptime_float => .comptime_float, 32773 .float => .fixed_float, 32774 .pointer => if (ty.ptrInfo(zcu).flags.size == .c) .c_ptr else .ptr, 32775 .array => .array, 32776 .vector => .vector, 32777 .optional => .optional, 32778 .error_set => .error_set, 32779 .error_union => .error_union, 32780 .enum_literal, .@"enum", .@"union" => .enum_or_union, 32781 .@"struct" => if (ty.isTuple(zcu)) .tuple else .exact, 32782 .@"fn" => .func, 32783 }; 32784 } 32785 }; 32786 32787 const PeerTypeCandidateSrc = union(enum) { 32788 /// Do not print out error notes for candidate sources 32789 none: void, 32790 /// When we want to know the the src of candidate i, look up at 32791 /// index i in this slice 32792 override: []const ?LazySrcLoc, 32793 /// resolvePeerTypes originates from a @TypeOf(...) call 32794 typeof_builtin_call_node_offset: std.zig.Ast.Node.Offset, 32795 32796 pub fn resolve( 32797 self: PeerTypeCandidateSrc, 32798 block: *Block, 32799 candidate_i: usize, 32800 ) ?LazySrcLoc { 32801 return switch (self) { 32802 .none => null, 32803 .override => |candidate_srcs| if (candidate_i >= candidate_srcs.len) 32804 null 32805 else 32806 candidate_srcs[candidate_i], 32807 .typeof_builtin_call_node_offset => |node_offset| block.builtinCallArgSrc(node_offset, @intCast(candidate_i)), 32808 }; 32809 } 32810 }; 32811 32812 const PeerResolveResult = union(enum) { 32813 /// The peer type resolution was successful, and resulted in the given type. 32814 success: Type, 32815 /// There was some generic conflict between two peers. 32816 conflict: struct { 32817 peer_idx_a: usize, 32818 peer_idx_b: usize, 32819 }, 32820 /// There was an error when resolving the type of a struct or tuple field. 32821 field_error: struct { 32822 /// The name of the field which caused the failure. 32823 field_name: InternPool.NullTerminatedString, 32824 /// The type of this field in each peer. 32825 field_types: []Type, 32826 /// The error from resolving the field type. Guaranteed not to be `success`. 32827 sub_result: *PeerResolveResult, 32828 }, 32829 32830 fn report( 32831 result: PeerResolveResult, 32832 sema: *Sema, 32833 block: *Block, 32834 src: LazySrcLoc, 32835 instructions: []const Air.Inst.Ref, 32836 candidate_srcs: PeerTypeCandidateSrc, 32837 ) !*Zcu.ErrorMsg { 32838 const pt = sema.pt; 32839 32840 var opt_msg: ?*Zcu.ErrorMsg = null; 32841 errdefer if (opt_msg) |msg| msg.destroy(sema.gpa); 32842 32843 // If we mention fields we'll want to include field types, so put peer types in a buffer 32844 var peer_tys = try sema.arena.alloc(Type, instructions.len); 32845 for (peer_tys, instructions) |*ty, inst| { 32846 ty.* = sema.typeOf(inst); 32847 } 32848 32849 var cur = result; 32850 while (true) { 32851 var conflict_idx: [2]usize = undefined; 32852 32853 switch (cur) { 32854 .success => unreachable, 32855 .conflict => |conflict| { 32856 // Fall through to two-peer conflict handling below 32857 conflict_idx = .{ 32858 conflict.peer_idx_a, 32859 conflict.peer_idx_b, 32860 }; 32861 }, 32862 .field_error => |field_error| { 32863 const fmt = "struct field '{f}' has conflicting types"; 32864 const args = .{field_error.field_name.fmt(&pt.zcu.intern_pool)}; 32865 if (opt_msg) |msg| { 32866 try sema.errNote(src, msg, fmt, args); 32867 } else { 32868 opt_msg = try sema.errMsg(src, fmt, args); 32869 } 32870 32871 // Continue on to child error 32872 cur = field_error.sub_result.*; 32873 peer_tys = field_error.field_types; 32874 continue; 32875 }, 32876 } 32877 32878 // This is the path for reporting a generic conflict between two peers. 32879 32880 if (conflict_idx[1] < conflict_idx[0]) { 32881 // b comes first in source, so it's better if it comes first in the error 32882 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]); 32883 } 32884 32885 const conflict_tys: [2]Type = .{ 32886 peer_tys[conflict_idx[0]], 32887 peer_tys[conflict_idx[1]], 32888 }; 32889 const conflict_srcs: [2]?LazySrcLoc = .{ 32890 candidate_srcs.resolve(block, conflict_idx[0]), 32891 candidate_srcs.resolve(block, conflict_idx[1]), 32892 }; 32893 32894 const fmt = "incompatible types: '{f}' and '{f}'"; 32895 const args = .{ 32896 conflict_tys[0].fmt(pt), 32897 conflict_tys[1].fmt(pt), 32898 }; 32899 const msg = if (opt_msg) |msg| msg: { 32900 try sema.errNote(src, msg, fmt, args); 32901 break :msg msg; 32902 } else msg: { 32903 const msg = try sema.errMsg(src, fmt, args); 32904 opt_msg = msg; 32905 break :msg msg; 32906 }; 32907 32908 if (conflict_srcs[0]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[0].fmt(pt)}); 32909 if (conflict_srcs[1]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[1].fmt(pt)}); 32910 32911 // No child error 32912 break; 32913 } 32914 32915 return opt_msg.?; 32916 } 32917 }; 32918 32919 fn resolvePeerTypes( 32920 sema: *Sema, 32921 block: *Block, 32922 src: LazySrcLoc, 32923 instructions: []const Air.Inst.Ref, 32924 candidate_srcs: PeerTypeCandidateSrc, 32925 ) !Type { 32926 switch (instructions.len) { 32927 0 => return .noreturn, 32928 1 => return sema.typeOf(instructions[0]), 32929 else => {}, 32930 } 32931 32932 // Fast path: check if everything has the same type to bypass the main PTR logic. 32933 same_type: { 32934 const ty = sema.typeOf(instructions[0]); 32935 for (instructions[1..]) |inst| { 32936 if (sema.typeOf(inst).toIntern() != ty.toIntern()) { 32937 break :same_type; 32938 } 32939 } 32940 return ty; 32941 } 32942 32943 const peer_tys = try sema.arena.alloc(?Type, instructions.len); 32944 const peer_vals = try sema.arena.alloc(?Value, instructions.len); 32945 32946 for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { 32947 ty.* = sema.typeOf(inst); 32948 val.* = try sema.resolveValue(inst); 32949 } 32950 32951 switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { 32952 .success => |ty| return ty, 32953 else => |result| { 32954 const msg = try result.report(sema, block, src, instructions, candidate_srcs); 32955 return sema.failWithOwnedErrorMsg(block, msg); 32956 }, 32957 } 32958 } 32959 32960 fn resolvePeerTypesInner( 32961 sema: *Sema, 32962 block: *Block, 32963 src: LazySrcLoc, 32964 peer_tys: []?Type, 32965 peer_vals: []?Value, 32966 ) !PeerResolveResult { 32967 const pt = sema.pt; 32968 const zcu = pt.zcu; 32969 const ip = &zcu.intern_pool; 32970 32971 var strat_reason: usize = 0; 32972 var s: PeerResolveStrategy = .unknown; 32973 for (peer_tys, 0..) |opt_ty, i| { 32974 const ty = opt_ty orelse continue; 32975 s = s.merge(PeerResolveStrategy.select(ty, zcu), &strat_reason, i); 32976 } 32977 32978 if (s == .unknown) { 32979 // The whole thing was noreturn or undefined - try to do an exact match 32980 s = .exact; 32981 } else { 32982 // There was something other than noreturn and undefined, so we can ignore those peers 32983 for (peer_tys) |*ty_ptr| { 32984 const ty = ty_ptr.* orelse continue; 32985 switch (ty.zigTypeTag(zcu)) { 32986 .noreturn, .undefined => ty_ptr.* = null, 32987 else => {}, 32988 } 32989 } 32990 } 32991 32992 const target = zcu.getTarget(); 32993 32994 switch (s) { 32995 .unknown => unreachable, 32996 32997 .error_set => { 32998 var final_set: ?Type = null; 32999 for (peer_tys, 0..) |opt_ty, i| { 33000 const ty = opt_ty orelse continue; 33001 if (ty.zigTypeTag(zcu) != .error_set) return .{ .conflict = .{ 33002 .peer_idx_a = strat_reason, 33003 .peer_idx_b = i, 33004 } }; 33005 if (final_set) |cur_set| { 33006 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty); 33007 } else { 33008 final_set = ty; 33009 } 33010 } 33011 return .{ .success = final_set.? }; 33012 }, 33013 33014 .error_union => { 33015 var final_set: ?Type = null; 33016 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 33017 const ty = ty_ptr.* orelse continue; 33018 const set_ty = switch (ty.zigTypeTag(zcu)) { 33019 .error_set => blk: { 33020 ty_ptr.* = null; // no payload to decide on 33021 val_ptr.* = null; 33022 break :blk ty; 33023 }, 33024 .error_union => blk: { 33025 const set_ty = ty.errorUnionSet(zcu); 33026 ty_ptr.* = ty.errorUnionPayload(zcu); 33027 if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) { 33028 .error_union => |eu| switch (eu.val) { 33029 .payload => |payload_ip| val_ptr.* = Value.fromInterned(payload_ip), 33030 .err_name => val_ptr.* = null, 33031 }, 33032 .undef => val_ptr.* = Value.fromInterned(try pt.intern(.{ .undef = ty_ptr.*.?.toIntern() })), 33033 else => unreachable, 33034 }; 33035 break :blk set_ty; 33036 }, 33037 else => continue, // whole type is the payload 33038 }; 33039 if (final_set) |cur_set| { 33040 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty); 33041 } else { 33042 final_set = set_ty; 33043 } 33044 } 33045 assert(final_set != null); 33046 const final_payload = switch (try sema.resolvePeerTypesInner( 33047 block, 33048 src, 33049 peer_tys, 33050 peer_vals, 33051 )) { 33052 .success => |ty| ty, 33053 else => |result| return result, 33054 }; 33055 return .{ .success = try pt.errorUnionType(final_set.?, final_payload) }; 33056 }, 33057 33058 .nullable => { 33059 for (peer_tys, 0..) |opt_ty, i| { 33060 const ty = opt_ty orelse continue; 33061 if (!ty.eql(.null, zcu)) return .{ .conflict = .{ 33062 .peer_idx_a = strat_reason, 33063 .peer_idx_b = i, 33064 } }; 33065 } 33066 return .{ .success = .null }; 33067 }, 33068 33069 .optional => { 33070 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 33071 const ty = ty_ptr.* orelse continue; 33072 switch (ty.zigTypeTag(zcu)) { 33073 .null => { 33074 ty_ptr.* = null; 33075 val_ptr.* = null; 33076 }, 33077 .optional => { 33078 ty_ptr.* = ty.optionalChild(zcu); 33079 if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(zcu)) opt_val.optionalValue(zcu) else null; 33080 }, 33081 else => {}, 33082 } 33083 } 33084 const child_ty = switch (try sema.resolvePeerTypesInner( 33085 block, 33086 src, 33087 peer_tys, 33088 peer_vals, 33089 )) { 33090 .success => |ty| ty, 33091 else => |result| return result, 33092 }; 33093 return .{ .success = try pt.optionalType(child_ty.toIntern()) }; 33094 }, 33095 33096 .array => { 33097 // Index of the first non-null peer 33098 var opt_first_idx: ?usize = null; 33099 // Index of the first array or vector peer (i.e. not a tuple) 33100 var opt_first_arr_idx: ?usize = null; 33101 // Set to non-null once we see any peer, even a tuple 33102 var len: u64 = undefined; 33103 var sentinel: ?Value = undefined; 33104 // Only set once we see a non-tuple peer 33105 var elem_ty: Type = undefined; 33106 33107 for (peer_tys, 0..) |*ty_ptr, i| { 33108 const ty = ty_ptr.* orelse continue; 33109 33110 if (!ty.isArrayOrVector(zcu)) { 33111 // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced. 33112 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 33113 .peer_idx_a = strat_reason, 33114 .peer_idx_b = i, 33115 } }; 33116 33117 if (opt_first_idx) |first_idx| { 33118 if (arr_like.len != len) return .{ .conflict = .{ 33119 .peer_idx_a = first_idx, 33120 .peer_idx_b = i, 33121 } }; 33122 } else { 33123 opt_first_idx = i; 33124 len = arr_like.len; 33125 } 33126 33127 sentinel = null; 33128 33129 continue; 33130 } 33131 33132 const first_arr_idx = opt_first_arr_idx orelse { 33133 if (opt_first_idx == null) { 33134 opt_first_idx = i; 33135 len = ty.arrayLen(zcu); 33136 sentinel = ty.sentinel(zcu); 33137 } 33138 opt_first_arr_idx = i; 33139 elem_ty = ty.childType(zcu); 33140 continue; 33141 }; 33142 33143 if (ty.arrayLen(zcu) != len) return .{ .conflict = .{ 33144 .peer_idx_a = first_arr_idx, 33145 .peer_idx_b = i, 33146 } }; 33147 33148 const peer_elem_ty = ty.childType(zcu); 33149 if (!peer_elem_ty.eql(elem_ty, zcu)) coerce: { 33150 const peer_elem_coerces_to_elem = 33151 try sema.coerceInMemoryAllowed(block, elem_ty, peer_elem_ty, false, zcu.getTarget(), src, src, null); 33152 if (peer_elem_coerces_to_elem == .ok) { 33153 break :coerce; 33154 } 33155 33156 const elem_coerces_to_peer_elem = 33157 try sema.coerceInMemoryAllowed(block, peer_elem_ty, elem_ty, false, zcu.getTarget(), src, src, null); 33158 if (elem_coerces_to_peer_elem == .ok) { 33159 elem_ty = peer_elem_ty; 33160 break :coerce; 33161 } 33162 33163 return .{ .conflict = .{ 33164 .peer_idx_a = first_arr_idx, 33165 .peer_idx_b = i, 33166 } }; 33167 } 33168 33169 if (sentinel) |cur_sent| { 33170 if (ty.sentinel(zcu)) |peer_sent| { 33171 if (!peer_sent.eql(cur_sent, elem_ty, zcu)) sentinel = null; 33172 } else { 33173 sentinel = null; 33174 } 33175 } 33176 } 33177 33178 // There should always be at least one array or vector peer 33179 assert(opt_first_arr_idx != null); 33180 33181 return .{ .success = try pt.arrayType(.{ 33182 .len = len, 33183 .child = elem_ty.toIntern(), 33184 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none, 33185 }) }; 33186 }, 33187 33188 .vector => { 33189 var len: ?u64 = null; 33190 var first_idx: usize = undefined; 33191 for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| { 33192 const ty = ty_ptr.* orelse continue; 33193 33194 if (!ty.isArrayOrVector(zcu)) { 33195 // Allow tuples of the correct length 33196 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 33197 .peer_idx_a = strat_reason, 33198 .peer_idx_b = i, 33199 } }; 33200 33201 if (len) |expect_len| { 33202 if (arr_like.len != expect_len) return .{ .conflict = .{ 33203 .peer_idx_a = first_idx, 33204 .peer_idx_b = i, 33205 } }; 33206 } else { 33207 len = arr_like.len; 33208 first_idx = i; 33209 } 33210 33211 // Tuples won't participate in the child type resolution. We'll resolve without 33212 // them, and if the tuples have a bad type, we'll get a coercion error later. 33213 ty_ptr.* = null; 33214 val_ptr.* = null; 33215 33216 continue; 33217 } 33218 33219 if (len) |expect_len| { 33220 if (ty.arrayLen(zcu) != expect_len) return .{ .conflict = .{ 33221 .peer_idx_a = first_idx, 33222 .peer_idx_b = i, 33223 } }; 33224 } else { 33225 len = ty.arrayLen(zcu); 33226 first_idx = i; 33227 } 33228 33229 ty_ptr.* = ty.childType(zcu); 33230 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR 33231 } 33232 33233 const child_ty = switch (try sema.resolvePeerTypesInner( 33234 block, 33235 src, 33236 peer_tys, 33237 peer_vals, 33238 )) { 33239 .success => |ty| ty, 33240 else => |result| return result, 33241 }; 33242 33243 return .{ .success = try pt.vectorType(.{ 33244 .len = @intCast(len.?), 33245 .child = child_ty.toIntern(), 33246 }) }; 33247 }, 33248 33249 .c_ptr => { 33250 var opt_ptr_info: ?InternPool.Key.PtrType = null; 33251 var first_idx: usize = undefined; 33252 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 33253 const ty = opt_ty orelse continue; 33254 switch (ty.zigTypeTag(zcu)) { 33255 .comptime_int => continue, // comptime-known integers can always coerce to C pointers 33256 .int => { 33257 if (opt_val != null) { 33258 // Always allow the coercion for comptime-known ints 33259 continue; 33260 } else { 33261 // Runtime-known, so check if the type is no bigger than a usize 33262 const ptr_bits = target.ptrBitWidth(); 33263 const bits = ty.intInfo(zcu).bits; 33264 if (bits <= ptr_bits) continue; 33265 } 33266 }, 33267 .null => continue, 33268 else => {}, 33269 } 33270 33271 if (!ty.isPtrAtRuntime(zcu)) return .{ .conflict = .{ 33272 .peer_idx_a = strat_reason, 33273 .peer_idx_b = i, 33274 } }; 33275 33276 // Goes through optionals 33277 const peer_info = ty.ptrInfo(zcu); 33278 33279 var ptr_info = opt_ptr_info orelse { 33280 opt_ptr_info = peer_info; 33281 opt_ptr_info.?.flags.size = .c; 33282 first_idx = i; 33283 continue; 33284 }; 33285 33286 // Try peer -> cur, then cur -> peer 33287 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) orelse { 33288 return .{ .conflict = .{ 33289 .peer_idx_a = first_idx, 33290 .peer_idx_b = i, 33291 } }; 33292 }).toIntern(); 33293 33294 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) { 33295 const peer_sent = try ip.getCoerced(sema.gpa, pt.tid, ptr_info.sentinel, ptr_info.child); 33296 const ptr_sent = try ip.getCoerced(sema.gpa, pt.tid, peer_info.sentinel, ptr_info.child); 33297 if (ptr_sent == peer_sent) { 33298 ptr_info.sentinel = ptr_sent; 33299 } else { 33300 ptr_info.sentinel = .none; 33301 } 33302 } else { 33303 ptr_info.sentinel = .none; 33304 } 33305 33306 // Note that the align can be always non-zero; Zcu.ptrType will canonicalize it 33307 ptr_info.flags.alignment = InternPool.Alignment.min( 33308 if (ptr_info.flags.alignment != .none) 33309 ptr_info.flags.alignment 33310 else 33311 Type.fromInterned(ptr_info.child).abiAlignment(zcu), 33312 33313 if (peer_info.flags.alignment != .none) 33314 peer_info.flags.alignment 33315 else 33316 Type.fromInterned(peer_info.child).abiAlignment(zcu), 33317 ); 33318 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 33319 return .{ .conflict = .{ 33320 .peer_idx_a = first_idx, 33321 .peer_idx_b = i, 33322 } }; 33323 } 33324 33325 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 33326 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 33327 { 33328 return .{ .conflict = .{ 33329 .peer_idx_a = first_idx, 33330 .peer_idx_b = i, 33331 } }; 33332 } 33333 33334 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 33335 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 33336 33337 opt_ptr_info = ptr_info; 33338 } 33339 return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) }; 33340 }, 33341 33342 .ptr => { 33343 // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only 33344 // if there were no actual slices. Else, we want the slice index to report a conflict. 33345 var opt_slice_idx: ?usize = null; 33346 33347 var any_abi_aligned = false; 33348 var opt_ptr_info: ?InternPool.Key.PtrType = null; 33349 var first_idx: usize = undefined; 33350 var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error 33351 33352 for (peer_tys, 0..) |opt_ty, i| { 33353 const ty = opt_ty orelse continue; 33354 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(zcu)) { 33355 .pointer => ty.ptrInfo(zcu), 33356 .@"fn" => .{ 33357 .child = ty.toIntern(), 33358 .flags = .{ 33359 .address_space = target_util.defaultAddressSpace(target, .global_constant), 33360 }, 33361 }, 33362 else => return .{ .conflict = .{ 33363 .peer_idx_a = strat_reason, 33364 .peer_idx_b = i, 33365 } }, 33366 }; 33367 33368 switch (peer_info.flags.size) { 33369 .one, .many => {}, 33370 .slice => opt_slice_idx = i, 33371 .c => return .{ .conflict = .{ 33372 .peer_idx_a = strat_reason, 33373 .peer_idx_b = i, 33374 } }, 33375 } 33376 33377 var ptr_info = opt_ptr_info orelse { 33378 opt_ptr_info = peer_info; 33379 first_idx = i; 33380 continue; 33381 }; 33382 33383 other_idx = i; 33384 33385 // We want to return this in a lot of cases, so alias it here for convenience 33386 const generic_err: PeerResolveResult = .{ .conflict = .{ 33387 .peer_idx_a = first_idx, 33388 .peer_idx_b = i, 33389 } }; 33390 33391 // Note that the align can be always non-zero; Type.ptr will canonicalize it 33392 if (peer_info.flags.alignment == .none) { 33393 any_abi_aligned = true; 33394 } else if (ptr_info.flags.alignment == .none) { 33395 any_abi_aligned = true; 33396 ptr_info.flags.alignment = peer_info.flags.alignment; 33397 } else { 33398 ptr_info.flags.alignment = ptr_info.flags.alignment.minStrict(peer_info.flags.alignment); 33399 } 33400 33401 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 33402 return generic_err; 33403 } 33404 33405 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 33406 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 33407 { 33408 return generic_err; 33409 } 33410 33411 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 33412 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 33413 ptr_info.flags.is_allowzero = ptr_info.flags.is_allowzero or peer_info.flags.is_allowzero; 33414 33415 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) { 33416 .one => switch (ip.indexToKey(peer_info.child)) { 33417 .array_type => |array_type| array_type.sentinel, 33418 else => .none, 33419 }, 33420 .many, .slice => peer_info.sentinel, 33421 .c => unreachable, 33422 }; 33423 33424 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) { 33425 .one => switch (ip.indexToKey(ptr_info.child)) { 33426 .array_type => |array_type| array_type.sentinel, 33427 else => .none, 33428 }, 33429 .many, .slice => ptr_info.sentinel, 33430 .c => unreachable, 33431 }; 33432 33433 // We abstract array handling slightly so that tuple pointers can work like array pointers 33434 const peer_pointee_array = sema.typeIsArrayLike(.fromInterned(peer_info.child)); 33435 const cur_pointee_array = sema.typeIsArrayLike(.fromInterned(ptr_info.child)); 33436 33437 // This switch is just responsible for deciding the size and pointee (not including 33438 // single-pointer array sentinel). 33439 good: { 33440 switch (peer_info.flags.size) { 33441 .one => switch (ptr_info.flags.size) { 33442 .one => { 33443 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { 33444 ptr_info.child = pointee.toIntern(); 33445 break :good; 33446 } 33447 33448 const cur_arr = cur_pointee_array orelse return generic_err; 33449 const peer_arr = peer_pointee_array orelse return generic_err; 33450 33451 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| { 33452 // *[n:x]T + *[n:y]T = *[n]T 33453 if (cur_arr.len == peer_arr.len) { 33454 ptr_info.child = (try pt.arrayType(.{ 33455 .len = cur_arr.len, 33456 .child = elem_ty.toIntern(), 33457 })).toIntern(); 33458 break :good; 33459 } 33460 // *[a]T + *[b]T = []T 33461 ptr_info.flags.size = .slice; 33462 ptr_info.child = elem_ty.toIntern(); 33463 break :good; 33464 } 33465 33466 if (peer_arr.elem_ty.toIntern() == .noreturn_type) { 33467 // *struct{} + *[a]T = []T 33468 ptr_info.flags.size = .slice; 33469 ptr_info.child = cur_arr.elem_ty.toIntern(); 33470 break :good; 33471 } 33472 33473 if (cur_arr.elem_ty.toIntern() == .noreturn_type) { 33474 // *[a]T + *struct{} = []T 33475 ptr_info.flags.size = .slice; 33476 ptr_info.child = peer_arr.elem_ty.toIntern(); 33477 break :good; 33478 } 33479 33480 return generic_err; 33481 }, 33482 .many => { 33483 // Only works for *[n]T + [*]T -> [*]T 33484 const arr = peer_pointee_array orelse return generic_err; 33485 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| { 33486 ptr_info.child = pointee.toIntern(); 33487 break :good; 33488 } 33489 if (arr.elem_ty.toIntern() == .noreturn_type) { 33490 // *struct{} + [*]T -> [*]T 33491 break :good; 33492 } 33493 return generic_err; 33494 }, 33495 .slice => { 33496 // Only works for *[n]T + []T -> []T 33497 const arr = peer_pointee_array orelse return generic_err; 33498 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| { 33499 ptr_info.child = pointee.toIntern(); 33500 break :good; 33501 } 33502 if (arr.elem_ty.toIntern() == .noreturn_type) { 33503 // *struct{} + []T -> []T 33504 break :good; 33505 } 33506 return generic_err; 33507 }, 33508 .c => unreachable, 33509 }, 33510 .many => switch (ptr_info.flags.size) { 33511 .one => { 33512 // Only works for [*]T + *[n]T -> [*]T 33513 const arr = cur_pointee_array orelse return generic_err; 33514 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| { 33515 ptr_info.flags.size = .many; 33516 ptr_info.child = pointee.toIntern(); 33517 break :good; 33518 } 33519 if (arr.elem_ty.toIntern() == .noreturn_type) { 33520 // [*]T + *struct{} -> [*]T 33521 ptr_info.flags.size = .many; 33522 ptr_info.child = peer_info.child; 33523 break :good; 33524 } 33525 return generic_err; 33526 }, 33527 .many => { 33528 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { 33529 ptr_info.child = pointee.toIntern(); 33530 break :good; 33531 } 33532 return generic_err; 33533 }, 33534 .slice => { 33535 // Only works if no peers are actually slices 33536 if (opt_slice_idx) |slice_idx| { 33537 return .{ .conflict = .{ 33538 .peer_idx_a = slice_idx, 33539 .peer_idx_b = i, 33540 } }; 33541 } 33542 // Okay, then works for [*]T + "[]T" -> [*]T 33543 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { 33544 ptr_info.flags.size = .many; 33545 ptr_info.child = pointee.toIntern(); 33546 break :good; 33547 } 33548 return generic_err; 33549 }, 33550 .c => unreachable, 33551 }, 33552 .slice => switch (ptr_info.flags.size) { 33553 .one => { 33554 // Only works for []T + *[n]T -> []T 33555 const arr = cur_pointee_array orelse return generic_err; 33556 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| { 33557 ptr_info.flags.size = .slice; 33558 ptr_info.child = pointee.toIntern(); 33559 break :good; 33560 } 33561 if (arr.elem_ty.toIntern() == .noreturn_type) { 33562 // []T + *struct{} -> []T 33563 ptr_info.flags.size = .slice; 33564 ptr_info.child = peer_info.child; 33565 break :good; 33566 } 33567 return generic_err; 33568 }, 33569 .many => { 33570 // Impossible! (current peer is an actual slice) 33571 return generic_err; 33572 }, 33573 .slice => { 33574 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { 33575 ptr_info.child = pointee.toIntern(); 33576 break :good; 33577 } 33578 return generic_err; 33579 }, 33580 .c => unreachable, 33581 }, 33582 .c => unreachable, 33583 } 33584 } 33585 33586 const sentinel_ty = switch (ptr_info.flags.size) { 33587 .one => switch (ip.indexToKey(ptr_info.child)) { 33588 .array_type => |array_type| array_type.child, 33589 else => ptr_info.child, 33590 }, 33591 .many, .slice, .c => ptr_info.child, 33592 }; 33593 33594 sentinel: { 33595 no_sentinel: { 33596 if (peer_sentinel == .none) break :no_sentinel; 33597 if (cur_sentinel == .none) break :no_sentinel; 33598 const peer_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, peer_sentinel, sentinel_ty); 33599 const cur_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, cur_sentinel, sentinel_ty); 33600 if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel; 33601 // Sentinels match 33602 if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) { 33603 .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{ 33604 .len = array_type.len, 33605 .child = array_type.child, 33606 .sentinel = cur_sent_coerced, 33607 })).toIntern(), 33608 else => unreachable, 33609 } else { 33610 ptr_info.sentinel = cur_sent_coerced; 33611 } 33612 break :sentinel; 33613 } 33614 // Clear existing sentinel 33615 ptr_info.sentinel = .none; 33616 if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) { 33617 .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{ 33618 .len = array_type.len, 33619 .child = array_type.child, 33620 .sentinel = .none, 33621 })).toIntern(), 33622 else => {}, 33623 }; 33624 } 33625 33626 opt_ptr_info = ptr_info; 33627 } 33628 33629 // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance) 33630 // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to 33631 // coerce the empty struct to a specific type, but no peer provided one. We need to 33632 // detect this case and emit an error. 33633 const pointee = opt_ptr_info.?.child; 33634 switch (pointee) { 33635 .noreturn_type => return .{ .conflict = .{ 33636 .peer_idx_a = first_idx, 33637 .peer_idx_b = other_idx, 33638 } }, 33639 else => switch (ip.indexToKey(pointee)) { 33640 .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{ 33641 .peer_idx_a = first_idx, 33642 .peer_idx_b = other_idx, 33643 } }, 33644 else => {}, 33645 }, 33646 } 33647 33648 if (any_abi_aligned and opt_ptr_info.?.flags.alignment != .none) { 33649 opt_ptr_info.?.flags.alignment = opt_ptr_info.?.flags.alignment.minStrict( 33650 try Type.fromInterned(pointee).abiAlignmentSema(pt), 33651 ); 33652 } 33653 33654 return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) }; 33655 }, 33656 33657 .func => { 33658 var opt_cur_ty: ?Type = null; 33659 var first_idx: usize = undefined; 33660 for (peer_tys, 0..) |opt_ty, i| { 33661 const ty = opt_ty orelse continue; 33662 const cur_ty = opt_cur_ty orelse { 33663 opt_cur_ty = ty; 33664 first_idx = i; 33665 continue; 33666 }; 33667 if (ty.zigTypeTag(zcu) != .@"fn") return .{ .conflict = .{ 33668 .peer_idx_a = strat_reason, 33669 .peer_idx_b = i, 33670 } }; 33671 // ty -> cur_ty 33672 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, false, target, src, src)) { 33673 continue; 33674 } 33675 // cur_ty -> ty 33676 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, false, target, src, src)) { 33677 opt_cur_ty = ty; 33678 continue; 33679 } 33680 return .{ .conflict = .{ 33681 .peer_idx_a = first_idx, 33682 .peer_idx_b = i, 33683 } }; 33684 } 33685 return .{ .success = opt_cur_ty.? }; 33686 }, 33687 33688 .enum_or_union => { 33689 var opt_cur_ty: ?Type = null; 33690 // The peer index which gave the current type 33691 var cur_ty_idx: usize = undefined; 33692 33693 for (peer_tys, 0..) |opt_ty, i| { 33694 const ty = opt_ty orelse continue; 33695 switch (ty.zigTypeTag(zcu)) { 33696 .enum_literal, .@"enum", .@"union" => {}, 33697 else => return .{ .conflict = .{ 33698 .peer_idx_a = strat_reason, 33699 .peer_idx_b = i, 33700 } }, 33701 } 33702 const cur_ty = opt_cur_ty orelse { 33703 opt_cur_ty = ty; 33704 cur_ty_idx = i; 33705 continue; 33706 }; 33707 33708 // We want to return this in a lot of cases, so alias it here for convenience 33709 const generic_err: PeerResolveResult = .{ .conflict = .{ 33710 .peer_idx_a = cur_ty_idx, 33711 .peer_idx_b = i, 33712 } }; 33713 33714 switch (cur_ty.zigTypeTag(zcu)) { 33715 .enum_literal => { 33716 opt_cur_ty = ty; 33717 cur_ty_idx = i; 33718 }, 33719 .@"enum" => switch (ty.zigTypeTag(zcu)) { 33720 .enum_literal => {}, 33721 .@"enum" => { 33722 if (!ty.eql(cur_ty, zcu)) return generic_err; 33723 }, 33724 .@"union" => { 33725 const tag_ty = ty.unionTagTypeHypothetical(zcu); 33726 if (!tag_ty.eql(cur_ty, zcu)) return generic_err; 33727 opt_cur_ty = ty; 33728 cur_ty_idx = i; 33729 }, 33730 else => unreachable, 33731 }, 33732 .@"union" => switch (ty.zigTypeTag(zcu)) { 33733 .enum_literal => {}, 33734 .@"enum" => { 33735 const cur_tag_ty = cur_ty.unionTagTypeHypothetical(zcu); 33736 if (!ty.eql(cur_tag_ty, zcu)) return generic_err; 33737 }, 33738 .@"union" => { 33739 if (!ty.eql(cur_ty, zcu)) return generic_err; 33740 }, 33741 else => unreachable, 33742 }, 33743 else => unreachable, 33744 } 33745 } 33746 return .{ .success = opt_cur_ty.? }; 33747 }, 33748 33749 .comptime_int => { 33750 for (peer_tys, 0..) |opt_ty, i| { 33751 const ty = opt_ty orelse continue; 33752 switch (ty.zigTypeTag(zcu)) { 33753 .comptime_int => {}, 33754 else => return .{ .conflict = .{ 33755 .peer_idx_a = strat_reason, 33756 .peer_idx_b = i, 33757 } }, 33758 } 33759 } 33760 return .{ .success = .comptime_int }; 33761 }, 33762 33763 .comptime_float => { 33764 for (peer_tys, 0..) |opt_ty, i| { 33765 const ty = opt_ty orelse continue; 33766 switch (ty.zigTypeTag(zcu)) { 33767 .comptime_int, .comptime_float => {}, 33768 else => return .{ .conflict = .{ 33769 .peer_idx_a = strat_reason, 33770 .peer_idx_b = i, 33771 } }, 33772 } 33773 } 33774 return .{ .success = .comptime_float }; 33775 }, 33776 33777 .fixed_int => { 33778 var idx_unsigned: ?usize = null; 33779 var idx_signed: ?usize = null; 33780 33781 // TODO: this is for compatibility with legacy behavior. See beneath the loop. 33782 var any_comptime_known = false; 33783 33784 for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| { 33785 const ty = opt_ty orelse continue; 33786 const opt_val = ptr_opt_val.*; 33787 33788 const peer_tag = ty.zigTypeTag(zcu); 33789 switch (peer_tag) { 33790 .comptime_int => { 33791 // If the value is undefined, we can't refine to a fixed-width int 33792 if (opt_val == null or opt_val.?.isUndef(zcu)) return .{ .conflict = .{ 33793 .peer_idx_a = strat_reason, 33794 .peer_idx_b = i, 33795 } }; 33796 any_comptime_known = true; 33797 ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?); 33798 continue; 33799 }, 33800 .int => {}, 33801 else => return .{ .conflict = .{ 33802 .peer_idx_a = strat_reason, 33803 .peer_idx_b = i, 33804 } }, 33805 } 33806 33807 if (opt_val != null) any_comptime_known = true; 33808 33809 const info = ty.intInfo(zcu); 33810 33811 const idx_ptr = switch (info.signedness) { 33812 .unsigned => &idx_unsigned, 33813 .signed => &idx_signed, 33814 }; 33815 33816 const largest_idx = idx_ptr.* orelse { 33817 idx_ptr.* = i; 33818 continue; 33819 }; 33820 33821 const cur_info = peer_tys[largest_idx].?.intInfo(zcu); 33822 if (info.bits > cur_info.bits) { 33823 idx_ptr.* = i; 33824 } 33825 } 33826 33827 if (idx_signed == null) { 33828 return .{ .success = peer_tys[idx_unsigned.?].? }; 33829 } 33830 33831 if (idx_unsigned == null) { 33832 return .{ .success = peer_tys[idx_signed.?].? }; 33833 } 33834 33835 const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(zcu); 33836 const signed_info = peer_tys[idx_signed.?].?.intInfo(zcu); 33837 if (signed_info.bits > unsigned_info.bits) { 33838 return .{ .success = peer_tys[idx_signed.?].? }; 33839 } 33840 33841 // TODO: this is for compatibility with legacy behavior. Before this version of PTR was 33842 // implemented, the algorithm very often returned false positives, with the expectation 33843 // that you'd just hit a coercion error later. One of these was that for integers, the 33844 // largest type would always be returned, even if it couldn't fit everything. This had 33845 // an unintentional consequence to semantics, which is that if values were known at 33846 // comptime, they would be coerced down to the smallest type where possible. This 33847 // behavior is unintuitive and order-dependent, so in my opinion should be eliminated, 33848 // but for now we'll retain compatibility. 33849 if (any_comptime_known) { 33850 if (unsigned_info.bits > signed_info.bits) { 33851 return .{ .success = peer_tys[idx_unsigned.?].? }; 33852 } 33853 const idx = @min(idx_unsigned.?, idx_signed.?); 33854 return .{ .success = peer_tys[idx].? }; 33855 } 33856 33857 return .{ .conflict = .{ 33858 .peer_idx_a = idx_unsigned.?, 33859 .peer_idx_b = idx_signed.?, 33860 } }; 33861 }, 33862 33863 .fixed_float => { 33864 var opt_cur_ty: ?Type = null; 33865 33866 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 33867 const ty = opt_ty orelse continue; 33868 switch (ty.zigTypeTag(zcu)) { 33869 .comptime_float, .comptime_int => {}, 33870 .int => { 33871 if (opt_val == null) return .{ .conflict = .{ 33872 .peer_idx_a = strat_reason, 33873 .peer_idx_b = i, 33874 } }; 33875 }, 33876 .float => { 33877 if (opt_cur_ty) |cur_ty| { 33878 if (cur_ty.eql(ty, zcu)) continue; 33879 // Recreate the type so we eliminate any c_longdouble 33880 const bits = @max(cur_ty.floatBits(target), ty.floatBits(target)); 33881 opt_cur_ty = switch (bits) { 33882 16 => .f16, 33883 32 => .f32, 33884 64 => .f64, 33885 80 => .f80, 33886 128 => .f128, 33887 else => unreachable, 33888 }; 33889 } else { 33890 opt_cur_ty = ty; 33891 } 33892 }, 33893 else => return .{ .conflict = .{ 33894 .peer_idx_a = strat_reason, 33895 .peer_idx_b = i, 33896 } }, 33897 } 33898 } 33899 33900 // Note that fixed_float is only chosen if there is at least one fixed-width float peer, 33901 // so opt_cur_ty must be non-null. 33902 return .{ .success = opt_cur_ty.? }; 33903 }, 33904 33905 .tuple => { 33906 // First, check that every peer has the same approximate structure (field count) 33907 33908 var opt_first_idx: ?usize = null; 33909 var is_tuple: bool = undefined; 33910 var field_count: usize = undefined; 33911 33912 for (peer_tys, 0..) |opt_ty, i| { 33913 const ty = opt_ty orelse continue; 33914 33915 if (!ty.isTuple(zcu)) { 33916 return .{ .conflict = .{ 33917 .peer_idx_a = strat_reason, 33918 .peer_idx_b = i, 33919 } }; 33920 } 33921 33922 const first_idx = opt_first_idx orelse { 33923 opt_first_idx = i; 33924 is_tuple = ty.isTuple(zcu); 33925 field_count = ty.structFieldCount(zcu); 33926 continue; 33927 }; 33928 33929 if (ty.structFieldCount(zcu) != field_count) { 33930 return .{ .conflict = .{ 33931 .peer_idx_a = first_idx, 33932 .peer_idx_b = i, 33933 } }; 33934 } 33935 } 33936 33937 assert(opt_first_idx != null); 33938 33939 // Now, we'll recursively resolve the field types 33940 const field_types = try sema.arena.alloc(InternPool.Index, field_count); 33941 // Values for `comptime` fields - `.none` used for non-comptime fields 33942 const field_vals = try sema.arena.alloc(InternPool.Index, field_count); 33943 const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); 33944 const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); 33945 33946 for (field_types, field_vals, 0..) |*field_ty, *field_val, field_index| { 33947 // Fill buffers with types and values of the field 33948 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { 33949 const ty = opt_ty orelse { 33950 peer_field_ty.* = null; 33951 peer_field_val.* = null; 33952 continue; 33953 }; 33954 peer_field_ty.* = ty.fieldType(field_index, zcu); 33955 peer_field_val.* = if (opt_val) |val| try val.fieldValue(pt, field_index) else null; 33956 } 33957 33958 // Resolve field type recursively 33959 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) { 33960 .success => |ty| ty.toIntern(), 33961 else => |result| { 33962 const result_buf = try sema.arena.create(PeerResolveResult); 33963 result_buf.* = result; 33964 const field_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); 33965 33966 // The error info needs the field types, but we can't reuse sub_peer_tys 33967 // since the recursive call may have clobbered it. 33968 const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len); 33969 for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| { 33970 // Already-resolved types won't be referenced by the error so it's fine 33971 // to leave them undefined. 33972 const ty = opt_ty orelse continue; 33973 peer_field_ty.* = ty.fieldType(field_index, zcu); 33974 } 33975 33976 return .{ .field_error = .{ 33977 .field_name = field_name, 33978 .field_types = peer_field_tys, 33979 .sub_result = result_buf, 33980 } }; 33981 }, 33982 }; 33983 33984 // Decide if this is a comptime field. If it is comptime in all peers, and the 33985 // coerced comptime values are all the same, we say it is comptime, else not. 33986 33987 var comptime_val: ?Value = null; 33988 for (peer_tys) |opt_ty| { 33989 const struct_ty = opt_ty orelse continue; 33990 try struct_ty.resolveStructFieldInits(pt); 33991 33992 const uncoerced_field_val = try struct_ty.structFieldValueComptime(pt, field_index) orelse { 33993 comptime_val = null; 33994 break; 33995 }; 33996 const uncoerced_field = Air.internedToRef(uncoerced_field_val.toIntern()); 33997 const coerced_inst = sema.coerceExtra(block, .fromInterned(field_ty.*), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) { 33998 // It's possible for PTR to give false positives. Just give up on making this a comptime field, we'll get an error later anyway 33999 error.NotCoercible => { 34000 comptime_val = null; 34001 break; 34002 }, 34003 else => |e| return e, 34004 }; 34005 const coerced_val = (try sema.resolveValue(coerced_inst)) orelse continue; 34006 const existing = comptime_val orelse { 34007 comptime_val = coerced_val; 34008 continue; 34009 }; 34010 if (!coerced_val.eql(existing, .fromInterned(field_ty.*), zcu)) { 34011 comptime_val = null; 34012 break; 34013 } 34014 } 34015 34016 field_val.* = if (comptime_val) |v| v.toIntern() else .none; 34017 } 34018 34019 const final_ty = try ip.getTupleType(zcu.gpa, pt.tid, .{ 34020 .types = field_types, 34021 .values = field_vals, 34022 }); 34023 34024 return .{ .success = .fromInterned(final_ty) }; 34025 }, 34026 34027 .exact => { 34028 var expect_ty: ?Type = null; 34029 var first_idx: usize = undefined; 34030 for (peer_tys, 0..) |opt_ty, i| { 34031 const ty = opt_ty orelse continue; 34032 if (expect_ty) |expect| { 34033 if (!ty.eql(expect, zcu)) return .{ .conflict = .{ 34034 .peer_idx_a = first_idx, 34035 .peer_idx_b = i, 34036 } }; 34037 } else { 34038 expect_ty = ty; 34039 first_idx = i; 34040 } 34041 } 34042 return .{ .success = expect_ty.? }; 34043 }, 34044 } 34045 } 34046 34047 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { 34048 // e0 -> e1 34049 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { 34050 return e1; 34051 } 34052 34053 // e1 -> e0 34054 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { 34055 return e0; 34056 } 34057 34058 return sema.errorSetMerge(e0, e1); 34059 } 34060 34061 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { 34062 const target = sema.pt.zcu.getTarget(); 34063 34064 // ty_b -> ty_a 34065 if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, false, target, src, src, null)) { 34066 return ty_a; 34067 } 34068 34069 // ty_a -> ty_b 34070 if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, false, target, src, src, null)) { 34071 return ty_b; 34072 } 34073 34074 return null; 34075 } 34076 34077 const ArrayLike = struct { 34078 len: u64, 34079 /// `noreturn` indicates that this type is `struct{}` so can coerce to anything 34080 elem_ty: Type, 34081 }; 34082 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { 34083 const pt = sema.pt; 34084 const zcu = pt.zcu; 34085 return switch (ty.zigTypeTag(zcu)) { 34086 .array => .{ 34087 .len = ty.arrayLen(zcu), 34088 .elem_ty = ty.childType(zcu), 34089 }, 34090 .@"struct" => { 34091 const field_count = ty.structFieldCount(zcu); 34092 if (field_count == 0) return .{ 34093 .len = 0, 34094 .elem_ty = .noreturn, 34095 }; 34096 if (!ty.isTuple(zcu)) return null; 34097 const elem_ty = ty.fieldType(0, zcu); 34098 for (1..field_count) |i| { 34099 if (!ty.fieldType(i, zcu).eql(elem_ty, zcu)) { 34100 return null; 34101 } 34102 } 34103 return .{ 34104 .len = field_count, 34105 .elem_ty = elem_ty, 34106 }; 34107 }, 34108 else => null, 34109 }; 34110 } 34111 34112 pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void { 34113 const pt = sema.pt; 34114 const zcu = pt.zcu; 34115 const ip = &zcu.intern_pool; 34116 34117 if (sema.fn_ret_ty_ies) |ies| { 34118 try sema.resolveInferredErrorSetPtr(block, src, ies); 34119 assert(ies.resolved != .none); 34120 ip.funcIesResolved(sema.func_index).* = ies.resolved; 34121 } 34122 } 34123 34124 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type, src: LazySrcLoc) CompileError!void { 34125 const pt = sema.pt; 34126 const zcu = pt.zcu; 34127 const ip = &zcu.intern_pool; 34128 const fn_ty_info = zcu.typeToFunc(fn_ty).?; 34129 34130 try Type.fromInterned(fn_ty_info.return_type).resolveFully(pt); 34131 34132 if (zcu.comp.config.any_error_tracing and 34133 Type.fromInterned(fn_ty_info.return_type).isError(zcu)) 34134 { 34135 // Ensure the type exists so that backends can assume that. 34136 _ = try sema.getBuiltinType(src, .StackTrace); 34137 } 34138 34139 for (0..fn_ty_info.param_types.len) |i| { 34140 try Type.fromInterned(fn_ty_info.param_types.get(ip)[i]).resolveFully(pt); 34141 } 34142 } 34143 34144 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { 34145 return val.resolveLazy(sema.arena, sema.pt); 34146 } 34147 34148 /// Resolve a struct's alignment only without triggering resolution of its layout. 34149 /// Asserts that the alignment is not yet resolved and the layout is non-packed. 34150 pub fn resolveStructAlignment( 34151 sema: *Sema, 34152 ty: InternPool.Index, 34153 struct_type: InternPool.LoadedStructType, 34154 ) SemaError!void { 34155 const pt = sema.pt; 34156 const zcu = pt.zcu; 34157 const ip = &zcu.intern_pool; 34158 const target = zcu.getTarget(); 34159 34160 assert(sema.owner.unwrap().type == ty); 34161 34162 assert(struct_type.layout != .@"packed"); 34163 assert(struct_type.flagsUnordered(ip).alignment == .none); 34164 34165 const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); 34166 34167 // We'll guess "pointer-aligned", if the struct has an 34168 // underaligned pointer field then some allocations 34169 // might require explicit alignment. 34170 if (struct_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return; 34171 34172 try sema.resolveStructFieldTypes(ty, struct_type); 34173 34174 // We'll guess "pointer-aligned", if the struct has an 34175 // underaligned pointer field then some allocations 34176 // might require explicit alignment. 34177 if (struct_type.assumePointerAlignedIfWip(ip, ptr_align)) return; 34178 defer struct_type.clearAlignmentWip(ip); 34179 34180 var alignment: Alignment = .@"1"; 34181 34182 for (0..struct_type.field_types.len) |i| { 34183 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34184 if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) 34185 continue; 34186 const field_align = try field_ty.structFieldAlignmentSema( 34187 struct_type.fieldAlign(ip, i), 34188 struct_type.layout, 34189 pt, 34190 ); 34191 alignment = alignment.maxStrict(field_align); 34192 } 34193 34194 struct_type.setAlignment(ip, alignment); 34195 } 34196 34197 pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void { 34198 const pt = sema.pt; 34199 const zcu = pt.zcu; 34200 const ip = &zcu.intern_pool; 34201 const struct_type = zcu.typeToStruct(ty) orelse return; 34202 34203 assert(sema.owner.unwrap().type == ty.toIntern()); 34204 34205 if (struct_type.haveLayout(ip)) 34206 return; 34207 34208 try sema.resolveStructFieldTypes(ty.toIntern(), struct_type); 34209 34210 if (struct_type.layout == .@"packed") { 34211 sema.backingIntType(struct_type) catch |err| switch (err) { 34212 error.OutOfMemory, error.AnalysisFail => |e| return e, 34213 error.ComptimeBreak, error.ComptimeReturn => unreachable, 34214 }; 34215 return; 34216 } 34217 34218 if (struct_type.setLayoutWip(ip)) { 34219 const msg = try sema.errMsg( 34220 ty.srcLoc(zcu), 34221 "struct '{f}' depends on itself", 34222 .{ty.fmt(pt)}, 34223 ); 34224 return sema.failWithOwnedErrorMsg(null, msg); 34225 } 34226 defer struct_type.clearLayoutWip(ip); 34227 34228 const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len); 34229 const sizes = try sema.arena.alloc(u64, struct_type.field_types.len); 34230 34231 var big_align: Alignment = .@"1"; 34232 34233 for (aligns, sizes, 0..) |*field_align, *field_size, i| { 34234 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34235 if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) { 34236 struct_type.offsets.get(ip)[i] = 0; 34237 field_size.* = 0; 34238 field_align.* = .none; 34239 continue; 34240 } 34241 34242 field_size.* = field_ty.abiSizeSema(pt) catch |err| switch (err) { 34243 error.AnalysisFail => { 34244 const msg = sema.err orelse return err; 34245 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); 34246 return err; 34247 }, 34248 else => return err, 34249 }; 34250 field_align.* = try field_ty.structFieldAlignmentSema( 34251 struct_type.fieldAlign(ip, i), 34252 struct_type.layout, 34253 pt, 34254 ); 34255 big_align = big_align.maxStrict(field_align.*); 34256 } 34257 34258 if (struct_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) { 34259 const msg = try sema.errMsg( 34260 ty.srcLoc(zcu), 34261 "struct layout depends on it having runtime bits", 34262 .{}, 34263 ); 34264 return sema.failWithOwnedErrorMsg(null, msg); 34265 } 34266 34267 if (struct_type.flagsUnordered(ip).assumed_pointer_aligned and 34268 big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(zcu.getTarget().ptrBitWidth(), 8)))) 34269 { 34270 const msg = try sema.errMsg( 34271 ty.srcLoc(zcu), 34272 "struct layout depends on being pointer aligned", 34273 .{}, 34274 ); 34275 return sema.failWithOwnedErrorMsg(null, msg); 34276 } 34277 34278 if (struct_type.hasReorderedFields()) { 34279 const runtime_order = struct_type.runtime_order.get(ip); 34280 34281 for (runtime_order, 0..) |*ro, i| { 34282 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34283 if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) { 34284 ro.* = .omitted; 34285 } else { 34286 ro.* = @enumFromInt(i); 34287 } 34288 } 34289 34290 const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder; 34291 34292 const AlignSortContext = struct { 34293 aligns: []const Alignment, 34294 34295 fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool { 34296 if (a == .omitted) return false; 34297 if (b == .omitted) return true; 34298 const a_align = ctx.aligns[@intFromEnum(a)]; 34299 const b_align = ctx.aligns[@intFromEnum(b)]; 34300 return a_align.compare(.gt, b_align); 34301 } 34302 }; 34303 if (!zcu.backendSupportsFeature(.field_reordering)) { 34304 // TODO: we should probably also reorder tuple fields? This is a bit weird because it'll involve 34305 // mutating the `InternPool` for a non-container type. 34306 // 34307 // TODO: implement field reordering support in all the backends! 34308 // 34309 // This logic does not reorder fields; it only moves the omitted ones to the end 34310 // so that logic elsewhere does not need to special-case here. 34311 var i: usize = 0; 34312 var off: usize = 0; 34313 while (i + off < runtime_order.len) { 34314 if (runtime_order[i + off] == .omitted) { 34315 off += 1; 34316 continue; 34317 } 34318 runtime_order[i] = runtime_order[i + off]; 34319 i += 1; 34320 } 34321 @memset(runtime_order[i..], .omitted); 34322 } else { 34323 mem.sortUnstable(RuntimeOrder, runtime_order, AlignSortContext{ 34324 .aligns = aligns, 34325 }, AlignSortContext.lessThan); 34326 } 34327 } 34328 34329 // Calculate size, alignment, and field offsets. 34330 const offsets = struct_type.offsets.get(ip); 34331 var it = struct_type.iterateRuntimeOrder(ip); 34332 var offset: u64 = 0; 34333 while (it.next()) |i| { 34334 offsets[i] = @intCast(aligns[i].forward(offset)); 34335 offset = offsets[i] + sizes[i]; 34336 } 34337 const size = std.math.cast(u32, big_align.forward(offset)) orelse { 34338 const msg = try sema.errMsg( 34339 ty.srcLoc(zcu), 34340 "struct layout requires size {d}, this compiler implementation supports up to {d}", 34341 .{ big_align.forward(offset), std.math.maxInt(u32) }, 34342 ); 34343 return sema.failWithOwnedErrorMsg(null, msg); 34344 }; 34345 struct_type.setLayoutResolved(ip, size, big_align); 34346 _ = try ty.comptimeOnlySema(pt); 34347 } 34348 34349 fn backingIntType( 34350 sema: *Sema, 34351 struct_type: InternPool.LoadedStructType, 34352 ) CompileError!void { 34353 const pt = sema.pt; 34354 const zcu = pt.zcu; 34355 const gpa = zcu.gpa; 34356 const ip = &zcu.intern_pool; 34357 34358 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 34359 defer analysis_arena.deinit(); 34360 34361 var block: Block = .{ 34362 .parent = null, 34363 .sema = sema, 34364 .namespace = struct_type.namespace, 34365 .instructions = .{}, 34366 .inlining = null, 34367 .comptime_reason = null, // set below if needed 34368 .src_base_inst = struct_type.zir_index, 34369 .type_name_ctx = struct_type.name, 34370 }; 34371 defer assert(block.instructions.items.len == 0); 34372 34373 const fields_bit_sum = blk: { 34374 var accumulator: u64 = 0; 34375 for (0..struct_type.field_types.len) |i| { 34376 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34377 accumulator += try field_ty.bitSizeSema(pt); 34378 } 34379 break :blk accumulator; 34380 }; 34381 34382 const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?; 34383 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; 34384 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; 34385 assert(extended.opcode == .struct_decl); 34386 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 34387 34388 if (small.has_backing_int) { 34389 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; 34390 const captures_len = if (small.has_captures_len) blk: { 34391 const captures_len = zir.extra[extra_index]; 34392 extra_index += 1; 34393 break :blk captures_len; 34394 } else 0; 34395 extra_index += @intFromBool(small.has_fields_len); 34396 extra_index += @intFromBool(small.has_decls_len); 34397 34398 extra_index += captures_len * 2; 34399 34400 const backing_int_body_len = zir.extra[extra_index]; 34401 extra_index += 1; 34402 34403 const backing_int_src: LazySrcLoc = .{ 34404 .base_node_inst = struct_type.zir_index, 34405 .offset = .{ .node_offset_container_tag = .zero }, 34406 }; 34407 block.comptime_reason = .{ .reason = .{ 34408 .src = backing_int_src, 34409 .r = .{ .simple = .type }, 34410 } }; 34411 const backing_int_ty = blk: { 34412 if (backing_int_body_len == 0) { 34413 const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 34414 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref); 34415 } else { 34416 const body = zir.bodySlice(extra_index, backing_int_body_len); 34417 const ty_ref = try sema.resolveInlineBody(&block, body, zir_index); 34418 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref); 34419 } 34420 }; 34421 34422 try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); 34423 struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); 34424 } else { 34425 if (fields_bit_sum > std.math.maxInt(u16)) { 34426 return sema.fail(&block, block.nodeOffset(.zero), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum}); 34427 } 34428 const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum)); 34429 struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); 34430 } 34431 34432 try sema.flushExports(); 34433 } 34434 34435 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { 34436 const pt = sema.pt; 34437 const zcu = pt.zcu; 34438 34439 if (!backing_int_ty.isInt(zcu)) { 34440 return sema.fail(block, src, "expected backing integer type, found '{f}'", .{backing_int_ty.fmt(pt)}); 34441 } 34442 if (backing_int_ty.bitSize(zcu) != fields_bit_sum) { 34443 return sema.fail( 34444 block, 34445 src, 34446 "backing integer type '{f}' has bit size {d} but the struct fields have a total bit size of {d}", 34447 .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum }, 34448 ); 34449 } 34450 } 34451 34452 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 34453 const pt = sema.pt; 34454 if (!ty.isIndexable(pt.zcu)) { 34455 const msg = msg: { 34456 const msg = try sema.errMsg(src, "type '{f}' does not support indexing", .{ty.fmt(pt)}); 34457 errdefer msg.destroy(sema.gpa); 34458 try sema.errNote(src, msg, "operand must be an array, slice, tuple, or vector", .{}); 34459 break :msg msg; 34460 }; 34461 return sema.failWithOwnedErrorMsg(block, msg); 34462 } 34463 } 34464 34465 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 34466 const pt = sema.pt; 34467 const zcu = pt.zcu; 34468 if (ty.zigTypeTag(zcu) == .pointer) { 34469 switch (ty.ptrSize(zcu)) { 34470 .slice, .many, .c => return, 34471 .one => { 34472 const elem_ty = ty.childType(zcu); 34473 if (elem_ty.zigTypeTag(zcu) == .array) return; 34474 // TODO https://github.com/ziglang/zig/issues/15479 34475 // if (elem_ty.isTuple()) return; 34476 }, 34477 } 34478 } 34479 const msg = msg: { 34480 const msg = try sema.errMsg(src, "type '{f}' is not an indexable pointer", .{ty.fmt(pt)}); 34481 errdefer msg.destroy(sema.gpa); 34482 try sema.errNote(src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); 34483 break :msg msg; 34484 }; 34485 return sema.failWithOwnedErrorMsg(block, msg); 34486 } 34487 34488 /// Resolve a unions's alignment only without triggering resolution of its layout. 34489 /// Asserts that the alignment is not yet resolved. 34490 pub fn resolveUnionAlignment( 34491 sema: *Sema, 34492 ty: Type, 34493 union_type: InternPool.LoadedUnionType, 34494 ) SemaError!void { 34495 const pt = sema.pt; 34496 const zcu = pt.zcu; 34497 const ip = &zcu.intern_pool; 34498 const target = zcu.getTarget(); 34499 34500 assert(sema.owner.unwrap().type == ty.toIntern()); 34501 34502 assert(!union_type.haveLayout(ip)); 34503 34504 const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); 34505 34506 // We'll guess "pointer-aligned", if the union has an 34507 // underaligned pointer field then some allocations 34508 // might require explicit alignment. 34509 if (union_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return; 34510 34511 try sema.resolveUnionFieldTypes(ty, union_type); 34512 34513 var max_align: Alignment = .@"1"; 34514 for (0..union_type.field_types.len) |field_index| { 34515 const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]); 34516 if (!(try field_ty.hasRuntimeBitsSema(pt))) continue; 34517 34518 const explicit_align = union_type.fieldAlign(ip, field_index); 34519 const field_align = if (explicit_align != .none) 34520 explicit_align 34521 else 34522 try field_ty.abiAlignmentSema(sema.pt); 34523 34524 max_align = max_align.max(field_align); 34525 } 34526 34527 union_type.setAlignment(ip, max_align); 34528 } 34529 34530 /// This logic must be kept in sync with `Type.getUnionLayout`. 34531 pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void { 34532 const pt = sema.pt; 34533 const ip = &pt.zcu.intern_pool; 34534 34535 try sema.resolveUnionFieldTypes(ty, ip.loadUnionType(ty.ip_index)); 34536 34537 // Load again, since the tag type might have changed due to resolution. 34538 const union_type = ip.loadUnionType(ty.ip_index); 34539 34540 assert(sema.owner.unwrap().type == ty.toIntern()); 34541 34542 const old_flags = union_type.flagsUnordered(ip); 34543 switch (old_flags.status) { 34544 .none, .have_field_types => {}, 34545 .field_types_wip, .layout_wip => { 34546 const msg = try sema.errMsg( 34547 ty.srcLoc(pt.zcu), 34548 "union '{f}' depends on itself", 34549 .{ty.fmt(pt)}, 34550 ); 34551 return sema.failWithOwnedErrorMsg(null, msg); 34552 }, 34553 .have_layout, .fully_resolved_wip, .fully_resolved => return, 34554 } 34555 34556 errdefer union_type.setStatusIfLayoutWip(ip, old_flags.status); 34557 34558 union_type.setStatus(ip, .layout_wip); 34559 34560 var max_size: u64 = 0; 34561 var max_align: Alignment = .@"1"; 34562 for (0..union_type.field_types.len) |field_index| { 34563 const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]); 34564 if (field_ty.isNoReturn(pt.zcu)) continue; 34565 34566 // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s, 34567 // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type, 34568 // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us. 34569 // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to 34570 // be queried, enabling failures to be handled with the emission of a compile error, and also in 34571 // `hasRuntimeBits` for ever being able to infinite recurse in the first place. 34572 try field_ty.resolveLayout(pt); 34573 34574 if (try field_ty.hasRuntimeBitsSema(pt)) { 34575 max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) { 34576 error.AnalysisFail => { 34577 const msg = sema.err orelse return err; 34578 try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{}); 34579 return err; 34580 }, 34581 else => return err, 34582 }); 34583 } 34584 34585 const explicit_align = union_type.fieldAlign(ip, field_index); 34586 const field_align = if (explicit_align != .none) 34587 explicit_align 34588 else 34589 try field_ty.abiAlignmentSema(pt); 34590 max_align = max_align.max(field_align); 34591 } 34592 34593 const has_runtime_tag = union_type.flagsUnordered(ip).runtime_tag.hasTag() and 34594 try Type.fromInterned(union_type.enum_tag_ty).hasRuntimeBitsSema(pt); 34595 const size, const alignment, const padding = if (has_runtime_tag) layout: { 34596 const enum_tag_type: Type = .fromInterned(union_type.enum_tag_ty); 34597 const tag_align = try enum_tag_type.abiAlignmentSema(pt); 34598 const tag_size = try enum_tag_type.abiSizeSema(pt); 34599 34600 // Put the tag before or after the payload depending on which one's 34601 // alignment is greater. 34602 var size: u64 = 0; 34603 var padding: u32 = 0; 34604 if (tag_align.order(max_align).compare(.gte)) { 34605 // {Tag, Payload} 34606 size += tag_size; 34607 size = max_align.forward(size); 34608 size += max_size; 34609 const prev_size = size; 34610 size = tag_align.forward(size); 34611 padding = @intCast(size - prev_size); 34612 } else { 34613 // {Payload, Tag} 34614 size += max_size; 34615 size = switch (pt.zcu.getTarget().ofmt) { 34616 .c => max_align, 34617 else => tag_align, 34618 }.forward(size); 34619 size += tag_size; 34620 const prev_size = size; 34621 size = max_align.forward(size); 34622 padding = @intCast(size - prev_size); 34623 } 34624 34625 break :layout .{ size, max_align.max(tag_align), padding }; 34626 } else .{ max_align.forward(max_size), max_align, 0 }; 34627 34628 const casted_size = std.math.cast(u32, size) orelse { 34629 const msg = try sema.errMsg( 34630 ty.srcLoc(pt.zcu), 34631 "union layout requires size {d}, this compiler implementation supports up to {d}", 34632 .{ size, std.math.maxInt(u32) }, 34633 ); 34634 return sema.failWithOwnedErrorMsg(null, msg); 34635 }; 34636 union_type.setHaveLayout(ip, casted_size, padding, alignment); 34637 34638 if (union_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) { 34639 const msg = try sema.errMsg( 34640 ty.srcLoc(pt.zcu), 34641 "union layout depends on it having runtime bits", 34642 .{}, 34643 ); 34644 return sema.failWithOwnedErrorMsg(null, msg); 34645 } 34646 34647 if (union_type.flagsUnordered(ip).assumed_pointer_aligned and 34648 alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(pt.zcu.getTarget().ptrBitWidth(), 8)))) 34649 { 34650 const msg = try sema.errMsg( 34651 ty.srcLoc(pt.zcu), 34652 "union layout depends on being pointer aligned", 34653 .{}, 34654 ); 34655 return sema.failWithOwnedErrorMsg(null, msg); 34656 } 34657 _ = try ty.comptimeOnlySema(pt); 34658 } 34659 34660 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to 34661 /// be resolved. 34662 pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void { 34663 try sema.resolveStructLayout(ty); 34664 try sema.resolveStructFieldInits(ty); 34665 34666 const pt = sema.pt; 34667 const zcu = pt.zcu; 34668 const ip = &zcu.intern_pool; 34669 const struct_type = zcu.typeToStruct(ty).?; 34670 34671 assert(sema.owner.unwrap().type == ty.toIntern()); 34672 34673 if (struct_type.setFullyResolved(ip)) return; 34674 errdefer struct_type.clearFullyResolved(ip); 34675 34676 // After we have resolve struct layout we have to go over the fields again to 34677 // make sure pointer fields get their child types resolved as well. 34678 // See also similar code for unions. 34679 34680 for (0..struct_type.field_types.len) |i| { 34681 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 34682 try field_ty.resolveFully(pt); 34683 } 34684 } 34685 34686 pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void { 34687 try sema.resolveUnionLayout(ty); 34688 34689 const pt = sema.pt; 34690 const zcu = pt.zcu; 34691 const ip = &zcu.intern_pool; 34692 const union_obj = zcu.typeToUnion(ty).?; 34693 34694 assert(sema.owner.unwrap().type == ty.toIntern()); 34695 34696 switch (union_obj.flagsUnordered(ip).status) { 34697 .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, 34698 .fully_resolved_wip, .fully_resolved => return, 34699 } 34700 34701 { 34702 // After we have resolve union layout we have to go over the fields again to 34703 // make sure pointer fields get their child types resolved as well. 34704 // See also similar code for structs. 34705 const prev_status = union_obj.flagsUnordered(ip).status; 34706 errdefer union_obj.setStatus(ip, prev_status); 34707 34708 union_obj.setStatus(ip, .fully_resolved_wip); 34709 for (0..union_obj.field_types.len) |field_index| { 34710 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); 34711 try field_ty.resolveFully(pt); 34712 } 34713 union_obj.setStatus(ip, .fully_resolved); 34714 } 34715 34716 // And let's not forget comptime-only status. 34717 _ = try ty.comptimeOnlySema(pt); 34718 } 34719 34720 pub fn resolveStructFieldTypes( 34721 sema: *Sema, 34722 ty: InternPool.Index, 34723 struct_type: InternPool.LoadedStructType, 34724 ) SemaError!void { 34725 const pt = sema.pt; 34726 const zcu = pt.zcu; 34727 const ip = &zcu.intern_pool; 34728 34729 assert(sema.owner.unwrap().type == ty); 34730 34731 if (struct_type.haveFieldTypes(ip)) return; 34732 34733 if (struct_type.setFieldTypesWip(ip)) { 34734 const msg = try sema.errMsg( 34735 Type.fromInterned(ty).srcLoc(zcu), 34736 "struct '{f}' depends on itself", 34737 .{Type.fromInterned(ty).fmt(pt)}, 34738 ); 34739 return sema.failWithOwnedErrorMsg(null, msg); 34740 } 34741 defer struct_type.clearFieldTypesWip(ip); 34742 34743 sema.structFields(struct_type) catch |err| switch (err) { 34744 error.AnalysisFail, error.OutOfMemory => |e| return e, 34745 error.ComptimeBreak, error.ComptimeReturn => unreachable, 34746 }; 34747 } 34748 34749 pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void { 34750 const pt = sema.pt; 34751 const zcu = pt.zcu; 34752 const ip = &zcu.intern_pool; 34753 const struct_type = zcu.typeToStruct(ty) orelse return; 34754 34755 assert(sema.owner.unwrap().type == ty.toIntern()); 34756 34757 // Inits can start as resolved 34758 if (struct_type.haveFieldInits(ip)) return; 34759 34760 try sema.resolveStructLayout(ty); 34761 34762 if (struct_type.setInitsWip(ip)) { 34763 const msg = try sema.errMsg( 34764 ty.srcLoc(zcu), 34765 "struct '{f}' depends on itself", 34766 .{ty.fmt(pt)}, 34767 ); 34768 return sema.failWithOwnedErrorMsg(null, msg); 34769 } 34770 defer struct_type.clearInitsWip(ip); 34771 34772 sema.structFieldInits(struct_type) catch |err| switch (err) { 34773 error.AnalysisFail, error.OutOfMemory => |e| return e, 34774 error.ComptimeBreak, error.ComptimeReturn => unreachable, 34775 }; 34776 struct_type.setHaveFieldInits(ip); 34777 } 34778 34779 pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.LoadedUnionType) SemaError!void { 34780 const pt = sema.pt; 34781 const zcu = pt.zcu; 34782 const ip = &zcu.intern_pool; 34783 34784 assert(sema.owner.unwrap().type == ty.toIntern()); 34785 34786 switch (union_type.flagsUnordered(ip).status) { 34787 .none => {}, 34788 .field_types_wip => { 34789 const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)}); 34790 return sema.failWithOwnedErrorMsg(null, msg); 34791 }, 34792 .have_field_types, 34793 .have_layout, 34794 .layout_wip, 34795 .fully_resolved_wip, 34796 .fully_resolved, 34797 => return, 34798 } 34799 34800 union_type.setStatus(ip, .field_types_wip); 34801 errdefer union_type.setStatus(ip, .none); 34802 sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) { 34803 error.AnalysisFail, error.OutOfMemory => |e| return e, 34804 error.ComptimeBreak, error.ComptimeReturn => unreachable, 34805 }; 34806 union_type.setStatus(ip, .have_field_types); 34807 } 34808 34809 /// Returns a normal error set corresponding to the fully populated inferred 34810 /// error set. 34811 fn resolveInferredErrorSet( 34812 sema: *Sema, 34813 block: *Block, 34814 src: LazySrcLoc, 34815 ies_index: InternPool.Index, 34816 ) CompileError!InternPool.Index { 34817 const pt = sema.pt; 34818 const zcu = pt.zcu; 34819 const ip = &zcu.intern_pool; 34820 const func_index = ip.iesFuncIndex(ies_index); 34821 const func = zcu.funcInfo(func_index); 34822 34823 try sema.declareDependency(.{ .interned = func_index }); // resolved IES 34824 34825 try zcu.maybeUnresolveIes(func_index); 34826 const resolved_ty = func.resolvedErrorSetUnordered(ip); 34827 if (resolved_ty != .none) return resolved_ty; 34828 34829 if (zcu.analysis_in_progress.contains(AnalUnit.wrap(.{ .func = func_index }))) { 34830 return sema.fail(block, src, "unable to resolve inferred error set", .{}); 34831 } 34832 34833 // In order to ensure that all dependencies are properly added to the set, 34834 // we need to ensure the function body is analyzed of the inferred error 34835 // set. However, in the case of comptime/inline function calls with 34836 // inferred error sets, each call gets an adhoc InferredErrorSet object, which 34837 // has no corresponding function body. 34838 const ies_func_info = zcu.typeToFunc(.fromInterned(func.ty)).?; 34839 // if ies declared by a inline function with generic return type, the return_type should be generic_poison, 34840 // because inline function does not create a new declaration, and the ies has been filled with analyzeCall, 34841 // so here we can simply skip this case. 34842 if (ies_func_info.return_type == .generic_poison_type) { 34843 assert(ies_func_info.cc == .@"inline"); 34844 } else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) { 34845 if (ies_func_info.is_generic) { 34846 return sema.failWithOwnedErrorMsg(block, msg: { 34847 const msg = try sema.errMsg(src, "unable to resolve inferred error set of generic function", .{}); 34848 errdefer msg.destroy(sema.gpa); 34849 try sema.errNote(zcu.navSrcLoc(func.owner_nav), msg, "generic function declared here", .{}); 34850 break :msg msg; 34851 }); 34852 } 34853 // In this case we are dealing with the actual InferredErrorSet object that 34854 // corresponds to the function, not one created to track an inline/comptime call. 34855 try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = func_index })); 34856 try pt.ensureFuncBodyUpToDate(func_index); 34857 } 34858 34859 // This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody` 34860 // which calls `resolveInferredErrorSetPtr`. 34861 const final_resolved_ty = func.resolvedErrorSetUnordered(ip); 34862 assert(final_resolved_ty != .none); 34863 return final_resolved_ty; 34864 } 34865 34866 pub fn resolveInferredErrorSetPtr( 34867 sema: *Sema, 34868 block: *Block, 34869 src: LazySrcLoc, 34870 ies: *InferredErrorSet, 34871 ) CompileError!void { 34872 const pt = sema.pt; 34873 const ip = &pt.zcu.intern_pool; 34874 34875 if (ies.resolved != .none) return; 34876 34877 const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern()); 34878 34879 for (ies.inferred_error_sets.keys()) |other_ies_index| { 34880 if (ies_index == other_ies_index) continue; 34881 switch (try sema.resolveInferredErrorSet(block, src, other_ies_index)) { 34882 .anyerror_type => { 34883 ies.resolved = .anyerror_type; 34884 return; 34885 }, 34886 else => |error_set_ty_index| { 34887 const names = ip.indexToKey(error_set_ty_index).error_set_type.names; 34888 for (names.get(ip)) |name| { 34889 try ies.errors.put(sema.arena, name, {}); 34890 } 34891 }, 34892 } 34893 } 34894 34895 const resolved_error_set_ty = try pt.errorSetFromUnsortedNames(ies.errors.keys()); 34896 ies.resolved = resolved_error_set_ty.toIntern(); 34897 } 34898 34899 fn resolveAdHocInferredErrorSet( 34900 sema: *Sema, 34901 block: *Block, 34902 src: LazySrcLoc, 34903 value: InternPool.Index, 34904 ) CompileError!InternPool.Index { 34905 const pt = sema.pt; 34906 const zcu = pt.zcu; 34907 const gpa = sema.gpa; 34908 const ip = &zcu.intern_pool; 34909 const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value)); 34910 if (new_ty == .none) return value; 34911 return ip.getCoerced(gpa, pt.tid, value, new_ty); 34912 } 34913 34914 fn resolveAdHocInferredErrorSetTy( 34915 sema: *Sema, 34916 block: *Block, 34917 src: LazySrcLoc, 34918 ty: InternPool.Index, 34919 ) CompileError!InternPool.Index { 34920 const ies = sema.fn_ret_ty_ies orelse return .none; 34921 const pt = sema.pt; 34922 const zcu = pt.zcu; 34923 const ip = &zcu.intern_pool; 34924 const error_union_info = switch (ip.indexToKey(ty)) { 34925 .error_union_type => |x| x, 34926 else => return .none, 34927 }; 34928 if (error_union_info.error_set_type != .adhoc_inferred_error_set_type) 34929 return .none; 34930 34931 try sema.resolveInferredErrorSetPtr(block, src, ies); 34932 const new_ty = try pt.intern(.{ .error_union_type = .{ 34933 .error_set_type = ies.resolved, 34934 .payload_type = error_union_info.payload_type, 34935 } }); 34936 return new_ty; 34937 } 34938 34939 fn resolveInferredErrorSetTy( 34940 sema: *Sema, 34941 block: *Block, 34942 src: LazySrcLoc, 34943 ty: InternPool.Index, 34944 ) CompileError!InternPool.Index { 34945 const pt = sema.pt; 34946 const zcu = pt.zcu; 34947 const ip = &zcu.intern_pool; 34948 if (ty == .anyerror_type) return ty; 34949 switch (ip.indexToKey(ty)) { 34950 .error_set_type => return ty, 34951 .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty), 34952 else => unreachable, 34953 } 34954 } 34955 34956 fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct { 34957 /// fields_len 34958 usize, 34959 Zir.Inst.StructDecl.Small, 34960 /// extra_index 34961 usize, 34962 } { 34963 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; 34964 assert(extended.opcode == .struct_decl); 34965 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 34966 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; 34967 34968 const captures_len = if (small.has_captures_len) blk: { 34969 const captures_len = zir.extra[extra_index]; 34970 extra_index += 1; 34971 break :blk captures_len; 34972 } else 0; 34973 34974 const fields_len = if (small.has_fields_len) blk: { 34975 const fields_len = zir.extra[extra_index]; 34976 extra_index += 1; 34977 break :blk fields_len; 34978 } else 0; 34979 34980 const decls_len = if (small.has_decls_len) decls_len: { 34981 const decls_len = zir.extra[extra_index]; 34982 extra_index += 1; 34983 break :decls_len decls_len; 34984 } else 0; 34985 34986 extra_index += captures_len * 2; 34987 34988 // The backing integer cannot be handled until `resolveStructLayout()`. 34989 if (small.has_backing_int) { 34990 const backing_int_body_len = zir.extra[extra_index]; 34991 extra_index += 1; // backing_int_body_len 34992 if (backing_int_body_len == 0) { 34993 extra_index += 1; // backing_int_ref 34994 } else { 34995 extra_index += backing_int_body_len; // backing_int_body_inst 34996 } 34997 } 34998 34999 // Skip over decls. 35000 extra_index += decls_len; 35001 35002 return .{ fields_len, small, extra_index }; 35003 } 35004 35005 fn structFields( 35006 sema: *Sema, 35007 struct_type: InternPool.LoadedStructType, 35008 ) CompileError!void { 35009 const pt = sema.pt; 35010 const zcu = pt.zcu; 35011 const gpa = zcu.gpa; 35012 const ip = &zcu.intern_pool; 35013 const namespace_index = struct_type.namespace; 35014 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?; 35015 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; 35016 35017 const fields_len, _, var extra_index = structZirInfo(zir, zir_index); 35018 35019 if (fields_len == 0) switch (struct_type.layout) { 35020 .@"packed" => { 35021 try sema.backingIntType(struct_type); 35022 return; 35023 }, 35024 .auto, .@"extern" => { 35025 struct_type.setLayoutResolved(ip, 0, .none); 35026 return; 35027 }, 35028 }; 35029 35030 var block_scope: Block = .{ 35031 .parent = null, 35032 .sema = sema, 35033 .namespace = namespace_index, 35034 .instructions = .{}, 35035 .inlining = null, 35036 .comptime_reason = .{ .reason = .{ 35037 .src = .{ 35038 .base_node_inst = struct_type.zir_index, 35039 .offset = .nodeOffset(.zero), 35040 }, 35041 .r = .{ .simple = .struct_fields }, 35042 } }, 35043 .src_base_inst = struct_type.zir_index, 35044 .type_name_ctx = struct_type.name, 35045 }; 35046 defer assert(block_scope.instructions.items.len == 0); 35047 35048 const Field = struct { 35049 type_body_len: u32 = 0, 35050 align_body_len: u32 = 0, 35051 init_body_len: u32 = 0, 35052 type_ref: Zir.Inst.Ref = .none, 35053 }; 35054 const fields = try sema.arena.alloc(Field, fields_len); 35055 35056 var any_inits = false; 35057 var any_aligned = false; 35058 35059 { 35060 const bits_per_field = 4; 35061 const fields_per_u32 = 32 / bits_per_field; 35062 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 35063 const flags_index = extra_index; 35064 var bit_bag_index: usize = flags_index; 35065 extra_index += bit_bags_count; 35066 var cur_bit_bag: u32 = undefined; 35067 var field_i: u32 = 0; 35068 while (field_i < fields_len) : (field_i += 1) { 35069 if (field_i % fields_per_u32 == 0) { 35070 cur_bit_bag = zir.extra[bit_bag_index]; 35071 bit_bag_index += 1; 35072 } 35073 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 35074 cur_bit_bag >>= 1; 35075 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; 35076 cur_bit_bag >>= 1; 35077 const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0; 35078 cur_bit_bag >>= 1; 35079 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; 35080 cur_bit_bag >>= 1; 35081 35082 if (is_comptime) struct_type.setFieldComptime(ip, field_i); 35083 35084 const field_name_zir: [:0]const u8 = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index])); 35085 extra_index += 1; // field_name 35086 35087 fields[field_i] = .{}; 35088 35089 if (has_type_body) { 35090 fields[field_i].type_body_len = zir.extra[extra_index]; 35091 } else { 35092 fields[field_i].type_ref = @enumFromInt(zir.extra[extra_index]); 35093 } 35094 extra_index += 1; 35095 35096 // This string needs to outlive the ZIR code. 35097 const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls); 35098 assert(struct_type.addFieldName(ip, field_name) == null); 35099 35100 if (has_align) { 35101 fields[field_i].align_body_len = zir.extra[extra_index]; 35102 extra_index += 1; 35103 any_aligned = true; 35104 } 35105 if (has_init) { 35106 fields[field_i].init_body_len = zir.extra[extra_index]; 35107 extra_index += 1; 35108 any_inits = true; 35109 } 35110 } 35111 } 35112 35113 // Next we do only types and alignments, saving the inits for a second pass, 35114 // so that init values may depend on type layout. 35115 35116 for (fields, 0..) |zir_field, field_i| { 35117 const ty_src: LazySrcLoc = .{ 35118 .base_node_inst = struct_type.zir_index, 35119 .offset = .{ .container_field_type = @intCast(field_i) }, 35120 }; 35121 const field_ty: Type = ty: { 35122 if (zir_field.type_ref != .none) { 35123 break :ty try sema.resolveType(&block_scope, ty_src, zir_field.type_ref); 35124 } 35125 assert(zir_field.type_body_len != 0); 35126 const body = zir.bodySlice(extra_index, zir_field.type_body_len); 35127 extra_index += body.len; 35128 const ty_ref = try sema.resolveInlineBody(&block_scope, body, zir_index); 35129 break :ty try sema.analyzeAsType(&block_scope, ty_src, ty_ref); 35130 }; 35131 35132 struct_type.field_types.get(ip)[field_i] = field_ty.toIntern(); 35133 35134 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 35135 const msg = msg: { 35136 const msg = try sema.errMsg(ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 35137 errdefer msg.destroy(sema.gpa); 35138 35139 try sema.addDeclaredHereNote(msg, field_ty); 35140 break :msg msg; 35141 }; 35142 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35143 } 35144 if (field_ty.zigTypeTag(zcu) == .noreturn) { 35145 const msg = msg: { 35146 const msg = try sema.errMsg(ty_src, "struct fields cannot be 'noreturn'", .{}); 35147 errdefer msg.destroy(sema.gpa); 35148 35149 try sema.addDeclaredHereNote(msg, field_ty); 35150 break :msg msg; 35151 }; 35152 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35153 } 35154 switch (struct_type.layout) { 35155 .@"extern" => if (!try sema.validateExternType(field_ty, .struct_field)) { 35156 const msg = msg: { 35157 const msg = try sema.errMsg(ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 35158 errdefer msg.destroy(sema.gpa); 35159 35160 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .struct_field); 35161 35162 try sema.addDeclaredHereNote(msg, field_ty); 35163 break :msg msg; 35164 }; 35165 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35166 }, 35167 .@"packed" => if (!try sema.validatePackedType(field_ty)) { 35168 const msg = msg: { 35169 const msg = try sema.errMsg(ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 35170 errdefer msg.destroy(sema.gpa); 35171 35172 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty); 35173 35174 try sema.addDeclaredHereNote(msg, field_ty); 35175 break :msg msg; 35176 }; 35177 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35178 }, 35179 else => {}, 35180 } 35181 35182 if (zir_field.align_body_len > 0) { 35183 const body = zir.bodySlice(extra_index, zir_field.align_body_len); 35184 extra_index += body.len; 35185 const align_ref = try sema.resolveInlineBody(&block_scope, body, zir_index); 35186 const align_src: LazySrcLoc = .{ 35187 .base_node_inst = struct_type.zir_index, 35188 .offset = .{ .container_field_align = @intCast(field_i) }, 35189 }; 35190 const field_align = try sema.analyzeAsAlign(&block_scope, align_src, align_ref); 35191 struct_type.field_aligns.get(ip)[field_i] = field_align; 35192 } 35193 35194 extra_index += zir_field.init_body_len; 35195 } 35196 35197 struct_type.clearFieldTypesWip(ip); 35198 if (!any_inits) struct_type.setHaveFieldInits(ip); 35199 35200 try sema.flushExports(); 35201 } 35202 35203 // This logic must be kept in sync with `structFields` 35204 fn structFieldInits( 35205 sema: *Sema, 35206 struct_type: InternPool.LoadedStructType, 35207 ) CompileError!void { 35208 const pt = sema.pt; 35209 const zcu = pt.zcu; 35210 const ip = &zcu.intern_pool; 35211 35212 assert(!struct_type.haveFieldInits(ip)); 35213 35214 const namespace_index = struct_type.namespace; 35215 const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?; 35216 const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; 35217 const fields_len, _, var extra_index = structZirInfo(zir, zir_index); 35218 35219 var block_scope: Block = .{ 35220 .parent = null, 35221 .sema = sema, 35222 .namespace = namespace_index, 35223 .instructions = .{}, 35224 .inlining = null, 35225 .comptime_reason = undefined, // set when `block_scope` is used 35226 .src_base_inst = struct_type.zir_index, 35227 .type_name_ctx = struct_type.name, 35228 }; 35229 defer assert(block_scope.instructions.items.len == 0); 35230 35231 const Field = struct { 35232 type_body_len: u32 = 0, 35233 align_body_len: u32 = 0, 35234 init_body_len: u32 = 0, 35235 }; 35236 const fields = try sema.arena.alloc(Field, fields_len); 35237 35238 var any_inits = false; 35239 35240 { 35241 const bits_per_field = 4; 35242 const fields_per_u32 = 32 / bits_per_field; 35243 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 35244 const flags_index = extra_index; 35245 var bit_bag_index: usize = flags_index; 35246 extra_index += bit_bags_count; 35247 var cur_bit_bag: u32 = undefined; 35248 var field_i: u32 = 0; 35249 while (field_i < fields_len) : (field_i += 1) { 35250 if (field_i % fields_per_u32 == 0) { 35251 cur_bit_bag = zir.extra[bit_bag_index]; 35252 bit_bag_index += 1; 35253 } 35254 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 35255 cur_bit_bag >>= 1; 35256 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; 35257 cur_bit_bag >>= 2; 35258 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; 35259 cur_bit_bag >>= 1; 35260 35261 extra_index += 1; // field_name 35262 35263 fields[field_i] = .{}; 35264 35265 if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index]; 35266 extra_index += 1; 35267 35268 if (has_align) { 35269 fields[field_i].align_body_len = zir.extra[extra_index]; 35270 extra_index += 1; 35271 } 35272 if (has_init) { 35273 fields[field_i].init_body_len = zir.extra[extra_index]; 35274 extra_index += 1; 35275 any_inits = true; 35276 } 35277 } 35278 } 35279 35280 if (any_inits) { 35281 for (fields, 0..) |zir_field, field_i| { 35282 extra_index += zir_field.type_body_len; 35283 extra_index += zir_field.align_body_len; 35284 const body = zir.bodySlice(extra_index, zir_field.init_body_len); 35285 extra_index += zir_field.init_body_len; 35286 35287 if (body.len == 0) continue; 35288 35289 // Pre-populate the type mapping the body expects to be there. 35290 // In init bodies, the zir index of the struct itself is used 35291 // to refer to the current field type. 35292 35293 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_i]); 35294 const type_ref = Air.internedToRef(field_ty.toIntern()); 35295 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index}); 35296 sema.inst_map.putAssumeCapacity(zir_index, type_ref); 35297 35298 const init_src: LazySrcLoc = .{ 35299 .base_node_inst = struct_type.zir_index, 35300 .offset = .{ .container_field_value = @intCast(field_i) }, 35301 }; 35302 35303 block_scope.comptime_reason = .{ .reason = .{ 35304 .src = init_src, 35305 .r = .{ .simple = .struct_field_default_value }, 35306 } }; 35307 const init = try sema.resolveInlineBody(&block_scope, body, zir_index); 35308 const coerced = try sema.coerce(&block_scope, field_ty, init, init_src); 35309 const default_val = try sema.resolveConstValue(&block_scope, init_src, coerced, null); 35310 35311 if (default_val.canMutateComptimeVarState(zcu)) { 35312 const field_name = struct_type.fieldName(ip, field_i).unwrap().?; 35313 return sema.failWithContainsReferenceToComptimeVar(&block_scope, init_src, field_name, "field default value", default_val); 35314 } 35315 struct_type.field_inits.get(ip)[field_i] = default_val.toIntern(); 35316 } 35317 } 35318 35319 try sema.flushExports(); 35320 } 35321 35322 fn unionFields( 35323 sema: *Sema, 35324 union_ty: InternPool.Index, 35325 union_type: InternPool.LoadedUnionType, 35326 ) CompileError!void { 35327 const tracy = trace(@src()); 35328 defer tracy.end(); 35329 35330 const pt = sema.pt; 35331 const zcu = pt.zcu; 35332 const gpa = zcu.gpa; 35333 const ip = &zcu.intern_pool; 35334 const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?; 35335 const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail; 35336 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; 35337 assert(extended.opcode == .union_decl); 35338 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); 35339 const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); 35340 var extra_index: usize = extra.end; 35341 35342 const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { 35343 const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35344 extra_index += 1; 35345 break :blk ty_ref; 35346 } else .none; 35347 35348 const captures_len = if (small.has_captures_len) blk: { 35349 const captures_len = zir.extra[extra_index]; 35350 extra_index += 1; 35351 break :blk captures_len; 35352 } else 0; 35353 35354 const body_len = if (small.has_body_len) blk: { 35355 const body_len = zir.extra[extra_index]; 35356 extra_index += 1; 35357 break :blk body_len; 35358 } else 0; 35359 35360 const fields_len = if (small.has_fields_len) blk: { 35361 const fields_len = zir.extra[extra_index]; 35362 extra_index += 1; 35363 break :blk fields_len; 35364 } else 0; 35365 35366 const decls_len = if (small.has_decls_len) decls_len: { 35367 const decls_len = zir.extra[extra_index]; 35368 extra_index += 1; 35369 break :decls_len decls_len; 35370 } else 0; 35371 35372 // Skip over captures and decls. 35373 extra_index += captures_len * 2 + decls_len; 35374 35375 const body = zir.bodySlice(extra_index, body_len); 35376 extra_index += body.len; 35377 35378 const src: LazySrcLoc = .{ 35379 .base_node_inst = union_type.zir_index, 35380 .offset = .nodeOffset(.zero), 35381 }; 35382 35383 var block_scope: Block = .{ 35384 .parent = null, 35385 .sema = sema, 35386 .namespace = union_type.namespace, 35387 .instructions = .{}, 35388 .inlining = null, 35389 .comptime_reason = .{ .reason = .{ 35390 .src = src, 35391 .r = .{ .simple = .union_fields }, 35392 } }, 35393 .src_base_inst = union_type.zir_index, 35394 .type_name_ctx = union_type.name, 35395 }; 35396 defer assert(block_scope.instructions.items.len == 0); 35397 35398 if (body.len != 0) { 35399 _ = try sema.analyzeInlineBody(&block_scope, body, zir_index); 35400 } 35401 35402 var int_tag_ty: Type = undefined; 35403 var enum_field_names: []InternPool.NullTerminatedString = &.{}; 35404 var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty; 35405 var explicit_tags_seen: []bool = &.{}; 35406 if (tag_type_ref != .none) { 35407 const tag_ty_src: LazySrcLoc = .{ 35408 .base_node_inst = union_type.zir_index, 35409 .offset = .{ .node_offset_container_tag = .zero }, 35410 }; 35411 const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); 35412 if (small.auto_enum_tag) { 35413 // The provided type is an integer type and we must construct the enum tag type here. 35414 int_tag_ty = provided_ty; 35415 if (int_tag_ty.zigTypeTag(zcu) != .int and int_tag_ty.zigTypeTag(zcu) != .comptime_int) { 35416 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{f}'", .{int_tag_ty.fmt(pt)}); 35417 } 35418 35419 if (fields_len > 0) { 35420 const field_count_val = try pt.intValue(.comptime_int, fields_len - 1); 35421 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) { 35422 const msg = msg: { 35423 const msg = try sema.errMsg(tag_ty_src, "specified integer tag type cannot represent every field", .{}); 35424 errdefer msg.destroy(sema.gpa); 35425 try sema.errNote(tag_ty_src, msg, "type '{f}' cannot fit values in range 0...{d}", .{ 35426 int_tag_ty.fmt(pt), 35427 fields_len - 1, 35428 }); 35429 break :msg msg; 35430 }; 35431 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35432 } 35433 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 35434 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len); 35435 } 35436 } else { 35437 // The provided type is the enum tag type. 35438 const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) { 35439 .enum_type => ip.loadEnumType(provided_ty.toIntern()), 35440 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{f}'", .{provided_ty.fmt(pt)}), 35441 }; 35442 union_type.setTagType(ip, provided_ty.toIntern()); 35443 // The fields of the union must match the enum exactly. 35444 // A flag per field is used to check for missing and extraneous fields. 35445 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); 35446 @memset(explicit_tags_seen, false); 35447 } 35448 } else { 35449 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis 35450 // purposes, we still auto-generate an enum tag type the same way. That the union is 35451 // untagged is represented by the Type tag (union vs union_tagged). 35452 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 35453 } 35454 35455 var field_types: std.ArrayListUnmanaged(InternPool.Index) = .empty; 35456 var field_aligns: std.ArrayListUnmanaged(InternPool.Alignment) = .empty; 35457 35458 try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len); 35459 if (small.any_aligned_fields) 35460 try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len); 35461 35462 const bits_per_field = 4; 35463 const fields_per_u32 = 32 / bits_per_field; 35464 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 35465 var bit_bag_index: usize = extra_index; 35466 extra_index += bit_bags_count; 35467 var cur_bit_bag: u32 = undefined; 35468 var field_i: u32 = 0; 35469 var last_tag_val: ?Value = null; 35470 while (field_i < fields_len) : (field_i += 1) { 35471 if (field_i % fields_per_u32 == 0) { 35472 cur_bit_bag = zir.extra[bit_bag_index]; 35473 bit_bag_index += 1; 35474 } 35475 const has_type = @as(u1, @truncate(cur_bit_bag)) != 0; 35476 cur_bit_bag >>= 1; 35477 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 35478 cur_bit_bag >>= 1; 35479 const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0; 35480 cur_bit_bag >>= 1; 35481 const unused = @as(u1, @truncate(cur_bit_bag)) != 0; 35482 cur_bit_bag >>= 1; 35483 _ = unused; 35484 35485 const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]); 35486 const field_name_zir = zir.nullTerminatedString(field_name_index); 35487 extra_index += 1; 35488 35489 const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { 35490 const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35491 extra_index += 1; 35492 break :blk field_type_ref; 35493 } else .none; 35494 35495 const align_ref: Zir.Inst.Ref = if (has_align) blk: { 35496 const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35497 extra_index += 1; 35498 break :blk align_ref; 35499 } else .none; 35500 35501 const tag_ref: Air.Inst.Ref = if (has_tag) blk: { 35502 const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35503 extra_index += 1; 35504 break :blk try sema.resolveInst(tag_ref); 35505 } else .none; 35506 35507 const name_src: LazySrcLoc = .{ 35508 .base_node_inst = union_type.zir_index, 35509 .offset = .{ .container_field_name = field_i }, 35510 }; 35511 const value_src: LazySrcLoc = .{ 35512 .base_node_inst = union_type.zir_index, 35513 .offset = .{ .container_field_value = field_i }, 35514 }; 35515 const align_src: LazySrcLoc = .{ 35516 .base_node_inst = union_type.zir_index, 35517 .offset = .{ .container_field_align = field_i }, 35518 }; 35519 const type_src: LazySrcLoc = .{ 35520 .base_node_inst = union_type.zir_index, 35521 .offset = .{ .container_field_type = field_i }, 35522 }; 35523 35524 if (enum_field_vals.capacity() > 0) { 35525 const enum_tag_val = if (tag_ref != .none) blk: { 35526 const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, value_src); 35527 const val = try sema.resolveConstDefinedValue(&block_scope, value_src, coerced, .{ .simple = .enum_field_tag_value }); 35528 last_tag_val = val; 35529 35530 break :blk val; 35531 } else blk: { 35532 if (last_tag_val) |last_tag| { 35533 const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag); 35534 if (result.overflow) return sema.fail( 35535 &block_scope, 35536 value_src, 35537 "enumeration value '{f}' too large for type '{f}'", 35538 .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) }, 35539 ); 35540 last_tag_val = result.val; 35541 } else { 35542 last_tag_val = try pt.intValue(int_tag_ty, 0); 35543 } 35544 break :blk last_tag_val.?; 35545 }; 35546 const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern()); 35547 if (gop.found_existing) { 35548 const other_value_src: LazySrcLoc = .{ 35549 .base_node_inst = union_type.zir_index, 35550 .offset = .{ .container_field_value = @intCast(gop.index) }, 35551 }; 35552 const msg = msg: { 35553 const msg = try sema.errMsg( 35554 value_src, 35555 "enum tag value {f} already taken", 35556 .{enum_tag_val.fmtValueSema(pt, sema)}, 35557 ); 35558 errdefer msg.destroy(gpa); 35559 try sema.errNote(other_value_src, msg, "other occurrence here", .{}); 35560 break :msg msg; 35561 }; 35562 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35563 } 35564 } 35565 35566 // This string needs to outlive the ZIR code. 35567 const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls); 35568 if (enum_field_names.len != 0) { 35569 enum_field_names[field_i] = field_name; 35570 } 35571 35572 const field_ty: Type = if (!has_type) 35573 .void 35574 else if (field_type_ref == .none) 35575 .noreturn 35576 else 35577 try sema.resolveType(&block_scope, type_src, field_type_ref); 35578 35579 if (explicit_tags_seen.len > 0) { 35580 const tag_ty = union_type.tagTypeUnordered(ip); 35581 const tag_info = ip.loadEnumType(tag_ty); 35582 const enum_index = tag_info.nameIndex(ip, field_name) orelse { 35583 return sema.fail(&block_scope, name_src, "no field named '{f}' in enum '{f}'", .{ 35584 field_name.fmt(ip), Type.fromInterned(tag_ty).fmt(pt), 35585 }); 35586 }; 35587 35588 // No check for duplicate because the check already happened in order 35589 // to create the enum type in the first place. 35590 assert(!explicit_tags_seen[enum_index]); 35591 explicit_tags_seen[enum_index] = true; 35592 35593 // Enforce the enum fields and the union fields being in the same order. 35594 if (enum_index != field_i) { 35595 const msg = msg: { 35596 const enum_field_src: LazySrcLoc = .{ 35597 .base_node_inst = Type.fromInterned(tag_ty).typeDeclInstAllowGeneratedTag(zcu).?, 35598 .offset = .{ .container_field_name = enum_index }, 35599 }; 35600 const msg = try sema.errMsg(name_src, "union field '{f}' ordered differently than corresponding enum field", .{ 35601 field_name.fmt(ip), 35602 }); 35603 errdefer msg.destroy(sema.gpa); 35604 try sema.errNote(enum_field_src, msg, "enum field here", .{}); 35605 break :msg msg; 35606 }; 35607 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35608 } 35609 } 35610 35611 if (field_ty.zigTypeTag(zcu) == .@"opaque") { 35612 const msg = msg: { 35613 const msg = try sema.errMsg(type_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 35614 errdefer msg.destroy(sema.gpa); 35615 35616 try sema.addDeclaredHereNote(msg, field_ty); 35617 break :msg msg; 35618 }; 35619 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35620 } 35621 const layout = union_type.flagsUnordered(ip).layout; 35622 if (layout == .@"extern" and 35623 !try sema.validateExternType(field_ty, .union_field)) 35624 { 35625 const msg = msg: { 35626 const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 35627 errdefer msg.destroy(sema.gpa); 35628 35629 try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field); 35630 35631 try sema.addDeclaredHereNote(msg, field_ty); 35632 break :msg msg; 35633 }; 35634 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35635 } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { 35636 const msg = msg: { 35637 const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); 35638 errdefer msg.destroy(sema.gpa); 35639 35640 try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty); 35641 35642 try sema.addDeclaredHereNote(msg, field_ty); 35643 break :msg msg; 35644 }; 35645 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35646 } 35647 35648 field_types.appendAssumeCapacity(field_ty.toIntern()); 35649 35650 if (small.any_aligned_fields) { 35651 field_aligns.appendAssumeCapacity(if (align_ref != .none) 35652 try sema.resolveAlign(&block_scope, align_src, align_ref) 35653 else 35654 .none); 35655 } else { 35656 assert(align_ref == .none); 35657 } 35658 } 35659 35660 union_type.setFieldTypes(ip, field_types.items); 35661 union_type.setFieldAligns(ip, field_aligns.items); 35662 35663 if (explicit_tags_seen.len > 0) { 35664 const tag_ty = union_type.tagTypeUnordered(ip); 35665 const tag_info = ip.loadEnumType(tag_ty); 35666 if (tag_info.names.len > fields_len) { 35667 const msg = msg: { 35668 const msg = try sema.errMsg(src, "enum field(s) missing in union", .{}); 35669 errdefer msg.destroy(sema.gpa); 35670 35671 for (tag_info.names.get(ip), 0..) |field_name, field_index| { 35672 if (explicit_tags_seen[field_index]) continue; 35673 try sema.addFieldErrNote(.fromInterned(tag_ty), field_index, msg, "field '{f}' missing, declared here", .{ 35674 field_name.fmt(ip), 35675 }); 35676 } 35677 try sema.addDeclaredHereNote(msg, .fromInterned(tag_ty)); 35678 break :msg msg; 35679 }; 35680 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35681 } 35682 } else if (enum_field_vals.count() > 0) { 35683 const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_ty, union_type.name); 35684 union_type.setTagType(ip, enum_ty); 35685 } else { 35686 const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_ty, union_type.name); 35687 union_type.setTagType(ip, enum_ty); 35688 } 35689 35690 try sema.flushExports(); 35691 } 35692 35693 fn generateUnionTagTypeNumbered( 35694 sema: *Sema, 35695 block: *Block, 35696 enum_field_names: []const InternPool.NullTerminatedString, 35697 enum_field_vals: []const InternPool.Index, 35698 union_type: InternPool.Index, 35699 union_name: InternPool.NullTerminatedString, 35700 ) !InternPool.Index { 35701 const pt = sema.pt; 35702 const zcu = pt.zcu; 35703 const gpa = sema.gpa; 35704 const ip = &zcu.intern_pool; 35705 35706 const name = try ip.getOrPutStringFmt( 35707 gpa, 35708 pt.tid, 35709 "@typeInfo({f}).@\"union\".tag_type.?", 35710 .{union_name.fmt(ip)}, 35711 .no_embedded_nulls, 35712 ); 35713 35714 const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{ 35715 .name = name, 35716 .owner_union_ty = union_type, 35717 .tag_ty = if (enum_field_vals.len == 0) 35718 (try pt.intType(.unsigned, 0)).toIntern() 35719 else 35720 ip.typeOf(enum_field_vals[0]), 35721 .names = enum_field_names, 35722 .values = enum_field_vals, 35723 .tag_mode = .explicit, 35724 .parent_namespace = block.namespace, 35725 }); 35726 35727 return enum_ty; 35728 } 35729 35730 fn generateUnionTagTypeSimple( 35731 sema: *Sema, 35732 block: *Block, 35733 enum_field_names: []const InternPool.NullTerminatedString, 35734 union_type: InternPool.Index, 35735 union_name: InternPool.NullTerminatedString, 35736 ) !InternPool.Index { 35737 const pt = sema.pt; 35738 const zcu = pt.zcu; 35739 const ip = &zcu.intern_pool; 35740 const gpa = sema.gpa; 35741 35742 const name = try ip.getOrPutStringFmt( 35743 gpa, 35744 pt.tid, 35745 "@typeInfo({f}).@\"union\".tag_type.?", 35746 .{union_name.fmt(ip)}, 35747 .no_embedded_nulls, 35748 ); 35749 35750 const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{ 35751 .name = name, 35752 .owner_union_ty = union_type, 35753 .tag_ty = (try pt.smallestUnsignedInt(enum_field_names.len -| 1)).toIntern(), 35754 .names = enum_field_names, 35755 .values = &.{}, 35756 .tag_mode = .auto, 35757 .parent_namespace = block.namespace, 35758 }); 35759 35760 return enum_ty; 35761 } 35762 35763 /// There is another implementation of this in `Type.onePossibleValue`. This one 35764 /// in `Sema` is for calling during semantic analysis, and performs field resolution 35765 /// to get the answer. The one in `Type` is for calling during codegen and asserts 35766 /// that the types are already resolved. 35767 /// TODO assert the return value matches `ty.onePossibleValue` 35768 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { 35769 const pt = sema.pt; 35770 const zcu = pt.zcu; 35771 const ip = &zcu.intern_pool; 35772 return switch (ty.toIntern()) { 35773 .u0_type, 35774 .i0_type, 35775 => try pt.intValue(ty, 0), 35776 .u1_type, 35777 .u8_type, 35778 .i8_type, 35779 .u16_type, 35780 .i16_type, 35781 .u29_type, 35782 .u32_type, 35783 .i32_type, 35784 .u64_type, 35785 .i64_type, 35786 .u80_type, 35787 .u128_type, 35788 .i128_type, 35789 .u256_type, 35790 .usize_type, 35791 .isize_type, 35792 .c_char_type, 35793 .c_short_type, 35794 .c_ushort_type, 35795 .c_int_type, 35796 .c_uint_type, 35797 .c_long_type, 35798 .c_ulong_type, 35799 .c_longlong_type, 35800 .c_ulonglong_type, 35801 .c_longdouble_type, 35802 .f16_type, 35803 .f32_type, 35804 .f64_type, 35805 .f80_type, 35806 .f128_type, 35807 .anyopaque_type, 35808 .bool_type, 35809 .type_type, 35810 .anyerror_type, 35811 .adhoc_inferred_error_set_type, 35812 .comptime_int_type, 35813 .comptime_float_type, 35814 .enum_literal_type, 35815 .ptr_usize_type, 35816 .ptr_const_comptime_int_type, 35817 .manyptr_u8_type, 35818 .manyptr_const_u8_type, 35819 .manyptr_const_u8_sentinel_0_type, 35820 .slice_const_u8_type, 35821 .slice_const_u8_sentinel_0_type, 35822 .vector_8_i8_type, 35823 .vector_16_i8_type, 35824 .vector_32_i8_type, 35825 .vector_64_i8_type, 35826 .vector_1_u8_type, 35827 .vector_2_u8_type, 35828 .vector_4_u8_type, 35829 .vector_8_u8_type, 35830 .vector_16_u8_type, 35831 .vector_32_u8_type, 35832 .vector_64_u8_type, 35833 .vector_2_i16_type, 35834 .vector_4_i16_type, 35835 .vector_8_i16_type, 35836 .vector_16_i16_type, 35837 .vector_32_i16_type, 35838 .vector_4_u16_type, 35839 .vector_8_u16_type, 35840 .vector_16_u16_type, 35841 .vector_32_u16_type, 35842 .vector_2_i32_type, 35843 .vector_4_i32_type, 35844 .vector_8_i32_type, 35845 .vector_16_i32_type, 35846 .vector_4_u32_type, 35847 .vector_8_u32_type, 35848 .vector_16_u32_type, 35849 .vector_2_i64_type, 35850 .vector_4_i64_type, 35851 .vector_8_i64_type, 35852 .vector_2_u64_type, 35853 .vector_4_u64_type, 35854 .vector_8_u64_type, 35855 .vector_1_u128_type, 35856 .vector_2_u128_type, 35857 .vector_1_u256_type, 35858 .vector_4_f16_type, 35859 .vector_8_f16_type, 35860 .vector_16_f16_type, 35861 .vector_32_f16_type, 35862 .vector_2_f32_type, 35863 .vector_4_f32_type, 35864 .vector_8_f32_type, 35865 .vector_16_f32_type, 35866 .vector_2_f64_type, 35867 .vector_4_f64_type, 35868 .vector_8_f64_type, 35869 .anyerror_void_error_union_type, 35870 => null, 35871 .void_type => Value.void, 35872 .noreturn_type => Value.@"unreachable", 35873 .anyframe_type => unreachable, 35874 .null_type => Value.null, 35875 .undefined_type => Value.undef, 35876 .optional_noreturn_type => try pt.nullValue(ty), 35877 .generic_poison_type => unreachable, 35878 .empty_tuple_type => Value.empty_tuple, 35879 // values, not types 35880 .undef, 35881 .undef_bool, 35882 .undef_usize, 35883 .undef_u1, 35884 .zero, 35885 .zero_usize, 35886 .zero_u1, 35887 .zero_u8, 35888 .one, 35889 .one_usize, 35890 .one_u1, 35891 .one_u8, 35892 .four_u8, 35893 .negative_one, 35894 .void_value, 35895 .unreachable_value, 35896 .null_value, 35897 .bool_true, 35898 .bool_false, 35899 .empty_tuple, 35900 // invalid 35901 .none, 35902 => unreachable, 35903 35904 _ => switch (ty.toIntern().unwrap(ip).getTag(ip)) { 35905 .removed => unreachable, 35906 35907 .type_int_signed, // i0 handled above 35908 .type_int_unsigned, // u0 handled above 35909 .type_pointer, 35910 .type_slice, 35911 .type_anyframe, 35912 .type_error_union, 35913 .type_anyerror_union, 35914 .type_error_set, 35915 .type_inferred_error_set, 35916 .type_opaque, 35917 .type_function, 35918 => null, 35919 35920 .simple_type, // handled above 35921 // values, not types 35922 .undef, 35923 .simple_value, 35924 .ptr_nav, 35925 .ptr_uav, 35926 .ptr_uav_aligned, 35927 .ptr_comptime_alloc, 35928 .ptr_comptime_field, 35929 .ptr_int, 35930 .ptr_eu_payload, 35931 .ptr_opt_payload, 35932 .ptr_elem, 35933 .ptr_field, 35934 .ptr_slice, 35935 .opt_payload, 35936 .opt_null, 35937 .int_u8, 35938 .int_u16, 35939 .int_u32, 35940 .int_i32, 35941 .int_usize, 35942 .int_comptime_int_u32, 35943 .int_comptime_int_i32, 35944 .int_small, 35945 .int_positive, 35946 .int_negative, 35947 .int_lazy_align, 35948 .int_lazy_size, 35949 .error_set_error, 35950 .error_union_error, 35951 .error_union_payload, 35952 .enum_literal, 35953 .enum_tag, 35954 .float_f16, 35955 .float_f32, 35956 .float_f64, 35957 .float_f80, 35958 .float_f128, 35959 .float_c_longdouble_f80, 35960 .float_c_longdouble_f128, 35961 .float_comptime_float, 35962 .variable, 35963 .threadlocal_variable, 35964 .@"extern", 35965 .func_decl, 35966 .func_instance, 35967 .func_coerced, 35968 .only_possible_value, 35969 .union_value, 35970 .bytes, 35971 .aggregate, 35972 .repeated, 35973 // memoized value, not types 35974 .memoized_call, 35975 => unreachable, 35976 35977 .type_array_big, 35978 .type_array_small, 35979 .type_vector, 35980 .type_enum_auto, 35981 .type_enum_explicit, 35982 .type_enum_nonexhaustive, 35983 .type_struct, 35984 .type_struct_packed, 35985 .type_struct_packed_inits, 35986 .type_tuple, 35987 .type_union, 35988 => switch (ip.indexToKey(ty.toIntern())) { 35989 inline .array_type, .vector_type => |seq_type, seq_tag| { 35990 const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; 35991 if (seq_type.len + @intFromBool(has_sentinel) == 0) return Value.fromInterned(try pt.intern(.{ .aggregate = .{ 35992 .ty = ty.toIntern(), 35993 .storage = .{ .elems = &.{} }, 35994 } })); 35995 35996 if (try sema.typeHasOnePossibleValue(.fromInterned(seq_type.child))) |opv| { 35997 return Value.fromInterned(try pt.intern(.{ .aggregate = .{ 35998 .ty = ty.toIntern(), 35999 .storage = .{ .repeated_elem = opv.toIntern() }, 36000 } })); 36001 } 36002 return null; 36003 }, 36004 36005 .struct_type => { 36006 // Resolving the layout first helps to avoid loops. 36007 // If the type has a coherent layout, we can recurse through fields safely. 36008 try ty.resolveLayout(pt); 36009 36010 const struct_type = ip.loadStructType(ty.toIntern()); 36011 36012 if (struct_type.field_types.len == 0) { 36013 // In this case the struct has no fields at all and 36014 // therefore has one possible value. 36015 return Value.fromInterned(try pt.intern(.{ .aggregate = .{ 36016 .ty = ty.toIntern(), 36017 .storage = .{ .elems = &.{} }, 36018 } })); 36019 } 36020 36021 const field_vals = try sema.arena.alloc( 36022 InternPool.Index, 36023 struct_type.field_types.len, 36024 ); 36025 for (field_vals, 0..) |*field_val, i| { 36026 if (struct_type.fieldIsComptime(ip, i)) { 36027 try ty.resolveStructFieldInits(pt); 36028 field_val.* = struct_type.field_inits.get(ip)[i]; 36029 continue; 36030 } 36031 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); 36032 if (try sema.typeHasOnePossibleValue(field_ty)) |field_opv| { 36033 field_val.* = field_opv.toIntern(); 36034 } else return null; 36035 } 36036 36037 // In this case the struct has no runtime-known fields and 36038 // therefore has one possible value. 36039 return Value.fromInterned(try pt.intern(.{ .aggregate = .{ 36040 .ty = ty.toIntern(), 36041 .storage = .{ .elems = field_vals }, 36042 } })); 36043 }, 36044 36045 .tuple_type => |tuple| { 36046 for (tuple.values.get(ip)) |val| { 36047 if (val == .none) return null; 36048 } 36049 // In this case the struct has all comptime-known fields and 36050 // therefore has one possible value. 36051 // TODO: write something like getCoercedInts to avoid needing to dupe 36052 return Value.fromInterned(try pt.intern(.{ .aggregate = .{ 36053 .ty = ty.toIntern(), 36054 .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values.get(ip)) }, 36055 } })); 36056 }, 36057 36058 .union_type => { 36059 // Resolving the layout first helps to avoid loops. 36060 // If the type has a coherent layout, we can recurse through fields safely. 36061 try ty.resolveLayout(pt); 36062 36063 const union_obj = ip.loadUnionType(ty.toIntern()); 36064 const tag_val = (try sema.typeHasOnePossibleValue(.fromInterned(union_obj.tagTypeUnordered(ip)))) orelse 36065 return null; 36066 if (union_obj.field_types.len == 0) { 36067 const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() }); 36068 return Value.fromInterned(only); 36069 } 36070 const only_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[0]); 36071 const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse 36072 return null; 36073 const only = try pt.internUnion(.{ 36074 .ty = ty.toIntern(), 36075 .tag = tag_val.toIntern(), 36076 .val = val_val.toIntern(), 36077 }); 36078 return Value.fromInterned(only); 36079 }, 36080 36081 .enum_type => { 36082 const enum_type = ip.loadEnumType(ty.toIntern()); 36083 switch (enum_type.tag_mode) { 36084 .nonexhaustive => { 36085 if (enum_type.tag_ty == .comptime_int_type) return null; 36086 36087 if (try sema.typeHasOnePossibleValue(.fromInterned(enum_type.tag_ty))) |int_opv| { 36088 const only = try pt.intern(.{ .enum_tag = .{ 36089 .ty = ty.toIntern(), 36090 .int = int_opv.toIntern(), 36091 } }); 36092 return Value.fromInterned(only); 36093 } 36094 36095 return null; 36096 }, 36097 .auto, .explicit => { 36098 if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null; 36099 36100 return Value.fromInterned(switch (enum_type.names.len) { 36101 0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }), 36102 1 => try pt.intern(.{ .enum_tag = .{ 36103 .ty = ty.toIntern(), 36104 .int = if (enum_type.values.len == 0) 36105 (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern() 36106 else 36107 try ip.getCoercedInts( 36108 zcu.gpa, 36109 pt.tid, 36110 ip.indexToKey(enum_type.values.get(ip)[0]).int, 36111 enum_type.tag_ty, 36112 ), 36113 } }), 36114 else => return null, 36115 }); 36116 }, 36117 } 36118 }, 36119 36120 else => unreachable, 36121 }, 36122 36123 .type_optional => { 36124 const payload_ip = ip.indexToKey(ty.toIntern()).opt_type; 36125 // Although ?noreturn is handled above, the element type 36126 // can be effectively noreturn for example via an empty 36127 // enum or error set. 36128 if (ip.isNoReturn(payload_ip)) return try pt.nullValue(ty); 36129 return null; 36130 }, 36131 }, 36132 }; 36133 } 36134 36135 /// Returns the type of the AIR instruction. 36136 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type { 36137 return sema.getTmpAir().typeOf(inst, &sema.pt.zcu.intern_pool); 36138 } 36139 36140 pub fn getTmpAir(sema: Sema) Air { 36141 return .{ 36142 .instructions = sema.air_instructions.slice(), 36143 .extra = sema.air_extra, 36144 }; 36145 } 36146 36147 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 { 36148 const fields = std.meta.fields(@TypeOf(extra)); 36149 try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len); 36150 return sema.addExtraAssumeCapacity(extra); 36151 } 36152 36153 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { 36154 const result: u32 = @intCast(sema.air_extra.items.len); 36155 sema.air_extra.appendSliceAssumeCapacity(&payloadToExtraItems(extra)); 36156 return result; 36157 } 36158 36159 fn payloadToExtraItems(data: anytype) [@typeInfo(@TypeOf(data)).@"struct".fields.len]u32 { 36160 const fields = @typeInfo(@TypeOf(data)).@"struct".fields; 36161 var result: [fields.len]u32 = undefined; 36162 inline for (&result, fields) |*val, field| { 36163 val.* = switch (field.type) { 36164 u32 => @field(data, field.name), 36165 i32, Air.CondBr.BranchHints, Air.Asm.Flags => @bitCast(@field(data, field.name)), 36166 Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(data, field.name)), 36167 else => @compileError("bad field type: " ++ @typeName(field.type)), 36168 }; 36169 } 36170 return result; 36171 } 36172 36173 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void { 36174 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(refs)); 36175 } 36176 36177 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index { 36178 const air_datas = sema.air_instructions.items(.data); 36179 const air_tags = sema.air_instructions.items(.tag); 36180 switch (air_tags[@intFromEnum(inst_index)]) { 36181 .br => return air_datas[@intFromEnum(inst_index)].br.block_inst, 36182 else => return null, 36183 } 36184 } 36185 36186 fn isComptimeKnown( 36187 sema: *Sema, 36188 inst: Air.Inst.Ref, 36189 ) !bool { 36190 return (try sema.resolveValue(inst)) != null; 36191 } 36192 36193 fn analyzeComptimeAlloc( 36194 sema: *Sema, 36195 block: *Block, 36196 src: LazySrcLoc, 36197 var_type: Type, 36198 alignment: Alignment, 36199 ) CompileError!Air.Inst.Ref { 36200 const pt = sema.pt; 36201 const zcu = pt.zcu; 36202 36203 // Needed to make an anon decl with type `var_type` (the `finish()` call below). 36204 _ = try sema.typeHasOnePossibleValue(var_type); 36205 36206 const ptr_type = try pt.ptrTypeSema(.{ 36207 .child = var_type.toIntern(), 36208 .flags = .{ 36209 .alignment = alignment, 36210 .address_space = target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), 36211 }, 36212 }); 36213 36214 const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment); 36215 36216 return Air.internedToRef((try pt.intern(.{ .ptr = .{ 36217 .ty = ptr_type.toIntern(), 36218 .base_addr = .{ .comptime_alloc = alloc }, 36219 .byte_offset = 0, 36220 } }))); 36221 } 36222 36223 fn resolveAddressSpace( 36224 sema: *Sema, 36225 block: *Block, 36226 src: LazySrcLoc, 36227 zir_ref: Zir.Inst.Ref, 36228 ctx: std.builtin.AddressSpace.Context, 36229 ) !std.builtin.AddressSpace { 36230 const air_ref = try sema.resolveInst(zir_ref); 36231 return sema.analyzeAsAddressSpace(block, src, air_ref, ctx); 36232 } 36233 36234 pub fn analyzeAsAddressSpace( 36235 sema: *Sema, 36236 block: *Block, 36237 src: LazySrcLoc, 36238 air_ref: Air.Inst.Ref, 36239 ctx: std.builtin.AddressSpace.Context, 36240 ) !std.builtin.AddressSpace { 36241 const pt = sema.pt; 36242 const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace); 36243 const coerced = try sema.coerce(block, addrspace_ty, air_ref, src); 36244 const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{ .simple = .@"addrspace" }); 36245 const address_space = try sema.interpretBuiltinType(block, src, addrspace_val, std.builtin.AddressSpace); 36246 const target = pt.zcu.getTarget(); 36247 36248 if (!target.cpu.supportsAddressSpace(address_space, ctx)) { 36249 // TODO error messages could be made more elaborate here 36250 const entity = switch (ctx) { 36251 .function => "functions", 36252 .variable => "mutable values", 36253 .constant => "constant values", 36254 .pointer => "pointers", 36255 }; 36256 return sema.fail( 36257 block, 36258 src, 36259 "{s} with address space '{s}' are not supported on {s}", 36260 .{ entity, @tagName(address_space), @tagName(target.cpu.arch.family()) }, 36261 ); 36262 } 36263 36264 return address_space; 36265 } 36266 36267 /// Asserts the value is a pointer and dereferences it. 36268 /// Returns `null` if the pointer contents cannot be loaded at comptime. 36269 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { 36270 // TODO: audit use sites to eliminate this coercion 36271 const pt = sema.pt; 36272 const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty); 36273 switch (try sema.pointerDerefExtra(block, src, coerced_ptr_val)) { 36274 .runtime_load => return null, 36275 .val => |v| return v, 36276 .needed_well_defined => |ty| return sema.fail( 36277 block, 36278 src, 36279 "comptime dereference requires '{f}' to have a well-defined layout", 36280 .{ty.fmt(pt)}, 36281 ), 36282 .out_of_bounds => |ty| return sema.fail( 36283 block, 36284 src, 36285 "dereference of '{f}' exceeds bounds of containing decl of type '{f}'", 36286 .{ ptr_ty.fmt(pt), ty.fmt(pt) }, 36287 ), 36288 } 36289 } 36290 36291 const DerefResult = union(enum) { 36292 runtime_load, 36293 val: Value, 36294 needed_well_defined: Type, 36295 out_of_bounds: Type, 36296 }; 36297 36298 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value) CompileError!DerefResult { 36299 const pt = sema.pt; 36300 const ip = &pt.zcu.intern_pool; 36301 switch (try sema.loadComptimePtr(block, src, ptr_val)) { 36302 .success => |mv| return .{ .val = try mv.intern(pt, sema.arena) }, 36303 .runtime_load => return .runtime_load, 36304 .undef => return sema.failWithUseOfUndef(block, src), 36305 .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}), 36306 .null_payload => return sema.fail(block, src, "attempt to use null value", .{}), 36307 .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}), 36308 .needed_well_defined => |ty| return .{ .needed_well_defined = ty }, 36309 .out_of_bounds => |ty| return .{ .out_of_bounds = ty }, 36310 .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}), 36311 } 36312 } 36313 36314 /// Used to convert a u64 value to a usize value, emitting a compile error if the number 36315 /// is too big to fit. 36316 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { 36317 if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; 36318 return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value '{d}' which is too big for this compiler implementation to handle", .{int}); 36319 } 36320 36321 /// For pointer-like optionals, it returns the pointer type. For pointers, 36322 /// the type is returned unmodified. 36323 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether 36324 /// a type has zero bits, which can cause a "foo depends on itself" compile error. 36325 /// This logic must be kept in sync with `Type.isPtrLikeOptional`. 36326 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { 36327 const pt = sema.pt; 36328 const zcu = pt.zcu; 36329 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { 36330 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 36331 .one, .many, .c => ty, 36332 .slice => null, 36333 }, 36334 .opt_type => |opt_child| switch (zcu.intern_pool.indexToKey(opt_child)) { 36335 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 36336 .slice, .c => null, 36337 .many, .one => { 36338 if (ptr_type.flags.is_allowzero) return null; 36339 36340 // optionals of zero sized types behave like bools, not pointers 36341 const payload_ty: Type = .fromInterned(opt_child); 36342 if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) { 36343 return null; 36344 } 36345 36346 return payload_ty; 36347 }, 36348 }, 36349 else => null, 36350 }, 36351 else => null, 36352 }; 36353 } 36354 36355 fn unionFieldIndex( 36356 sema: *Sema, 36357 block: *Block, 36358 union_ty: Type, 36359 field_name: InternPool.NullTerminatedString, 36360 field_src: LazySrcLoc, 36361 ) !u32 { 36362 const pt = sema.pt; 36363 const zcu = pt.zcu; 36364 const ip = &zcu.intern_pool; 36365 try union_ty.resolveFields(pt); 36366 const union_obj = zcu.typeToUnion(union_ty).?; 36367 const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse 36368 return sema.failWithBadUnionFieldAccess(block, union_ty, union_obj, field_src, field_name); 36369 return @intCast(field_index); 36370 } 36371 36372 fn structFieldIndex( 36373 sema: *Sema, 36374 block: *Block, 36375 struct_ty: Type, 36376 field_name: InternPool.NullTerminatedString, 36377 field_src: LazySrcLoc, 36378 ) !u32 { 36379 const pt = sema.pt; 36380 const zcu = pt.zcu; 36381 const ip = &zcu.intern_pool; 36382 try struct_ty.resolveFields(pt); 36383 const struct_type = zcu.typeToStruct(struct_ty).?; 36384 return struct_type.nameIndex(ip, field_name) orelse 36385 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); 36386 } 36387 36388 const IntFromFloatMode = enum { exact, truncate }; 36389 36390 fn intFromFloat( 36391 sema: *Sema, 36392 block: *Block, 36393 src: LazySrcLoc, 36394 val: Value, 36395 float_ty: Type, 36396 int_ty: Type, 36397 mode: IntFromFloatMode, 36398 ) CompileError!Value { 36399 const pt = sema.pt; 36400 const zcu = pt.zcu; 36401 if (float_ty.zigTypeTag(zcu) == .vector) { 36402 const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(zcu)); 36403 for (result_data, 0..) |*scalar, i| { 36404 const elem_val = try val.elemValue(pt, i); 36405 scalar.* = (try sema.intFromFloatScalar(block, src, elem_val, int_ty.scalarType(zcu), mode)).toIntern(); 36406 } 36407 return Value.fromInterned(try pt.intern(.{ .aggregate = .{ 36408 .ty = int_ty.toIntern(), 36409 .storage = .{ .elems = result_data }, 36410 } })); 36411 } 36412 return sema.intFromFloatScalar(block, src, val, int_ty, mode); 36413 } 36414 36415 fn intFromFloatScalar( 36416 sema: *Sema, 36417 block: *Block, 36418 src: LazySrcLoc, 36419 val: Value, 36420 int_ty: Type, 36421 mode: IntFromFloatMode, 36422 ) CompileError!Value { 36423 const pt = sema.pt; 36424 const zcu = pt.zcu; 36425 36426 if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src); 36427 36428 const float = val.toFloat(f128, zcu); 36429 if (std.math.isNan(float)) { 36430 return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{ 36431 int_ty.fmt(pt), 36432 }); 36433 } 36434 if (std.math.isInf(float)) { 36435 return sema.fail(block, src, "float value Inf cannot be stored in integer type '{f}'", .{ 36436 int_ty.fmt(pt), 36437 }); 36438 } 36439 36440 var big_int: std.math.big.int.Mutable = .{ 36441 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), 36442 .len = undefined, 36443 .positive = undefined, 36444 }; 36445 switch (big_int.setFloat(float, .trunc)) { 36446 .inexact => switch (mode) { 36447 .exact => return sema.fail( 36448 block, 36449 src, 36450 "fractional component prevents float value '{f}' from coercion to type '{f}'", 36451 .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) }, 36452 ), 36453 .truncate => {}, 36454 }, 36455 .exact => {}, 36456 } 36457 const cti_result = try pt.intValue_big(.comptime_int, big_int.toConst()); 36458 if (int_ty.toIntern() == .comptime_int_type) return cti_result; 36459 36460 const int_info = int_ty.intInfo(zcu); 36461 if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) { 36462 return sema.fail(block, src, "float value '{f}' cannot be stored in integer type '{f}'", .{ 36463 val.fmtValueSema(pt, sema), int_ty.fmt(pt), 36464 }); 36465 } 36466 return pt.getCoerced(cti_result, int_ty); 36467 } 36468 36469 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. 36470 /// Vectors are also accepted. Vector results are reduced with AND. 36471 /// 36472 /// If provided, `vector_index` reports the first element that failed the range check. 36473 fn intFitsInType( 36474 sema: *Sema, 36475 val: Value, 36476 ty: Type, 36477 vector_index: ?*usize, 36478 ) CompileError!bool { 36479 const pt = sema.pt; 36480 const zcu = pt.zcu; 36481 if (ty.toIntern() == .comptime_int_type) return true; 36482 const info = ty.intInfo(zcu); 36483 switch (val.toIntern()) { 36484 .zero_usize, .zero_u8 => return true, 36485 else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { 36486 .undef => return true, 36487 .variable, .@"extern", .func, .ptr => { 36488 const target = zcu.getTarget(); 36489 const ptr_bits = target.ptrBitWidth(); 36490 return switch (info.signedness) { 36491 .signed => info.bits > ptr_bits, 36492 .unsigned => info.bits >= ptr_bits, 36493 }; 36494 }, 36495 .int => |int| switch (int.storage) { 36496 .u64, .i64, .big_int => { 36497 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; 36498 const big_int = int.storage.toBigInt(&buffer); 36499 return big_int.fitsInTwosComp(info.signedness, info.bits); 36500 }, 36501 .lazy_align => |lazy_ty| { 36502 const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed); 36503 // If it is u16 or bigger we know the alignment fits without resolving it. 36504 if (info.bits >= max_needed_bits) return true; 36505 const x = try Type.fromInterned(lazy_ty).abiAlignmentSema(pt); 36506 if (x == .none) return true; 36507 const actual_needed_bits = @as(usize, x.toLog2Units()) + 1 + @intFromBool(info.signedness == .signed); 36508 return info.bits >= actual_needed_bits; 36509 }, 36510 .lazy_size => |lazy_ty| { 36511 const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed); 36512 // If it is u64 or bigger we know the size fits without resolving it. 36513 if (info.bits >= max_needed_bits) return true; 36514 const x = try Type.fromInterned(lazy_ty).abiSizeSema(pt); 36515 if (x == 0) return true; 36516 const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed); 36517 return info.bits >= actual_needed_bits; 36518 }, 36519 }, 36520 .aggregate => |aggregate| { 36521 assert(ty.zigTypeTag(zcu) == .vector); 36522 return switch (aggregate.storage) { 36523 .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(zcu), &zcu.intern_pool), 0..) |byte, i| { 36524 if (byte == 0) continue; 36525 const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); 36526 if (info.bits >= actual_needed_bits) continue; 36527 if (vector_index) |vi| vi.* = i; 36528 break false; 36529 } else true, 36530 .elems, .repeated_elem => for (switch (aggregate.storage) { 36531 .bytes => unreachable, 36532 .elems => |elems| elems, 36533 .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), 36534 }, 0..) |elem, i| { 36535 if (try sema.intFitsInType(Value.fromInterned(elem), ty.scalarType(zcu), null)) continue; 36536 if (vector_index) |vi| vi.* = i; 36537 break false; 36538 } else true, 36539 }; 36540 }, 36541 else => unreachable, 36542 }, 36543 } 36544 } 36545 36546 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { 36547 const pt = sema.pt; 36548 if (!(try int_val.compareAllWithZeroSema(.gte, pt))) return false; 36549 const end_val = try pt.intValue(tag_ty, end); 36550 if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false; 36551 return true; 36552 } 36553 36554 /// Asserts the type is an enum. 36555 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { 36556 const pt = sema.pt; 36557 const zcu = pt.zcu; 36558 const enum_type = zcu.intern_pool.loadEnumType(ty.toIntern()); 36559 assert(enum_type.tag_mode != .nonexhaustive); 36560 // The `tagValueIndex` function call below relies on the type being the integer tag type. 36561 // `getCoerced` assumes the value will fit the new type. 36562 if (!(try sema.intFitsInType(int, .fromInterned(enum_type.tag_ty), null))) return false; 36563 const int_coerced = try pt.getCoerced(int, .fromInterned(enum_type.tag_ty)); 36564 36565 return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null; 36566 } 36567 36568 /// Asserts the values are comparable. Both operands have type `ty`. 36569 /// For vectors, returns true if the comparison is true for ALL elements. 36570 /// 36571 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)` 36572 fn compareAll( 36573 sema: *Sema, 36574 lhs: Value, 36575 op: std.math.CompareOperator, 36576 rhs: Value, 36577 ty: Type, 36578 ) CompileError!bool { 36579 const pt = sema.pt; 36580 const zcu = pt.zcu; 36581 if (ty.zigTypeTag(zcu) == .vector) { 36582 var i: usize = 0; 36583 while (i < ty.vectorLen(zcu)) : (i += 1) { 36584 const lhs_elem = try lhs.elemValue(pt, i); 36585 const rhs_elem = try rhs.elemValue(pt, i); 36586 if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)))) { 36587 return false; 36588 } 36589 } 36590 return true; 36591 } 36592 return sema.compareScalar(lhs, op, rhs, ty); 36593 } 36594 36595 /// Asserts the values are comparable. Both operands have type `ty`. 36596 fn compareScalar( 36597 sema: *Sema, 36598 lhs: Value, 36599 op: std.math.CompareOperator, 36600 rhs: Value, 36601 ty: Type, 36602 ) CompileError!bool { 36603 const pt = sema.pt; 36604 const coerced_lhs = try pt.getCoerced(lhs, ty); 36605 const coerced_rhs = try pt.getCoerced(rhs, ty); 36606 36607 // Equality comparisons of signed zero and NaN need to use floating point semantics 36608 if (coerced_lhs.isFloat(pt.zcu) or coerced_rhs.isFloat(pt.zcu)) 36609 return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt); 36610 36611 switch (op) { 36612 .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty), 36613 .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)), 36614 else => return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt), 36615 } 36616 } 36617 36618 fn valuesEqual( 36619 sema: *Sema, 36620 lhs: Value, 36621 rhs: Value, 36622 ty: Type, 36623 ) CompileError!bool { 36624 return lhs.eql(rhs, ty, sema.pt.zcu); 36625 } 36626 36627 /// Asserts the values are comparable vectors of type `ty`. 36628 fn compareVector( 36629 sema: *Sema, 36630 lhs: Value, 36631 op: std.math.CompareOperator, 36632 rhs: Value, 36633 ty: Type, 36634 ) !Value { 36635 const pt = sema.pt; 36636 const zcu = pt.zcu; 36637 assert(ty.zigTypeTag(zcu) == .vector); 36638 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(zcu)); 36639 for (result_data, 0..) |*scalar, i| { 36640 const lhs_elem = try lhs.elemValue(pt, i); 36641 const rhs_elem = try rhs.elemValue(pt, i); 36642 if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) { 36643 scalar.* = .undef_bool; 36644 } else { 36645 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)); 36646 scalar.* = Value.makeBool(res_bool).toIntern(); 36647 } 36648 } 36649 return Value.fromInterned(try pt.intern(.{ .aggregate = .{ 36650 .ty = (try pt.vectorType(.{ .len = ty.vectorLen(zcu), .child = .bool_type })).toIntern(), 36651 .storage = .{ .elems = result_data }, 36652 } })); 36653 } 36654 36655 /// Merge lhs with rhs. 36656 /// Asserts that lhs and rhs are both error sets and are resolved. 36657 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { 36658 const pt = sema.pt; 36659 const ip = &pt.zcu.intern_pool; 36660 const arena = sema.arena; 36661 const lhs_names = lhs.errorSetNames(pt.zcu); 36662 const rhs_names = rhs.errorSetNames(pt.zcu); 36663 var names: InferredErrorSet.NameMap = .{}; 36664 try names.ensureUnusedCapacity(arena, lhs_names.len); 36665 36666 for (0..lhs_names.len) |lhs_index| { 36667 names.putAssumeCapacityNoClobber(lhs_names.get(ip)[lhs_index], {}); 36668 } 36669 for (0..rhs_names.len) |rhs_index| { 36670 try names.put(arena, rhs_names.get(ip)[rhs_index], {}); 36671 } 36672 36673 return pt.errorSetFromUnsortedNames(names.keys()); 36674 } 36675 36676 /// Avoids crashing the compiler when asking if inferred allocations are noreturn. 36677 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool { 36678 if (ref == .unreachable_value) return true; 36679 if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) { 36680 .inferred_alloc, .inferred_alloc_comptime => return false, 36681 else => {}, 36682 }; 36683 return sema.typeOf(ref).isNoReturn(sema.pt.zcu); 36684 } 36685 36686 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type. 36687 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool { 36688 if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) { 36689 .inferred_alloc, .inferred_alloc_comptime => return false, 36690 else => {}, 36691 }; 36692 return sema.typeOf(ref).zigTypeTag(sema.pt.zcu) == tag; 36693 } 36694 36695 pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { 36696 const pt = sema.pt; 36697 if (!pt.zcu.comp.incremental) return; 36698 36699 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee); 36700 if (gop.found_existing) return; 36701 36702 // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields 36703 // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would 36704 // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve 36705 // the loop. 36706 // Note that this also disallows a `nav_val` 36707 switch (sema.owner.unwrap()) { 36708 .nav_val => |this_nav| switch (dependee) { 36709 .nav_val => |other_nav| if (this_nav == other_nav) return, 36710 else => {}, 36711 }, 36712 .nav_ty => |this_nav| switch (dependee) { 36713 .nav_ty => |other_nav| if (this_nav == other_nav) return, 36714 else => {}, 36715 }, 36716 else => {}, 36717 } 36718 36719 try pt.addDependency(sema.owner, dependee); 36720 } 36721 36722 fn isComptimeMutablePtr(sema: *Sema, val: Value) bool { 36723 return switch (sema.pt.zcu.intern_pool.indexToKey(val.toIntern())) { 36724 .slice => |slice| sema.isComptimeMutablePtr(Value.fromInterned(slice.ptr)), 36725 .ptr => |ptr| switch (ptr.base_addr) { 36726 .uav, .nav, .int => false, 36727 .comptime_field => true, 36728 .comptime_alloc => |alloc_index| !sema.getComptimeAlloc(alloc_index).is_const, 36729 .eu_payload, .opt_payload => |base| sema.isComptimeMutablePtr(Value.fromInterned(base)), 36730 .arr_elem, .field => |bi| sema.isComptimeMutablePtr(Value.fromInterned(bi.base)), 36731 }, 36732 else => false, 36733 }; 36734 } 36735 36736 fn checkRuntimeValue(sema: *Sema, ptr: Air.Inst.Ref) bool { 36737 const val = ptr.toInterned() orelse return true; 36738 return !Value.fromInterned(val).canMutateComptimeVarState(sema.pt.zcu); 36739 } 36740 36741 fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Air.Inst.Ref) CompileError!void { 36742 if (sema.checkRuntimeValue(val)) return; 36743 return sema.failWithOwnedErrorMsg(block, msg: { 36744 const msg = try sema.errMsg(val_src, "runtime value contains reference to comptime var", .{}); 36745 errdefer msg.destroy(sema.gpa); 36746 try sema.errNote(val_src, msg, "comptime var pointers are not available at runtime", .{}); 36747 const pt = sema.pt; 36748 const zcu = pt.zcu; 36749 const val_str = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, "runtime_value", .no_embedded_nulls); 36750 try sema.explainWhyValueContainsReferenceToComptimeVar(msg, val_src, val_str, .fromInterned(val.toInterned().?)); 36751 break :msg msg; 36752 }); 36753 } 36754 36755 fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError { 36756 return sema.failWithOwnedErrorMsg(block, msg: { 36757 const msg = try sema.errMsg(src, "{s} contains reference to comptime var", .{kind_of_value}); 36758 errdefer msg.destroy(sema.gpa); 36759 if (val) |v| try sema.explainWhyValueContainsReferenceToComptimeVar(msg, src, value_name, v); 36760 break :msg msg; 36761 }); 36762 } 36763 36764 fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, val: Value) Allocator.Error!void { 36765 // Our goal is something like this: 36766 // note: '(value.? catch unreachable)[0]' points to 'v0.?.foo' 36767 // note: '(v0.?.bar catch unreachable)' points to 'v1' 36768 // note: 'v1.?' points to a comptime var 36769 36770 var intermediate_value_count: u32 = 0; 36771 var cur_val: Value = val; 36772 while (true) { 36773 switch (try sema.notePathToComptimeAllocPtr(msg, src, cur_val, intermediate_value_count, value_name)) { 36774 .done => return, 36775 .new_val => |new_val| { 36776 intermediate_value_count += 1; 36777 cur_val = new_val; 36778 }, 36779 } 36780 } 36781 } 36782 36783 fn notePathToComptimeAllocPtr( 36784 sema: *Sema, 36785 msg: *Zcu.ErrorMsg, 36786 src: LazySrcLoc, 36787 val: Value, 36788 intermediate_value_count: u32, 36789 start_value_name: InternPool.NullTerminatedString, 36790 ) Allocator.Error!union(enum) { 36791 done, 36792 new_val: Value, 36793 } { 36794 const arena = sema.arena; 36795 const pt = sema.pt; 36796 const zcu = pt.zcu; 36797 const ip = &zcu.intern_pool; 36798 36799 var first_path: std.ArrayListUnmanaged(u8) = .empty; 36800 if (intermediate_value_count == 0) { 36801 try first_path.print(arena, "{f}", .{start_value_name.fmt(ip)}); 36802 } else { 36803 try first_path.print(arena, "v{d}", .{intermediate_value_count - 1}); 36804 } 36805 36806 const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path); 36807 36808 switch (ip.indexToKey(comptime_ptr.toIntern()).ptr.base_addr) { 36809 .comptime_field => { 36810 try sema.errNote(src, msg, "'{s}' points to comptime field", .{first_path.items}); 36811 return .done; 36812 }, 36813 .comptime_alloc => |idx| { 36814 const cta = sema.getComptimeAlloc(idx); 36815 if (!cta.is_const) { 36816 try sema.errNote(cta.src, msg, "'{s}' points to comptime var declared here", .{first_path.items}); 36817 return .done; 36818 } 36819 }, 36820 else => {}, // there will be another stage 36821 } 36822 36823 const derivation = comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema) catch |err| switch (err) { 36824 error.OutOfMemory => |e| return e, 36825 error.AnalysisFail => unreachable, 36826 }; 36827 36828 var second_path_aw: std.io.Writer.Allocating = .init(arena); 36829 defer second_path_aw.deinit(); 36830 const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count}); 36831 const deriv_start = @import("print_value.zig").printPtrDerivation( 36832 derivation, 36833 &second_path_aw.writer, 36834 pt, 36835 .lvalue, 36836 .{ .str = inter_name }, 36837 20, 36838 ) catch return error.OutOfMemory; 36839 36840 switch (deriv_start) { 36841 .int, .nav_ptr => unreachable, 36842 .uav_ptr => |uav| { 36843 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.getWritten() }); 36844 return .{ .new_val = .fromInterned(uav.val) }; 36845 }, 36846 .comptime_alloc_ptr => |cta_info| { 36847 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.getWritten() }); 36848 const cta = sema.getComptimeAlloc(cta_info.idx); 36849 if (cta.is_const) { 36850 return .{ .new_val = cta_info.val }; 36851 } else { 36852 try sema.errNote(cta.src, msg, "'{s}' is a comptime var declared here", .{inter_name}); 36853 return .done; 36854 } 36855 }, 36856 .comptime_field_ptr => { 36857 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.getWritten() }); 36858 try sema.errNote(src, msg, "'{s}' is a comptime field", .{inter_name}); 36859 return .done; 36860 }, 36861 .eu_payload_ptr, 36862 .opt_payload_ptr, 36863 .field_ptr, 36864 .elem_ptr, 36865 .offset_and_cast, 36866 => unreachable, 36867 } 36868 } 36869 36870 fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayListUnmanaged(u8)) Allocator.Error!Value { 36871 const pt = sema.pt; 36872 const zcu = pt.zcu; 36873 const ip = &zcu.intern_pool; 36874 const arena = sema.arena; 36875 assert(val.canMutateComptimeVarState(zcu)); 36876 switch (ip.indexToKey(val.toIntern())) { 36877 .ptr => return val, 36878 .error_union => |eu| { 36879 try path.insert(arena, 0, '('); 36880 try path.appendSlice(arena, " catch unreachable)"); 36881 return sema.notePathToComptimeAllocPtrInner(.fromInterned(eu.val.payload), path); 36882 }, 36883 .slice => |slice| { 36884 try path.appendSlice(arena, ".ptr"); 36885 return sema.notePathToComptimeAllocPtrInner(.fromInterned(slice.ptr), path); 36886 }, 36887 .opt => |opt| { 36888 try path.appendSlice(arena, ".?"); 36889 return sema.notePathToComptimeAllocPtrInner(.fromInterned(opt.val), path); 36890 }, 36891 .un => |un| { 36892 assert(un.tag != .none); 36893 const union_ty: Type = .fromInterned(un.ty); 36894 const backing_enum = union_ty.unionTagTypeHypothetical(zcu); 36895 const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?; 36896 const field_name = backing_enum.enumFieldName(field_idx, zcu); 36897 try path.print(arena, ".{f}", .{field_name.fmt(ip)}); 36898 return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path); 36899 }, 36900 .aggregate => |agg| { 36901 const elem: InternPool.Index, const elem_idx: usize = switch (agg.storage) { 36902 .bytes => unreachable, 36903 .repeated_elem => |elem| .{ elem, 0 }, 36904 .elems => |elems| for (elems, 0..) |elem, elem_idx| { 36905 if (Value.fromInterned(elem).canMutateComptimeVarState(zcu)) { 36906 break .{ elem, elem_idx }; 36907 } 36908 } else unreachable, 36909 }; 36910 const agg_ty: Type = .fromInterned(agg.ty); 36911 switch (agg_ty.zigTypeTag(zcu)) { 36912 .array, .vector => try path.print(arena, "[{d}]", .{elem_idx}), 36913 .pointer => switch (elem_idx) { 36914 Value.slice_ptr_index => try path.appendSlice(arena, ".ptr"), 36915 Value.slice_len_index => try path.appendSlice(arena, ".len"), 36916 else => unreachable, 36917 }, 36918 .@"struct" => if (agg_ty.isTuple(zcu)) { 36919 try path.print(arena, "[{d}]", .{elem_idx}); 36920 } else { 36921 const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?; 36922 try path.print(arena, ".{f}", .{name.fmt(ip)}); 36923 }, 36924 else => unreachable, 36925 } 36926 return sema.notePathToComptimeAllocPtrInner(.fromInterned(elem), path); 36927 }, 36928 else => unreachable, 36929 } 36930 } 36931 36932 /// Returns true if any value contained in `val` is undefined. 36933 fn anyUndef(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) !bool { 36934 const pt = sema.pt; 36935 const zcu = pt.zcu; 36936 return switch (zcu.intern_pool.indexToKey(val.toIntern())) { 36937 .undef => true, 36938 .simple_value => |v| v == .undefined, 36939 .slice => { 36940 // If the slice contents are runtime-known, reification will fail later on with a 36941 // specific error message. 36942 const arr = try sema.maybeDerefSliceAsArray(block, src, val) orelse return false; 36943 return sema.anyUndef(block, src, arr); 36944 }, 36945 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| { 36946 const elem = zcu.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i]; 36947 if (try sema.anyUndef(block, src, Value.fromInterned(elem))) break true; 36948 } else false, 36949 else => false, 36950 }; 36951 } 36952 36953 /// Asserts that `slice_val` is a slice of `u8`. 36954 fn sliceToIpString( 36955 sema: *Sema, 36956 block: *Block, 36957 src: LazySrcLoc, 36958 slice_val: Value, 36959 reason: ComptimeReason, 36960 ) CompileError!InternPool.NullTerminatedString { 36961 const pt = sema.pt; 36962 const zcu = pt.zcu; 36963 const slice_ty = slice_val.typeOf(zcu); 36964 assert(slice_ty.isSlice(zcu)); 36965 assert(slice_ty.childType(zcu).toIntern() == .u8_type); 36966 const array_val = try sema.derefSliceAsArray(block, src, slice_val, reason); 36967 const array_ty = array_val.typeOf(zcu); 36968 return array_val.toIpString(array_ty, pt); 36969 } 36970 36971 /// Given a slice value, attempts to dereference it into a comptime-known array. 36972 /// Emits a compile error if the contents of the slice are not comptime-known. 36973 /// Asserts that `slice_val` is a slice. 36974 fn derefSliceAsArray( 36975 sema: *Sema, 36976 block: *Block, 36977 src: LazySrcLoc, 36978 slice_val: Value, 36979 reason: ComptimeReason, 36980 ) CompileError!Value { 36981 return try sema.maybeDerefSliceAsArray(block, src, slice_val) orelse { 36982 return sema.failWithNeededComptime(block, src, reason); 36983 }; 36984 } 36985 36986 /// Given a slice value, attempts to dereference it into a comptime-known array. 36987 /// Returns `null` if the contents of the slice are not comptime-known. 36988 /// Asserts that `slice_val` is a slice. 36989 fn maybeDerefSliceAsArray( 36990 sema: *Sema, 36991 block: *Block, 36992 src: LazySrcLoc, 36993 slice_val: Value, 36994 ) CompileError!?Value { 36995 const pt = sema.pt; 36996 const zcu = pt.zcu; 36997 const ip = &zcu.intern_pool; 36998 assert(slice_val.typeOf(zcu).isSlice(zcu)); 36999 const slice = switch (ip.indexToKey(slice_val.toIntern())) { 37000 .undef => return sema.failWithUseOfUndef(block, src), 37001 .slice => |slice| slice, 37002 else => unreachable, 37003 }; 37004 const elem_ty = Type.fromInterned(slice.ty).childType(zcu); 37005 const len = try Value.fromInterned(slice.len).toUnsignedIntSema(pt); 37006 const array_ty = try pt.arrayType(.{ 37007 .child = elem_ty.toIntern(), 37008 .len = len, 37009 }); 37010 const ptr_ty = try pt.ptrTypeSema(p: { 37011 var p = Type.fromInterned(slice.ty).ptrInfo(zcu); 37012 p.flags.size = .one; 37013 p.child = array_ty.toIntern(); 37014 p.sentinel = .none; 37015 break :p p; 37016 }); 37017 const casted_ptr = try pt.getCoerced(Value.fromInterned(slice.ptr), ptr_ty); 37018 return sema.pointerDeref(block, src, casted_ptr, ptr_ty); 37019 } 37020 37021 fn analyzeUnreachable(sema: *Sema, block: *Block, src: LazySrcLoc, safety_check: bool) !void { 37022 if (safety_check and block.wantSafety()) { 37023 // We only apply the first hint in a branch. 37024 // This allows user-provided hints to override implicit cold hints. 37025 if (sema.branch_hint == null) { 37026 sema.branch_hint = .cold; 37027 } 37028 37029 try sema.safetyPanic(block, src, .reached_unreachable); 37030 } else { 37031 _ = try block.addNoOp(.unreach); 37032 } 37033 } 37034 37035 /// This should be called exactly once, at the end of a `Sema`'s lifetime. 37036 /// It takes the exports stored in `sema.export` and flushes them to the `Zcu` 37037 /// to be processed by the linker after the update. 37038 pub fn flushExports(sema: *Sema) !void { 37039 if (sema.exports.items.len == 0) return; 37040 37041 const zcu = sema.pt.zcu; 37042 const gpa = zcu.gpa; 37043 37044 // There may be existing exports. For instance, a struct may export 37045 // things during both field type resolution and field default resolution. 37046 // 37047 // So, pick up and delete any existing exports. This strategy performs 37048 // redundant work, but that's okay, because this case is exceedingly rare. 37049 if (zcu.single_exports.get(sema.owner)) |export_idx| { 37050 try sema.exports.append(gpa, export_idx.ptr(zcu).*); 37051 } else if (zcu.multi_exports.get(sema.owner)) |info| { 37052 try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]); 37053 } 37054 zcu.deleteUnitExports(sema.owner); 37055 37056 // `sema.exports` is completed; store the data into the `Zcu`. 37057 if (sema.exports.items.len == 1) { 37058 try zcu.single_exports.ensureUnusedCapacity(gpa, 1); 37059 const export_idx: Zcu.Export.Index = zcu.free_exports.pop() orelse idx: { 37060 _ = try zcu.all_exports.addOne(gpa); 37061 break :idx @enumFromInt(zcu.all_exports.items.len - 1); 37062 }; 37063 export_idx.ptr(zcu).* = sema.exports.items[0]; 37064 zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx); 37065 } else { 37066 try zcu.multi_exports.ensureUnusedCapacity(gpa, 1); 37067 const exports_base = zcu.all_exports.items.len; 37068 try zcu.all_exports.appendSlice(gpa, sema.exports.items); 37069 zcu.multi_exports.putAssumeCapacityNoClobber(sema.owner, .{ 37070 .index = @intCast(exports_base), 37071 .len = @intCast(sema.exports.items.len), 37072 }); 37073 } 37074 } 37075 37076 /// Called as soon as a `declared` enum type is created. 37077 /// Resolves the tag type and field inits. 37078 /// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this. 37079 pub fn resolveDeclaredEnum( 37080 pt: Zcu.PerThread, 37081 wip_ty: InternPool.WipEnumType, 37082 inst: Zir.Inst.Index, 37083 tracked_inst: InternPool.TrackedInst.Index, 37084 namespace: InternPool.NamespaceIndex, 37085 type_name: InternPool.NullTerminatedString, 37086 small: Zir.Inst.EnumDecl.Small, 37087 body: []const Zir.Inst.Index, 37088 tag_type_ref: Zir.Inst.Ref, 37089 any_values: bool, 37090 fields_len: u32, 37091 zir: Zir, 37092 body_end: usize, 37093 ) Zcu.SemaError!void { 37094 const zcu = pt.zcu; 37095 const gpa = zcu.gpa; 37096 37097 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) }; 37098 37099 var arena: std.heap.ArenaAllocator = .init(gpa); 37100 defer arena.deinit(); 37101 37102 var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa); 37103 defer comptime_err_ret_trace.deinit(); 37104 37105 var sema: Sema = .{ 37106 .pt = pt, 37107 .gpa = gpa, 37108 .arena = arena.allocator(), 37109 .code = zir, 37110 .owner = .wrap(.{ .type = wip_ty.index }), 37111 .func_index = .none, 37112 .func_is_naked = false, 37113 .fn_ret_ty = .void, 37114 .fn_ret_ty_ies = null, 37115 .comptime_err_ret_trace = &comptime_err_ret_trace, 37116 }; 37117 defer sema.deinit(); 37118 37119 if (zcu.comp.debugIncremental()) { 37120 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, sema.owner); 37121 info.last_update_gen = zcu.generation; 37122 } 37123 37124 try sema.declareDependency(.{ .src_hash = tracked_inst }); 37125 37126 var block: Block = .{ 37127 .parent = null, 37128 .sema = &sema, 37129 .namespace = namespace, 37130 .instructions = .{}, 37131 .inlining = null, 37132 .comptime_reason = .{ .reason = .{ 37133 .src = src, 37134 .r = .{ .simple = .enum_fields }, 37135 } }, 37136 .src_base_inst = tracked_inst, 37137 .type_name_ctx = type_name, 37138 }; 37139 defer block.instructions.deinit(gpa); 37140 37141 sema.resolveDeclaredEnumInner( 37142 &block, 37143 wip_ty, 37144 inst, 37145 tracked_inst, 37146 src, 37147 small, 37148 body, 37149 tag_type_ref, 37150 any_values, 37151 fields_len, 37152 zir, 37153 body_end, 37154 ) catch |err| switch (err) { 37155 error.ComptimeBreak => unreachable, 37156 error.ComptimeReturn => unreachable, 37157 error.OutOfMemory => |e| return e, 37158 error.AnalysisFail => { 37159 if (!zcu.failed_analysis.contains(sema.owner)) { 37160 try zcu.transitive_failed_analysis.put(gpa, sema.owner, {}); 37161 } 37162 return error.AnalysisFail; 37163 }, 37164 }; 37165 } 37166 37167 fn resolveDeclaredEnumInner( 37168 sema: *Sema, 37169 block: *Block, 37170 wip_ty: InternPool.WipEnumType, 37171 inst: Zir.Inst.Index, 37172 tracked_inst: InternPool.TrackedInst.Index, 37173 src: LazySrcLoc, 37174 small: Zir.Inst.EnumDecl.Small, 37175 body: []const Zir.Inst.Index, 37176 tag_type_ref: Zir.Inst.Ref, 37177 any_values: bool, 37178 fields_len: u32, 37179 zir: Zir, 37180 body_end: usize, 37181 ) Zcu.CompileError!void { 37182 const pt = sema.pt; 37183 const zcu = pt.zcu; 37184 const gpa = zcu.gpa; 37185 const ip = &zcu.intern_pool; 37186 37187 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; 37188 37189 const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = .zero } }; 37190 37191 const int_tag_ty = ty: { 37192 if (body.len != 0) { 37193 _ = try sema.analyzeInlineBody(block, body, inst); 37194 } 37195 37196 if (tag_type_ref != .none) { 37197 const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); 37198 if (ty.zigTypeTag(zcu) != .int and ty.zigTypeTag(zcu) != .comptime_int) { 37199 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{f}'", .{ty.fmt(pt)}); 37200 } 37201 break :ty ty; 37202 } else if (fields_len == 0) { 37203 break :ty try pt.intType(.unsigned, 0); 37204 } else { 37205 const bits = std.math.log2_int_ceil(usize, fields_len); 37206 break :ty try pt.intType(.unsigned, bits); 37207 } 37208 }; 37209 37210 wip_ty.setTagTy(ip, int_tag_ty.toIntern()); 37211 37212 var extra_index = body_end + bit_bags_count; 37213 var bit_bag_index: usize = body_end; 37214 var cur_bit_bag: u32 = undefined; 37215 var last_tag_val: ?Value = null; 37216 for (0..fields_len) |field_i_usize| { 37217 const field_i: u32 = @intCast(field_i_usize); 37218 if (field_i % 32 == 0) { 37219 cur_bit_bag = zir.extra[bit_bag_index]; 37220 bit_bag_index += 1; 37221 } 37222 const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0; 37223 cur_bit_bag >>= 1; 37224 37225 const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]); 37226 const field_name_zir = zir.nullTerminatedString(field_name_index); 37227 extra_index += 1; // field name 37228 37229 const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls); 37230 37231 const value_src: LazySrcLoc = .{ 37232 .base_node_inst = tracked_inst, 37233 .offset = .{ .container_field_value = field_i }, 37234 }; 37235 37236 const tag_overflow = if (has_tag_value) overflow: { 37237 const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 37238 extra_index += 1; 37239 const tag_inst = try sema.resolveInst(tag_val_ref); 37240 last_tag_val = try sema.resolveConstDefinedValue(block, .{ 37241 .base_node_inst = tracked_inst, 37242 .offset = .{ .container_field_name = field_i }, 37243 }, tag_inst, .{ .simple = .enum_field_tag_value }); 37244 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true; 37245 last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty); 37246 if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| { 37247 assert(conflict.kind == .value); // AstGen validated names are unique 37248 const other_field_src: LazySrcLoc = .{ 37249 .base_node_inst = tracked_inst, 37250 .offset = .{ .container_field_value = conflict.prev_field_idx }, 37251 }; 37252 const msg = msg: { 37253 const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)}); 37254 errdefer msg.destroy(gpa); 37255 try sema.errNote(other_field_src, msg, "other occurrence here", .{}); 37256 break :msg msg; 37257 }; 37258 return sema.failWithOwnedErrorMsg(block, msg); 37259 } 37260 break :overflow false; 37261 } else if (any_values) overflow: { 37262 if (last_tag_val) |last_tag| { 37263 const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag); 37264 last_tag_val = result.val; 37265 if (result.overflow) break :overflow true; 37266 } else { 37267 last_tag_val = try pt.intValue(int_tag_ty, 0); 37268 } 37269 if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| { 37270 assert(conflict.kind == .value); // AstGen validated names are unique 37271 const other_field_src: LazySrcLoc = .{ 37272 .base_node_inst = tracked_inst, 37273 .offset = .{ .container_field_value = conflict.prev_field_idx }, 37274 }; 37275 const msg = msg: { 37276 const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)}); 37277 errdefer msg.destroy(gpa); 37278 try sema.errNote(other_field_src, msg, "other occurrence here", .{}); 37279 break :msg msg; 37280 }; 37281 return sema.failWithOwnedErrorMsg(block, msg); 37282 } 37283 break :overflow false; 37284 } else overflow: { 37285 assert(wip_ty.nextField(ip, field_name, .none) == null); 37286 last_tag_val = try pt.intValue(.comptime_int, field_i); 37287 if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true; 37288 last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty); 37289 break :overflow false; 37290 }; 37291 37292 if (tag_overflow) { 37293 const msg = try sema.errMsg(value_src, "enumeration value '{f}' too large for type '{f}'", .{ 37294 last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt), 37295 }); 37296 return sema.failWithOwnedErrorMsg(block, msg); 37297 } 37298 } 37299 if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) { 37300 if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) { 37301 return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); 37302 } 37303 } 37304 } 37305 37306 pub const bitCastVal = @import("Sema/bitcast.zig").bitCast; 37307 pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice; 37308 37309 const loadComptimePtr = @import("Sema/comptime_ptr_access.zig").loadComptimePtr; 37310 const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadResult; 37311 const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr; 37312 const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult; 37313 37314 pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!Type { 37315 assert(decl.kind() == .type); 37316 try sema.ensureMemoizedStateResolved(src, decl.stage()); 37317 return .fromInterned(sema.pt.zcu.builtin_decl_values.get(decl)); 37318 } 37319 pub fn getBuiltin(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!InternPool.Index { 37320 assert(decl.kind() != .type); 37321 try sema.ensureMemoizedStateResolved(src, decl.stage()); 37322 return sema.pt.zcu.builtin_decl_values.get(decl); 37323 } 37324 37325 pub const NavPtrModifiers = struct { 37326 alignment: Alignment, 37327 @"linksection": InternPool.OptionalNullTerminatedString, 37328 @"addrspace": std.builtin.AddressSpace, 37329 }; 37330 37331 pub fn resolveNavPtrModifiers( 37332 sema: *Sema, 37333 block: *Block, 37334 zir_decl: Zir.Inst.Declaration.Unwrapped, 37335 decl_inst: Zir.Inst.Index, 37336 nav_ty: Type, 37337 ) CompileError!NavPtrModifiers { 37338 const pt = sema.pt; 37339 const zcu = pt.zcu; 37340 const gpa = zcu.gpa; 37341 const ip = &zcu.intern_pool; 37342 37343 const align_src = block.src(.{ .node_offset_var_decl_align = .zero }); 37344 const section_src = block.src(.{ .node_offset_var_decl_section = .zero }); 37345 const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = .zero }); 37346 37347 const alignment: InternPool.Alignment = a: { 37348 const align_body = zir_decl.align_body orelse break :a .none; 37349 const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst); 37350 break :a try sema.analyzeAsAlign(block, align_src, align_ref); 37351 }; 37352 37353 const @"linksection": InternPool.OptionalNullTerminatedString = ls: { 37354 const linksection_body = zir_decl.linksection_body orelse break :ls .none; 37355 const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst); 37356 const bytes = try sema.toConstString(block, section_src, linksection_ref, .{ .simple = .@"linksection" }); 37357 if (std.mem.indexOfScalar(u8, bytes, 0) != null) { 37358 return sema.fail(block, section_src, "linksection cannot contain null bytes", .{}); 37359 } else if (bytes.len == 0) { 37360 return sema.fail(block, section_src, "linksection cannot be empty", .{}); 37361 } 37362 break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls); 37363 }; 37364 37365 const @"addrspace": std.builtin.AddressSpace = as: { 37366 const addrspace_ctx: std.builtin.AddressSpace.Context = switch (zir_decl.kind) { 37367 .@"var" => .variable, 37368 else => switch (nav_ty.zigTypeTag(zcu)) { 37369 .@"fn" => .function, 37370 else => .constant, 37371 }, 37372 }; 37373 const target = zcu.getTarget(); 37374 const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) { 37375 .function => target_util.defaultAddressSpace(target, .function), 37376 .variable => target_util.defaultAddressSpace(target, .global_mutable), 37377 .constant => target_util.defaultAddressSpace(target, .global_constant), 37378 else => unreachable, 37379 }; 37380 const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst); 37381 break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx); 37382 }; 37383 37384 return .{ 37385 .alignment = alignment, 37386 .@"linksection" = @"linksection", 37387 .@"addrspace" = @"addrspace", 37388 }; 37389 } 37390 37391 pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool { 37392 const pt = sema.pt; 37393 const zcu = pt.zcu; 37394 const ip = &zcu.intern_pool; 37395 const gpa = zcu.gpa; 37396 37397 var any_changed = false; 37398 37399 inline for (comptime std.enums.values(Zcu.BuiltinDecl)) |builtin_decl| { 37400 if (stage == comptime builtin_decl.stage()) { 37401 const parent_ns: Zcu.Namespace.Index, const parent_name: []const u8, const name: []const u8 = switch (comptime builtin_decl.access()) { 37402 .direct => |name| .{ builtin_namespace, "std.builtin", name }, 37403 .nested => |nested| access: { 37404 const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(nested[0])); 37405 const parent_ns = parent_ty.getNamespace(zcu).unwrap() orelse { 37406 return sema.fail(block, simple_src, "std.builtin.{s} is not a container type", .{@tagName(nested[0])}); 37407 }; 37408 break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] }; 37409 }, 37410 }; 37411 37412 const name_nts = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); 37413 const nav = try sema.namespaceLookup(block, simple_src, parent_ns, name_nts) orelse 37414 return sema.fail(block, simple_src, "{s} missing {s}", .{ parent_name, name }); 37415 37416 const src: LazySrcLoc = .{ 37417 .base_node_inst = ip.getNav(nav).srcInst(ip), 37418 .offset = .nodeOffset(.zero), 37419 }; 37420 37421 const result = try sema.analyzeNavVal(block, src, nav); 37422 37423 const uncoerced_val = try sema.resolveConstDefinedValue(block, src, result, null); 37424 const maybe_lazy_val: Value = switch (builtin_decl.kind()) { 37425 .type => if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .type) { 37426 return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name }); 37427 } else val: { 37428 try uncoerced_val.toType().resolveFully(pt); 37429 break :val uncoerced_val; 37430 }, 37431 .func => val: { 37432 const func_ty = try sema.getExpectedBuiltinFnType(builtin_decl); 37433 const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src); 37434 break :val .fromInterned(coerced.toInterned().?); 37435 }, 37436 .string => val: { 37437 const coerced = try sema.coerce(block, .slice_const_u8, Air.internedToRef(uncoerced_val.toIntern()), src); 37438 break :val .fromInterned(coerced.toInterned().?); 37439 }, 37440 }; 37441 const val = try sema.resolveLazyValue(maybe_lazy_val); 37442 37443 const prev = zcu.builtin_decl_values.get(builtin_decl); 37444 if (val.toIntern() != prev) { 37445 zcu.builtin_decl_values.set(builtin_decl, val.toIntern()); 37446 any_changed = true; 37447 } 37448 } 37449 } 37450 37451 return any_changed; 37452 } 37453 37454 /// Given that `decl.kind() == .func`, get the type expected of the function. 37455 fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Type { 37456 const pt = sema.pt; 37457 return switch (decl) { 37458 // `noinline fn () void` 37459 .returnError => try pt.funcType(.{ 37460 .param_types = &.{}, 37461 .return_type = .void_type, 37462 .is_noinline = true, 37463 }), 37464 37465 // `fn ([]const u8, ?usize) noreturn` 37466 .@"panic.call" => try pt.funcType(.{ 37467 .param_types = &.{ 37468 .slice_const_u8_type, 37469 (try pt.optionalType(.usize_type)).toIntern(), 37470 }, 37471 .return_type = .noreturn_type, 37472 }), 37473 37474 // `fn (anytype, anytype) noreturn` 37475 .@"panic.sentinelMismatch", 37476 .@"panic.inactiveUnionField", 37477 => try pt.funcType(.{ 37478 .param_types = &.{ .generic_poison_type, .generic_poison_type }, 37479 .return_type = .noreturn_type, 37480 .is_generic = true, 37481 }), 37482 37483 // `fn (anyerror) noreturn` 37484 .@"panic.unwrapError" => try pt.funcType(.{ 37485 .param_types = &.{.anyerror_type}, 37486 .return_type = .noreturn_type, 37487 }), 37488 37489 // `fn (usize) noreturn` 37490 .@"panic.sliceCastLenRemainder" => try pt.funcType(.{ 37491 .param_types = &.{.usize_type}, 37492 .return_type = .noreturn_type, 37493 }), 37494 37495 // `fn (usize, usize) noreturn` 37496 .@"panic.outOfBounds", 37497 .@"panic.startGreaterThanEnd", 37498 => try pt.funcType(.{ 37499 .param_types = &.{ .usize_type, .usize_type }, 37500 .return_type = .noreturn_type, 37501 }), 37502 37503 // `fn () noreturn` 37504 .@"panic.reachedUnreachable", 37505 .@"panic.unwrapNull", 37506 .@"panic.castToNull", 37507 .@"panic.incorrectAlignment", 37508 .@"panic.invalidErrorCode", 37509 .@"panic.integerOutOfBounds", 37510 .@"panic.integerOverflow", 37511 .@"panic.shlOverflow", 37512 .@"panic.shrOverflow", 37513 .@"panic.divideByZero", 37514 .@"panic.exactDivisionRemainder", 37515 .@"panic.integerPartOutOfBounds", 37516 .@"panic.corruptSwitch", 37517 .@"panic.shiftRhsTooBig", 37518 .@"panic.invalidEnumValue", 37519 .@"panic.forLenMismatch", 37520 .@"panic.copyLenMismatch", 37521 .@"panic.memcpyAlias", 37522 .@"panic.noreturnReturned", 37523 => try pt.funcType(.{ 37524 .param_types = &.{}, 37525 .return_type = .noreturn_type, 37526 }), 37527 37528 else => unreachable, 37529 }; 37530 }