blob a2ed8116 (1641998B) - 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 mod: *Module, 9 /// Alias to `mod.gpa`. 10 gpa: Allocator, 11 /// Points to the temporary arena allocator of the Sema. 12 /// This arena will be cleared when the sema is destroyed. 13 arena: Allocator, 14 code: Zir, 15 air_instructions: std.MultiArrayList(Air.Inst) = .{}, 16 air_extra: std.ArrayListUnmanaged(u32) = .{}, 17 /// Maps ZIR to AIR. 18 inst_map: InstMap = .{}, 19 /// When analyzing an inline function call, owner_decl is the Decl of the caller 20 /// and `src_decl` of `Block` is the `Decl` of the callee. 21 /// This `Decl` owns the arena memory of this `Sema`. 22 owner_decl: *Decl, 23 owner_decl_index: InternPool.DeclIndex, 24 /// For an inline or comptime function call, this will be the root parent function 25 /// which contains the callsite. Corresponds to `owner_decl`. 26 /// This could be `none`, a `func_decl`, or a `func_instance`. 27 owner_func_index: InternPool.Index, 28 /// The function this ZIR code is the body of, according to the source code. 29 /// This starts out the same as `owner_func_index` and then diverges in the case of 30 /// an inline or comptime function call. 31 /// This could be `none`, a `func_decl`, or a `func_instance`. 32 func_index: InternPool.Index, 33 /// Whether the type of func_index has a calling convention of `.Naked`. 34 func_is_naked: bool, 35 /// Used to restore the error return trace when returning a non-error from a function. 36 error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none, 37 /// When semantic analysis needs to know the return type of the function whose body 38 /// is being analyzed, this `Type` should be used instead of going through `func`. 39 /// This will correctly handle the case of a comptime/inline function call of a 40 /// generic function which uses a type expression for the return type. 41 /// The type will be `void` in the case that `func` is `null`. 42 fn_ret_ty: Type, 43 /// In case of the return type being an error union with an inferred error 44 /// set, this is the inferred error set. `null` otherwise. Allocated with 45 /// `Sema.arena`. 46 fn_ret_ty_ies: ?*InferredErrorSet, 47 branch_quota: u32 = default_branch_quota, 48 branch_count: u32 = 0, 49 /// Populated when returning `error.ComptimeBreak`. Used to communicate the 50 /// break instruction up the stack to find the corresponding Block. 51 comptime_break_inst: Zir.Inst.Index = undefined, 52 /// This field is updated when a new source location becomes active, so that 53 /// instructions which do not have explicitly mapped source locations still have 54 /// access to the source location set by the previous instruction which did 55 /// contain a mapped source location. 56 src: LazySrcLoc = .{ .token_offset = 0 }, 57 decl_val_table: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Air.Inst.Ref) = .{}, 58 /// When doing a generic function instantiation, this array collects a value 59 /// for each parameter of the generic owner. `none` for non-comptime parameters. 60 /// This is a separate array from `block.params` so that it can be passed 61 /// directly to `comptime_args` when calling `InternPool.getFuncInstance`. 62 /// This memory is allocated by a parent `Sema` in the temporary arena, and is 63 /// used only to add a `func_instance` into the `InternPool`. 64 comptime_args: []InternPool.Index = &.{}, 65 /// Used to communicate from a generic function instantiation to the logic that 66 /// creates a generic function instantiation value in `funcCommon`. 67 generic_owner: InternPool.Index = .none, 68 /// When `generic_owner` is not none, this contains the generic function 69 /// instantiation callsite so that compile errors on the parameter types of the 70 /// instantiation can point back to the instantiation site in addition to the 71 /// declaration site. 72 generic_call_src: LazySrcLoc = .unneeded, 73 /// Corresponds to `generic_call_src`. 74 generic_call_decl: InternPool.OptionalDeclIndex = .none, 75 /// The key is types that must be fully resolved prior to machine code 76 /// generation pass. Types are added to this set when resolving them 77 /// immediately could cause a dependency loop, but they do need to be resolved 78 /// before machine code generation passes process the AIR. 79 /// It would work fine if this were an array list instead of an array hash map. 80 /// I chose array hash map with the intention to save time by omitting 81 /// duplicates. 82 types_to_resolve: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, 83 /// These are lazily created runtime blocks from block_inline instructions. 84 /// They are created when an break_inline passes through a runtime condition, because 85 /// Sema must convert comptime control flow to runtime control flow, which means 86 /// breaking from a block. 87 post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{}, 88 /// Populated with the last compile error created. 89 err: ?*Module.ErrorMsg = null, 90 /// Set to true when analyzing a func type instruction so that nested generic 91 /// function types will emit generic poison instead of a partial type. 92 no_partial_func_ty: bool = false, 93 94 /// The temporary arena is used for the memory of the `InferredAlloc` values 95 /// here so the values can be dropped without any cleanup. 96 unresolved_inferred_allocs: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .{}, 97 98 /// Indices of comptime-mutable decls created by this Sema. These decls' values 99 /// should be interned after analysis completes, as they may refer to memory in 100 /// the Sema arena. 101 /// TODO: this is a workaround for memory bugs triggered by the removal of 102 /// Decl.value_arena. A better solution needs to be found. Probably this will 103 /// involve transitioning comptime-mutable memory away from using Decls at all. 104 comptime_mutable_decls: *std.ArrayList(InternPool.DeclIndex), 105 106 /// This is populated when `@setAlignStack` occurs so that if there is a duplicate 107 /// one encountered, the conflicting source location can be shown. 108 prev_stack_alignment_src: ?LazySrcLoc = null, 109 110 /// While analyzing a type which has a special InternPool index, this is set to the index at which 111 /// the struct/enum/union type created should be placed. Otherwise, it is `.none`. 112 builtin_type_target_index: InternPool.Index = .none, 113 114 /// Links every pointer derived from a base `alloc` back to that `alloc`. Used 115 /// to detect comptime-known `const`s. 116 /// TODO: ZIR liveness analysis would allow us to remove elements from this map. 117 base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .{}, 118 119 /// Runtime `alloc`s are placed in this map to track all comptime-known writes 120 /// before the corresponding `make_ptr_const` instruction. 121 /// If any store to the alloc depends on a runtime condition or stores a runtime 122 /// value, the corresponding element in this map is erased, to indicate that the 123 /// alloc is not comptime-known. 124 /// If the alloc remains in this map when `make_ptr_const` is reached, its value 125 /// is comptime-known, and all stores to the pointer must be applied at comptime 126 /// to determine the comptime value. 127 /// Backed by gpa. 128 maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .{}, 129 130 const MaybeComptimeAlloc = struct { 131 /// The runtime index of the `alloc` instruction. 132 runtime_index: Value.RuntimeIndex, 133 /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to 134 /// RLS, a single comptime-known allocation may have arbitrarily many stores. 135 /// This may also contain `set_union_tag` instructions. 136 stores: std.ArrayListUnmanaged(Air.Inst.Index) = .{}, 137 /// Backed by sema.arena. Contains instructions such as `optional_payload_ptr_set` 138 /// which have side effects so will not be elided by Liveness: we must rewrite these 139 /// instructions to be nops instead of relying on Liveness. 140 non_elideable_pointers: std.ArrayListUnmanaged(Air.Inst.Index) = .{}, 141 }; 142 143 const std = @import("std"); 144 const math = std.math; 145 const mem = std.mem; 146 const Allocator = mem.Allocator; 147 const assert = std.debug.assert; 148 const log = std.log.scoped(.sema); 149 150 const Sema = @This(); 151 const Value = @import("value.zig").Value; 152 const Type = @import("type.zig").Type; 153 const TypedValue = @import("TypedValue.zig"); 154 const Air = @import("Air.zig"); 155 const Zir = @import("Zir.zig"); 156 const Module = @import("Module.zig"); 157 const trace = @import("tracy.zig").trace; 158 const Namespace = Module.Namespace; 159 const CompileError = Module.CompileError; 160 const SemaError = Module.SemaError; 161 const Decl = Module.Decl; 162 const CaptureScope = Module.CaptureScope; 163 const LazySrcLoc = Module.LazySrcLoc; 164 const RangeSet = @import("RangeSet.zig"); 165 const target_util = @import("target.zig"); 166 const Package = @import("Package.zig"); 167 const crash_report = @import("crash_report.zig"); 168 const build_options = @import("build_options"); 169 const Compilation = @import("Compilation.zig"); 170 const InternPool = @import("InternPool.zig"); 171 const Alignment = InternPool.Alignment; 172 173 pub const default_branch_quota = 1000; 174 pub const default_reference_trace_len = 2; 175 176 pub const InferredErrorSet = struct { 177 /// The function body from which this error set originates. 178 /// This is `none` in the case of a comptime/inline function call, corresponding to 179 /// `InternPool.Index.adhoc_inferred_error_set_type`. 180 /// The function's resolved error set is not set until analysis of the 181 /// function body completes. 182 func: InternPool.Index, 183 /// All currently known errors that this error set contains. This includes 184 /// direct additions via `return error.Foo;`, and possibly also errors that 185 /// are returned from any dependent functions. 186 errors: NameMap = .{}, 187 /// Other inferred error sets which this inferred error set should include. 188 inferred_error_sets: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, 189 /// The regular error set created by resolving this inferred error set. 190 resolved: InternPool.Index = .none, 191 192 pub const NameMap = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void); 193 194 pub fn addErrorSet( 195 self: *InferredErrorSet, 196 err_set_ty: Type, 197 ip: *InternPool, 198 arena: Allocator, 199 ) !void { 200 switch (err_set_ty.toIntern()) { 201 .anyerror_type => self.resolved = .anyerror_type, 202 .adhoc_inferred_error_set_type => {}, // Adding an inferred error set to itself. 203 204 else => switch (ip.indexToKey(err_set_ty.toIntern())) { 205 .error_set_type => |error_set_type| { 206 for (error_set_type.names.get(ip)) |name| { 207 try self.errors.put(arena, name, {}); 208 } 209 }, 210 .inferred_error_set_type => { 211 try self.inferred_error_sets.put(arena, err_set_ty.toIntern(), {}); 212 }, 213 else => unreachable, 214 }, 215 } 216 } 217 }; 218 219 /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve 220 /// instructions during analysis. 221 /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the 222 /// zir instruction index and a start offset. An index is not pressent in the map if the value 223 /// at the index is `Air.Inst.Ref.none`. 224 /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that 225 /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can 226 /// be called safely for any of the instructions passed in. 227 pub const InstMap = struct { 228 items: []Air.Inst.Ref = &[_]Air.Inst.Ref{}, 229 start: Zir.Inst.Index = @enumFromInt(0), 230 231 pub fn deinit(map: InstMap, allocator: mem.Allocator) void { 232 allocator.free(map.items); 233 } 234 235 pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref { 236 if (!map.contains(key)) return null; 237 return map.items[@intFromEnum(key) - @intFromEnum(map.start)]; 238 } 239 240 pub fn putAssumeCapacity( 241 map: *InstMap, 242 key: Zir.Inst.Index, 243 ref: Air.Inst.Ref, 244 ) void { 245 map.items[@intFromEnum(key) - @intFromEnum(map.start)] = ref; 246 } 247 248 pub fn putAssumeCapacityNoClobber( 249 map: *InstMap, 250 key: Zir.Inst.Index, 251 ref: Air.Inst.Ref, 252 ) void { 253 assert(!map.contains(key)); 254 map.putAssumeCapacity(key, ref); 255 } 256 257 pub const GetOrPutResult = struct { 258 value_ptr: *Air.Inst.Ref, 259 found_existing: bool, 260 }; 261 262 pub fn getOrPutAssumeCapacity( 263 map: *InstMap, 264 key: Zir.Inst.Index, 265 ) GetOrPutResult { 266 const index = @intFromEnum(key) - @intFromEnum(map.start); 267 return GetOrPutResult{ 268 .value_ptr = &map.items[index], 269 .found_existing = map.items[index] != .none, 270 }; 271 } 272 273 pub fn remove(map: InstMap, key: Zir.Inst.Index) bool { 274 if (!map.contains(key)) return false; 275 map.items[@intFromEnum(key) - @intFromEnum(map.start)] = .none; 276 return true; 277 } 278 279 pub fn contains(map: InstMap, key: Zir.Inst.Index) bool { 280 return map.items[@intFromEnum(key) - @intFromEnum(map.start)] != .none; 281 } 282 283 pub fn ensureSpaceForInstructions( 284 map: *InstMap, 285 allocator: mem.Allocator, 286 insts: []const Zir.Inst.Index, 287 ) !void { 288 const start, const end = mem.minMax(u32, @ptrCast(insts)); 289 const map_start = @intFromEnum(map.start); 290 if (map_start <= start and end < map.items.len + map_start) 291 return; 292 293 const old_start = if (map.items.len == 0) start else map_start; 294 var better_capacity = map.items.len; 295 var better_start = old_start; 296 while (true) { 297 const extra_capacity = better_capacity / 2 + 16; 298 better_capacity += extra_capacity; 299 better_start -|= @intCast(extra_capacity / 2); 300 if (better_start <= start and end < better_capacity + better_start) 301 break; 302 } 303 304 const start_diff = old_start - better_start; 305 const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity); 306 @memset(new_items[0..start_diff], .none); 307 @memcpy(new_items[start_diff..][0..map.items.len], map.items); 308 @memset(new_items[start_diff + map.items.len ..], .none); 309 310 allocator.free(map.items); 311 map.items = new_items; 312 map.start = @enumFromInt(better_start); 313 } 314 }; 315 316 /// This is the context needed to semantically analyze ZIR instructions and 317 /// produce AIR instructions. 318 /// This is a temporary structure stored on the stack; references to it are valid only 319 /// during semantic analysis of the block. 320 pub const Block = struct { 321 parent: ?*Block, 322 /// Shared among all child blocks. 323 sema: *Sema, 324 /// The namespace to use for lookups from this source block 325 /// When analyzing fields, this is different from src_decl.src_namespace. 326 namespace: InternPool.NamespaceIndex, 327 /// The AIR instructions generated for this block. 328 instructions: std.ArrayListUnmanaged(Air.Inst.Index), 329 // `param` instructions are collected here to be used by the `func` instruction. 330 /// When doing a generic function instantiation, this array collects a type 331 /// for each *runtime-known* parameter. This array corresponds to the instance 332 /// function type, while `Sema.comptime_args` corresponds to the generic owner 333 /// function type. 334 /// This memory is allocated by a parent `Sema` in the temporary arena, and is 335 /// used to add a `func_instance` into the `InternPool`. 336 params: std.MultiArrayList(Param) = .{}, 337 338 wip_capture_scope: CaptureScope.Index, 339 340 label: ?*Label = null, 341 inlining: ?*Inlining, 342 /// If runtime_index is not 0 then one of these is guaranteed to be non null. 343 runtime_cond: ?LazySrcLoc = null, 344 runtime_loop: ?LazySrcLoc = null, 345 /// This Decl is the Decl according to the Zig source code corresponding to this Block. 346 /// This can vary during inline or comptime function calls. See `Sema.owner_decl` 347 /// for the one that will be the same for all Block instances. 348 src_decl: InternPool.DeclIndex, 349 /// Non zero if a non-inline loop or a runtime conditional have been encountered. 350 /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index. 351 runtime_index: Value.RuntimeIndex = .zero, 352 inline_block: Zir.Inst.OptionalIndex = .none, 353 354 comptime_reason: ?*const ComptimeReason = null, 355 // TODO is_comptime and comptime_reason should probably be merged together. 356 is_comptime: bool, 357 is_typeof: bool = false, 358 359 /// Keep track of the active error return trace index around blocks so that we can correctly 360 /// pop the error trace upon block exit. 361 error_return_trace_index: Air.Inst.Ref = .none, 362 363 /// when null, it is determined by build mode, changed by @setRuntimeSafety 364 want_safety: ?bool = null, 365 366 /// What mode to generate float operations in, set by @setFloatMode 367 float_mode: std.builtin.FloatMode = .Strict, 368 369 c_import_buf: ?*std.ArrayList(u8) = null, 370 371 const ComptimeReason = union(enum) { 372 c_import: struct { 373 block: *Block, 374 src: LazySrcLoc, 375 }, 376 comptime_ret_ty: struct { 377 block: *Block, 378 func: Air.Inst.Ref, 379 func_src: LazySrcLoc, 380 return_ty: Type, 381 }, 382 383 fn explain(cr: ComptimeReason, sema: *Sema, msg: ?*Module.ErrorMsg) !void { 384 const parent = msg orelse return; 385 const mod = sema.mod; 386 const prefix = "expression is evaluated at comptime because "; 387 switch (cr) { 388 .c_import => |ci| { 389 try sema.errNote(ci.block, ci.src, parent, prefix ++ "it is inside a @cImport", .{}); 390 }, 391 .comptime_ret_ty => |rt| { 392 const src_loc = if (try sema.funcDeclSrc(rt.func)) |fn_decl| blk: { 393 var src_loc = fn_decl.srcLoc(mod); 394 src_loc.lazy = .{ .node_offset_fn_type_ret_ty = 0 }; 395 break :blk src_loc; 396 } else blk: { 397 const src_decl = mod.declPtr(rt.block.src_decl); 398 break :blk rt.func_src.toSrcLoc(src_decl, mod); 399 }; 400 if (rt.return_ty.isGenericPoison()) { 401 return mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{}); 402 } 403 try mod.errNoteNonLazy( 404 src_loc, 405 parent, 406 prefix ++ "the function returns a comptime-only type '{}'", 407 .{rt.return_ty.fmt(mod)}, 408 ); 409 try sema.explainWhyTypeIsComptime(parent, src_loc, rt.return_ty); 410 }, 411 } 412 } 413 }; 414 415 const Param = struct { 416 /// `none` means `anytype`. 417 ty: InternPool.Index, 418 is_comptime: bool, 419 name: Zir.NullTerminatedString, 420 }; 421 422 /// This `Block` maps a block ZIR instruction to the corresponding 423 /// AIR instruction for break instruction analysis. 424 pub const Label = struct { 425 zir_block: Zir.Inst.Index, 426 merges: Merges, 427 }; 428 429 /// This `Block` indicates that an inline function call is happening 430 /// and return instructions should be analyzed as a break instruction 431 /// to this AIR block instruction. 432 /// It is shared among all the blocks in an inline or comptime called 433 /// function. 434 pub const Inlining = struct { 435 call_block: *Block, 436 call_src: LazySrcLoc, 437 has_comptime_args: bool, 438 func: InternPool.Index, 439 comptime_result: Air.Inst.Ref, 440 merges: Merges, 441 }; 442 443 pub const Merges = struct { 444 block_inst: Air.Inst.Index, 445 /// Separate array list from break_inst_list so that it can be passed directly 446 /// to resolvePeerTypes. 447 results: std.ArrayListUnmanaged(Air.Inst.Ref), 448 /// Keeps track of the break instructions so that the operand can be replaced 449 /// if we need to add type coercion at the end of block analysis. 450 /// Same indexes, capacity, length as `results`. 451 br_list: std.ArrayListUnmanaged(Air.Inst.Index), 452 /// Keeps the source location of the rhs operand of the break instruction, 453 /// to enable more precise compile errors. 454 /// Same indexes, capacity, length as `results`. 455 src_locs: std.ArrayListUnmanaged(?LazySrcLoc), 456 457 pub fn deinit(merges: *@This(), allocator: mem.Allocator) void { 458 merges.results.deinit(allocator); 459 merges.br_list.deinit(allocator); 460 merges.src_locs.deinit(allocator); 461 } 462 }; 463 464 /// For debugging purposes. 465 pub fn dump(block: *Block, mod: Module) void { 466 Zir.dumpBlock(mod, block); 467 } 468 469 pub fn makeSubBlock(parent: *Block) Block { 470 return .{ 471 .parent = parent, 472 .sema = parent.sema, 473 .src_decl = parent.src_decl, 474 .namespace = parent.namespace, 475 .instructions = .{}, 476 .wip_capture_scope = parent.wip_capture_scope, 477 .label = null, 478 .inlining = parent.inlining, 479 .is_comptime = parent.is_comptime, 480 .comptime_reason = parent.comptime_reason, 481 .is_typeof = parent.is_typeof, 482 .runtime_cond = parent.runtime_cond, 483 .runtime_loop = parent.runtime_loop, 484 .runtime_index = parent.runtime_index, 485 .want_safety = parent.want_safety, 486 .float_mode = parent.float_mode, 487 .c_import_buf = parent.c_import_buf, 488 .error_return_trace_index = parent.error_return_trace_index, 489 }; 490 } 491 492 pub fn wantSafety(block: *const Block) bool { 493 return block.want_safety orelse switch (block.sema.mod.optimizeMode()) { 494 .Debug => true, 495 .ReleaseSafe => true, 496 .ReleaseFast => false, 497 .ReleaseSmall => false, 498 }; 499 } 500 501 pub fn getFileScope(block: *Block, mod: *Module) *Module.File { 502 return mod.namespacePtr(block.namespace).file_scope; 503 } 504 505 fn addTy( 506 block: *Block, 507 tag: Air.Inst.Tag, 508 ty: Type, 509 ) error{OutOfMemory}!Air.Inst.Ref { 510 return block.addInst(.{ 511 .tag = tag, 512 .data = .{ .ty = ty }, 513 }); 514 } 515 516 fn addTyOp( 517 block: *Block, 518 tag: Air.Inst.Tag, 519 ty: Type, 520 operand: Air.Inst.Ref, 521 ) error{OutOfMemory}!Air.Inst.Ref { 522 return block.addInst(.{ 523 .tag = tag, 524 .data = .{ .ty_op = .{ 525 .ty = Air.internedToRef(ty.toIntern()), 526 .operand = operand, 527 } }, 528 }); 529 } 530 531 fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref { 532 return block.addInst(.{ 533 .tag = .bitcast, 534 .data = .{ .ty_op = .{ 535 .ty = Air.internedToRef(ty.toIntern()), 536 .operand = operand, 537 } }, 538 }); 539 } 540 541 fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref { 542 return block.addInst(.{ 543 .tag = tag, 544 .data = .{ .no_op = {} }, 545 }); 546 } 547 548 fn addUnOp( 549 block: *Block, 550 tag: Air.Inst.Tag, 551 operand: Air.Inst.Ref, 552 ) error{OutOfMemory}!Air.Inst.Ref { 553 return block.addInst(.{ 554 .tag = tag, 555 .data = .{ .un_op = operand }, 556 }); 557 } 558 559 fn addBr( 560 block: *Block, 561 target_block: Air.Inst.Index, 562 operand: Air.Inst.Ref, 563 ) error{OutOfMemory}!Air.Inst.Ref { 564 return block.addInst(.{ 565 .tag = .br, 566 .data = .{ .br = .{ 567 .block_inst = target_block, 568 .operand = operand, 569 } }, 570 }); 571 } 572 573 fn addBinOp( 574 block: *Block, 575 tag: Air.Inst.Tag, 576 lhs: Air.Inst.Ref, 577 rhs: Air.Inst.Ref, 578 ) error{OutOfMemory}!Air.Inst.Ref { 579 return block.addInst(.{ 580 .tag = tag, 581 .data = .{ .bin_op = .{ 582 .lhs = lhs, 583 .rhs = rhs, 584 } }, 585 }); 586 } 587 588 fn addStructFieldPtr( 589 block: *Block, 590 struct_ptr: Air.Inst.Ref, 591 field_index: u32, 592 ptr_field_ty: Type, 593 ) !Air.Inst.Ref { 594 const ty = Air.internedToRef(ptr_field_ty.toIntern()); 595 const tag: Air.Inst.Tag = switch (field_index) { 596 0 => .struct_field_ptr_index_0, 597 1 => .struct_field_ptr_index_1, 598 2 => .struct_field_ptr_index_2, 599 3 => .struct_field_ptr_index_3, 600 else => { 601 return block.addInst(.{ 602 .tag = .struct_field_ptr, 603 .data = .{ .ty_pl = .{ 604 .ty = ty, 605 .payload = try block.sema.addExtra(Air.StructField{ 606 .struct_operand = struct_ptr, 607 .field_index = field_index, 608 }), 609 } }, 610 }); 611 }, 612 }; 613 return block.addInst(.{ 614 .tag = tag, 615 .data = .{ .ty_op = .{ 616 .ty = ty, 617 .operand = struct_ptr, 618 } }, 619 }); 620 } 621 622 fn addStructFieldVal( 623 block: *Block, 624 struct_val: Air.Inst.Ref, 625 field_index: u32, 626 field_ty: Type, 627 ) !Air.Inst.Ref { 628 return block.addInst(.{ 629 .tag = .struct_field_val, 630 .data = .{ .ty_pl = .{ 631 .ty = Air.internedToRef(field_ty.toIntern()), 632 .payload = try block.sema.addExtra(Air.StructField{ 633 .struct_operand = struct_val, 634 .field_index = field_index, 635 }), 636 } }, 637 }); 638 } 639 640 fn addSliceElemPtr( 641 block: *Block, 642 slice: Air.Inst.Ref, 643 elem_index: Air.Inst.Ref, 644 elem_ptr_ty: Type, 645 ) !Air.Inst.Ref { 646 return block.addInst(.{ 647 .tag = .slice_elem_ptr, 648 .data = .{ .ty_pl = .{ 649 .ty = Air.internedToRef(elem_ptr_ty.toIntern()), 650 .payload = try block.sema.addExtra(Air.Bin{ 651 .lhs = slice, 652 .rhs = elem_index, 653 }), 654 } }, 655 }); 656 } 657 658 fn addPtrElemPtr( 659 block: *Block, 660 array_ptr: Air.Inst.Ref, 661 elem_index: Air.Inst.Ref, 662 elem_ptr_ty: Type, 663 ) !Air.Inst.Ref { 664 const ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 665 return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref); 666 } 667 668 fn addPtrElemPtrTypeRef( 669 block: *Block, 670 array_ptr: Air.Inst.Ref, 671 elem_index: Air.Inst.Ref, 672 elem_ptr_ty: Air.Inst.Ref, 673 ) !Air.Inst.Ref { 674 return block.addInst(.{ 675 .tag = .ptr_elem_ptr, 676 .data = .{ .ty_pl = .{ 677 .ty = elem_ptr_ty, 678 .payload = try block.sema.addExtra(Air.Bin{ 679 .lhs = array_ptr, 680 .rhs = elem_index, 681 }), 682 } }, 683 }); 684 } 685 686 fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref { 687 const sema = block.sema; 688 const mod = sema.mod; 689 return block.addInst(.{ 690 .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector, 691 .data = .{ .ty_pl = .{ 692 .ty = Air.internedToRef((try mod.vectorType(.{ 693 .len = sema.typeOf(lhs).vectorLen(mod), 694 .child = .bool_type, 695 })).toIntern()), 696 .payload = try sema.addExtra(Air.VectorCmp{ 697 .lhs = lhs, 698 .rhs = rhs, 699 .op = Air.VectorCmp.encodeOp(cmp_op), 700 }), 701 } }, 702 }); 703 } 704 705 fn addAggregateInit( 706 block: *Block, 707 aggregate_ty: Type, 708 elements: []const Air.Inst.Ref, 709 ) !Air.Inst.Ref { 710 const sema = block.sema; 711 const ty_ref = Air.internedToRef(aggregate_ty.toIntern()); 712 try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len); 713 const extra_index: u32 = @intCast(sema.air_extra.items.len); 714 sema.appendRefsAssumeCapacity(elements); 715 716 return block.addInst(.{ 717 .tag = .aggregate_init, 718 .data = .{ .ty_pl = .{ 719 .ty = ty_ref, 720 .payload = extra_index, 721 } }, 722 }); 723 } 724 725 fn addUnionInit( 726 block: *Block, 727 union_ty: Type, 728 field_index: u32, 729 init: Air.Inst.Ref, 730 ) !Air.Inst.Ref { 731 return block.addInst(.{ 732 .tag = .union_init, 733 .data = .{ .ty_pl = .{ 734 .ty = Air.internedToRef(union_ty.toIntern()), 735 .payload = try block.sema.addExtra(Air.UnionInit{ 736 .field_index = field_index, 737 .init = init, 738 }), 739 } }, 740 }); 741 } 742 743 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { 744 return (try block.addInstAsIndex(inst)).toRef(); 745 } 746 747 pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { 748 const sema = block.sema; 749 const gpa = sema.gpa; 750 751 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 752 try block.instructions.ensureUnusedCapacity(gpa, 1); 753 754 const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 755 sema.air_instructions.appendAssumeCapacity(inst); 756 block.instructions.appendAssumeCapacity(result_index); 757 return result_index; 758 } 759 760 /// Insert an instruction into the block at `index`. Moves all following 761 /// instructions forward in the block to make room. Operation is O(N). 762 pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { 763 return (try block.insertInstAsIndex(index, inst)).toRef(); 764 } 765 766 pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { 767 const sema = block.sema; 768 const gpa = sema.gpa; 769 770 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 771 772 const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 773 sema.air_instructions.appendAssumeCapacity(inst); 774 775 try block.instructions.insert(gpa, @intFromEnum(index), result_index); 776 return result_index; 777 } 778 779 fn addUnreachable(block: *Block, src: LazySrcLoc, safety_check: bool) !void { 780 if (safety_check and block.wantSafety()) { 781 try block.sema.safetyPanic(block, src, .unreach); 782 } else { 783 _ = try block.addNoOp(.unreach); 784 } 785 } 786 787 pub fn ownerModule(block: Block) *Package.Module { 788 const zcu = block.sema.mod; 789 return zcu.namespacePtr(block.namespace).file_scope.mod; 790 } 791 792 pub fn startAnonDecl(block: *Block) !WipAnonDecl { 793 return WipAnonDecl{ 794 .block = block, 795 .finished = false, 796 }; 797 } 798 799 pub const WipAnonDecl = struct { 800 block: *Block, 801 finished: bool, 802 803 pub fn deinit(wad: *WipAnonDecl) void { 804 wad.* = undefined; 805 } 806 807 /// `alignment` value of 0 means to use ABI alignment. 808 pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: Alignment) !InternPool.DeclIndex { 809 const sema = wad.block.sema; 810 // Do this ahead of time because `createAnonymousDecl` depends on calling 811 // `type.hasRuntimeBits()`. 812 _ = try sema.typeHasRuntimeBits(ty); 813 const new_decl_index = try sema.mod.createAnonymousDecl(wad.block, .{ 814 .ty = ty, 815 .val = val, 816 }); 817 const new_decl = sema.mod.declPtr(new_decl_index); 818 new_decl.alignment = alignment; 819 errdefer sema.mod.abortAnonDecl(new_decl_index); 820 wad.finished = true; 821 try sema.mod.finalizeAnonDecl(new_decl_index); 822 return new_decl_index; 823 } 824 }; 825 }; 826 827 const LabeledBlock = struct { 828 block: Block, 829 label: Block.Label, 830 831 fn destroy(lb: *LabeledBlock, gpa: Allocator) void { 832 lb.block.instructions.deinit(gpa); 833 lb.label.merges.deinit(gpa); 834 gpa.destroy(lb); 835 } 836 }; 837 838 /// The value stored in the inferred allocation. This will go into 839 /// peer type resolution. This is stored in a separate list so that 840 /// the items are contiguous in memory and thus can be passed to 841 /// `Module.resolvePeerTypes`. 842 const InferredAlloc = struct { 843 /// The placeholder `store` instructions used before the result pointer type 844 /// is known. These should be rewritten to perform any required coercions 845 /// when the type is resolved. 846 /// Allocated from `sema.arena`. 847 prongs: std.ArrayListUnmanaged(Air.Inst.Index) = .{}, 848 }; 849 850 const NeededComptimeReason = struct { 851 needed_comptime_reason: []const u8, 852 block_comptime_reason: ?*const Block.ComptimeReason = null, 853 }; 854 855 pub fn deinit(sema: *Sema) void { 856 const gpa = sema.gpa; 857 sema.air_instructions.deinit(gpa); 858 sema.air_extra.deinit(gpa); 859 sema.inst_map.deinit(gpa); 860 sema.decl_val_table.deinit(gpa); 861 sema.types_to_resolve.deinit(gpa); 862 { 863 var it = sema.post_hoc_blocks.iterator(); 864 while (it.next()) |entry| { 865 const labeled_block = entry.value_ptr.*; 866 labeled_block.destroy(gpa); 867 } 868 sema.post_hoc_blocks.deinit(gpa); 869 } 870 sema.unresolved_inferred_allocs.deinit(gpa); 871 sema.base_allocs.deinit(gpa); 872 sema.maybe_comptime_allocs.deinit(gpa); 873 sema.* = undefined; 874 } 875 876 /// Returns only the result from the body that is specified. 877 /// Only appropriate to call when it is determined at comptime that this body 878 /// has no peers. 879 fn resolveBody( 880 sema: *Sema, 881 block: *Block, 882 body: []const Zir.Inst.Index, 883 /// This is the instruction that a break instruction within `body` can 884 /// use to return from the body. 885 body_inst: Zir.Inst.Index, 886 ) CompileError!Air.Inst.Ref { 887 const break_data = (try sema.analyzeBodyBreak(block, body)) orelse 888 return .unreachable_value; 889 // For comptime control flow, we need to detect when `analyzeBody` reports 890 // that we need to break from an outer block. In such case we 891 // use Zig's error mechanism to send control flow up the stack until 892 // we find the corresponding block to this break. 893 if (block.is_comptime and break_data.block_inst != body_inst) { 894 sema.comptime_break_inst = break_data.inst; 895 return error.ComptimeBreak; 896 } 897 return try sema.resolveInst(break_data.operand); 898 } 899 900 fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void { 901 _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) { 902 error.ComptimeBreak => { 903 const zir_datas = sema.code.instructions.items(.data); 904 const break_data = zir_datas[@intFromEnum(sema.comptime_break_inst)].@"break"; 905 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 906 try sema.addRuntimeBreak(block, .{ 907 .block_inst = extra.block_inst, 908 .operand = break_data.operand, 909 .inst = sema.comptime_break_inst, 910 }); 911 }, 912 else => |e| return e, 913 }; 914 } 915 916 pub fn analyzeBody( 917 sema: *Sema, 918 block: *Block, 919 body: []const Zir.Inst.Index, 920 ) !void { 921 _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) { 922 error.ComptimeBreak => unreachable, // unexpected comptime control flow 923 else => |e| return e, 924 }; 925 } 926 927 const BreakData = struct { 928 block_inst: Zir.Inst.Index, 929 operand: Zir.Inst.Ref, 930 inst: Zir.Inst.Index, 931 }; 932 933 pub fn analyzeBodyBreak( 934 sema: *Sema, 935 block: *Block, 936 body: []const Zir.Inst.Index, 937 ) CompileError!?BreakData { 938 const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) { 939 error.ComptimeBreak => sema.comptime_break_inst, 940 else => |e| return e, 941 }; 942 if (block.instructions.items.len != 0 and 943 sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef())) 944 return null; 945 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; 946 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 947 return BreakData{ 948 .block_inst = extra.block_inst, 949 .operand = break_data.operand, 950 .inst = break_inst, 951 }; 952 } 953 954 /// ZIR instructions which are always `noreturn` return this. This matches the 955 /// return type of `analyzeBody` so that we can tail call them. 956 /// Only appropriate to return when the instruction is known to be NoReturn 957 /// solely based on the ZIR tag. 958 const always_noreturn: CompileError!Zir.Inst.Index = @as(Zir.Inst.Index, undefined); 959 960 /// This function is the main loop of `Sema` and it can be used in two different ways: 961 /// * The traditional way where there are N breaks out of the block and peer type 962 /// resolution is done on the break operands. In this case, the `Zir.Inst.Index` 963 /// part of the return value will be `undefined`, and callsites should ignore it, 964 /// finding the block result value via the block scope. 965 /// * The "flat" way. There is only 1 break out of the block, and it is with a `break_inline` 966 /// instruction. In this case, the `Zir.Inst.Index` part of the return value will be 967 /// the break instruction. This communicates both which block the break applies to, as 968 /// well as the operand. No block scope needs to be created for this strategy. 969 fn analyzeBodyInner( 970 sema: *Sema, 971 block: *Block, 972 body: []const Zir.Inst.Index, 973 ) CompileError!Zir.Inst.Index { 974 // No tracy calls here, to avoid interfering with the tail call mechanism. 975 976 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); 977 978 // Most of the time, we don't need to construct a new capture scope for a 979 // block. However, successive iterations of comptime loops can capture 980 // different values for the same Zir.Inst.Index, so in those cases, we will 981 // have to create nested capture scopes; see the `.repeat` case below. 982 const parent_capture_scope = block.wip_capture_scope; 983 984 const mod = sema.mod; 985 const map = &sema.inst_map; 986 const tags = sema.code.instructions.items(.tag); 987 const datas = sema.code.instructions.items(.data); 988 989 var crash_info = crash_report.prepAnalyzeBody(sema, block, body); 990 crash_info.push(); 991 defer crash_info.pop(); 992 993 var dbg_block_begins: u32 = 0; 994 995 // We use a while (true) loop here to avoid a redundant way of breaking out of 996 // the loop. The only way to break out of the loop is with a `noreturn` 997 // instruction. 998 var i: u32 = 0; 999 const result = while (true) { 1000 crash_info.setBodyIndex(i); 1001 const inst = body[i]; 1002 std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ 1003 mod.namespacePtr(mod.declPtr(block.src_decl).src_namespace).file_scope.sub_file_path, inst, 1004 }); 1005 const air_inst: Air.Inst.Ref = switch (tags[@intFromEnum(inst)]) { 1006 // zig fmt: off 1007 .alloc => try sema.zirAlloc(block, inst), 1008 .alloc_inferred => try sema.zirAllocInferred(block, inst, true), 1009 .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, false), 1010 .alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst, true), 1011 .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(inst, false), 1012 .alloc_mut => try sema.zirAllocMut(block, inst), 1013 .alloc_comptime_mut => try sema.zirAllocComptime(block, inst), 1014 .make_ptr_const => try sema.zirMakePtrConst(block, inst), 1015 .anyframe_type => try sema.zirAnyframeType(block, inst), 1016 .array_cat => try sema.zirArrayCat(block, inst), 1017 .array_mul => try sema.zirArrayMul(block, inst), 1018 .array_type => try sema.zirArrayType(block, inst), 1019 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), 1020 .vector_type => try sema.zirVectorType(block, inst), 1021 .as => try sema.zirAs(block, inst), 1022 .as_node => try sema.zirAsNode(block, inst), 1023 .as_shift_operand => try sema.zirAsShiftOperand(block, inst), 1024 .bit_and => try sema.zirBitwise(block, inst, .bit_and), 1025 .bit_not => try sema.zirBitNot(block, inst), 1026 .bit_or => try sema.zirBitwise(block, inst, .bit_or), 1027 .bitcast => try sema.zirBitcast(block, inst), 1028 .suspend_block => try sema.zirSuspendBlock(block, inst), 1029 .bool_not => try sema.zirBoolNot(block, inst), 1030 .bool_br_and => try sema.zirBoolBr(block, inst, false), 1031 .bool_br_or => try sema.zirBoolBr(block, inst, true), 1032 .c_import => try sema.zirCImport(block, inst), 1033 .call => try sema.zirCall(block, inst, .direct), 1034 .field_call => try sema.zirCall(block, inst, .field), 1035 .closure_get => try sema.zirClosureGet(block, inst), 1036 .cmp_lt => try sema.zirCmp(block, inst, .lt), 1037 .cmp_lte => try sema.zirCmp(block, inst, .lte), 1038 .cmp_eq => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .Optimized)), 1039 .cmp_gte => try sema.zirCmp(block, inst, .gte), 1040 .cmp_gt => try sema.zirCmp(block, inst, .gt), 1041 .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .Optimized)), 1042 .decl_ref => try sema.zirDeclRef(block, inst), 1043 .decl_val => try sema.zirDeclVal(block, inst), 1044 .load => try sema.zirLoad(block, inst), 1045 .elem_ptr => try sema.zirElemPtr(block, inst), 1046 .elem_ptr_node => try sema.zirElemPtrNode(block, inst), 1047 .elem_val => try sema.zirElemVal(block, inst), 1048 .elem_val_node => try sema.zirElemValNode(block, inst), 1049 .elem_val_imm => try sema.zirElemValImm(block, inst), 1050 .elem_type => try sema.zirElemType(block, inst), 1051 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), 1052 .vector_elem_type => try sema.zirVectorElemType(block, inst), 1053 .enum_literal => try sema.zirEnumLiteral(block, inst), 1054 .int_from_enum => try sema.zirIntFromEnum(block, inst), 1055 .enum_from_int => try sema.zirEnumFromInt(block, inst), 1056 .err_union_code => try sema.zirErrUnionCode(block, inst), 1057 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), 1058 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst), 1059 .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst), 1060 .error_union_type => try sema.zirErrorUnionType(block, inst), 1061 .error_value => try sema.zirErrorValue(block, inst), 1062 .field_ptr => try sema.zirFieldPtr(block, inst), 1063 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), 1064 .field_val => try sema.zirFieldVal(block, inst), 1065 .field_val_named => try sema.zirFieldValNamed(block, inst), 1066 .func => try sema.zirFunc(block, inst, false), 1067 .func_inferred => try sema.zirFunc(block, inst, true), 1068 .func_fancy => try sema.zirFuncFancy(block, inst), 1069 .import => try sema.zirImport(block, inst), 1070 .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), 1071 .int => try sema.zirInt(block, inst), 1072 .int_big => try sema.zirIntBig(block, inst), 1073 .float => try sema.zirFloat(block, inst), 1074 .float128 => try sema.zirFloat128(block, inst), 1075 .int_type => try sema.zirIntType(inst), 1076 .is_non_err => try sema.zirIsNonErr(block, inst), 1077 .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), 1078 .ret_is_non_err => try sema.zirRetIsNonErr(block, inst), 1079 .is_non_null => try sema.zirIsNonNull(block, inst), 1080 .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), 1081 .merge_error_sets => try sema.zirMergeErrorSets(block, inst), 1082 .negate => try sema.zirNegate(block, inst), 1083 .negate_wrap => try sema.zirNegateWrap(block, inst), 1084 .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), 1085 .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), 1086 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), 1087 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), 1088 .optional_type => try sema.zirOptionalType(block, inst), 1089 .ptr_type => try sema.zirPtrType(block, inst), 1090 .ref => try sema.zirRef(block, inst), 1091 .ret_err_value_code => try sema.zirRetErrValueCode(inst), 1092 .shr => try sema.zirShr(block, inst, .shr), 1093 .shr_exact => try sema.zirShr(block, inst, .shr_exact), 1094 .slice_end => try sema.zirSliceEnd(block, inst), 1095 .slice_sentinel => try sema.zirSliceSentinel(block, inst), 1096 .slice_start => try sema.zirSliceStart(block, inst), 1097 .slice_length => try sema.zirSliceLength(block, inst), 1098 .str => try sema.zirStr(inst), 1099 .switch_block => try sema.zirSwitchBlock(block, inst, false), 1100 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), 1101 .type_info => try sema.zirTypeInfo(block, inst), 1102 .size_of => try sema.zirSizeOf(block, inst), 1103 .bit_size_of => try sema.zirBitSizeOf(block, inst), 1104 .typeof => try sema.zirTypeof(block, inst), 1105 .typeof_builtin => try sema.zirTypeofBuiltin(block, inst), 1106 .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), 1107 .xor => try sema.zirBitwise(block, inst, .xor), 1108 .struct_init_empty => try sema.zirStructInitEmpty(block, inst), 1109 .struct_init_empty_result => try sema.zirStructInitEmptyResult(block, inst, false), 1110 .struct_init_empty_ref_result => try sema.zirStructInitEmptyResult(block, inst, true), 1111 .struct_init_anon => try sema.zirStructInitAnon(block, inst), 1112 .struct_init => try sema.zirStructInit(block, inst, false), 1113 .struct_init_ref => try sema.zirStructInit(block, inst, true), 1114 .struct_init_field_type => try sema.zirStructInitFieldType(block, inst), 1115 .struct_init_field_ptr => try sema.zirStructInitFieldPtr(block, inst), 1116 .array_init_anon => try sema.zirArrayInitAnon(block, inst), 1117 .array_init => try sema.zirArrayInit(block, inst, false), 1118 .array_init_ref => try sema.zirArrayInit(block, inst, true), 1119 .array_init_elem_type => try sema.zirArrayInitElemType(block, inst), 1120 .array_init_elem_ptr => try sema.zirArrayInitElemPtr(block, inst), 1121 .union_init => try sema.zirUnionInit(block, inst), 1122 .field_type_ref => try sema.zirFieldTypeRef(block, inst), 1123 .int_from_ptr => try sema.zirIntFromPtr(block, inst), 1124 .align_of => try sema.zirAlignOf(block, inst), 1125 .int_from_bool => try sema.zirIntFromBool(block, inst), 1126 .embed_file => try sema.zirEmbedFile(block, inst), 1127 .error_name => try sema.zirErrorName(block, inst), 1128 .tag_name => try sema.zirTagName(block, inst), 1129 .type_name => try sema.zirTypeName(block, inst), 1130 .frame_type => try sema.zirFrameType(block, inst), 1131 .frame_size => try sema.zirFrameSize(block, inst), 1132 .int_from_float => try sema.zirIntFromFloat(block, inst), 1133 .float_from_int => try sema.zirFloatFromInt(block, inst), 1134 .ptr_from_int => try sema.zirPtrFromInt(block, inst), 1135 .float_cast => try sema.zirFloatCast(block, inst), 1136 .int_cast => try sema.zirIntCast(block, inst), 1137 .ptr_cast => try sema.zirPtrCast(block, inst), 1138 .truncate => try sema.zirTruncate(block, inst), 1139 .has_decl => try sema.zirHasDecl(block, inst), 1140 .has_field => try sema.zirHasField(block, inst), 1141 .byte_swap => try sema.zirByteSwap(block, inst), 1142 .bit_reverse => try sema.zirBitReverse(block, inst), 1143 .bit_offset_of => try sema.zirBitOffsetOf(block, inst), 1144 .offset_of => try sema.zirOffsetOf(block, inst), 1145 .splat => try sema.zirSplat(block, inst), 1146 .reduce => try sema.zirReduce(block, inst), 1147 .shuffle => try sema.zirShuffle(block, inst), 1148 .atomic_load => try sema.zirAtomicLoad(block, inst), 1149 .atomic_rmw => try sema.zirAtomicRmw(block, inst), 1150 .mul_add => try sema.zirMulAdd(block, inst), 1151 .builtin_call => try sema.zirBuiltinCall(block, inst), 1152 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), 1153 .@"resume" => try sema.zirResume(block, inst), 1154 .@"await" => try sema.zirAwait(block, inst), 1155 .for_len => try sema.zirForLen(block, inst), 1156 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), 1157 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), 1158 .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst), 1159 1160 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), 1161 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), 1162 .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount), 1163 .abs => try sema.zirAbs(block, inst), 1164 1165 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), 1166 .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), 1167 .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos), 1168 .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan), 1169 .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp), 1170 .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2), 1171 .log => try sema.zirUnaryMath(block, inst, .log, Value.log), 1172 .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2), 1173 .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10), 1174 .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor), 1175 .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil), 1176 .round => try sema.zirUnaryMath(block, inst, .round, Value.round), 1177 .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc), 1178 1179 .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), 1180 .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), 1181 .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), 1182 1183 .add => try sema.zirArithmetic(block, inst, .add, true), 1184 .addwrap => try sema.zirArithmetic(block, inst, .addwrap, true), 1185 .add_sat => try sema.zirArithmetic(block, inst, .add_sat, true), 1186 .add_unsafe => try sema.zirArithmetic(block, inst, .add_unsafe, false), 1187 .mul => try sema.zirArithmetic(block, inst, .mul, true), 1188 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap, true), 1189 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat, true), 1190 .sub => try sema.zirArithmetic(block, inst, .sub, true), 1191 .subwrap => try sema.zirArithmetic(block, inst, .subwrap, true), 1192 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat, true), 1193 1194 .div => try sema.zirDiv(block, inst), 1195 .div_exact => try sema.zirDivExact(block, inst), 1196 .div_floor => try sema.zirDivFloor(block, inst), 1197 .div_trunc => try sema.zirDivTrunc(block, inst), 1198 1199 .mod_rem => try sema.zirModRem(block, inst), 1200 .mod => try sema.zirMod(block, inst), 1201 .rem => try sema.zirRem(block, inst), 1202 1203 .max => try sema.zirMinMax(block, inst, .max), 1204 .min => try sema.zirMinMax(block, inst, .min), 1205 1206 .shl => try sema.zirShl(block, inst, .shl), 1207 .shl_exact => try sema.zirShl(block, inst, .shl_exact), 1208 .shl_sat => try sema.zirShl(block, inst, .shl_sat), 1209 1210 .ret_ptr => try sema.zirRetPtr(block), 1211 .ret_type => Air.internedToRef(sema.fn_ret_ty.toIntern()), 1212 1213 // Instructions that we know to *always* be noreturn based solely on their tag. 1214 // These functions match the return type of analyzeBody so that we can 1215 // tail call them here. 1216 .compile_error => break sema.zirCompileError(block, inst), 1217 .ret_implicit => break sema.zirRetImplicit(block, inst), 1218 .ret_node => break sema.zirRetNode(block, inst), 1219 .ret_load => break sema.zirRetLoad(block, inst), 1220 .ret_err_value => break sema.zirRetErrValue(block, inst), 1221 .@"unreachable" => break sema.zirUnreachable(block, inst), 1222 .panic => break sema.zirPanic(block, inst), 1223 .trap => break sema.zirTrap(block, inst), 1224 // zig fmt: on 1225 1226 .extended => ext: { 1227 const extended = datas[@intFromEnum(inst)].extended; 1228 break :ext switch (extended.opcode) { 1229 // zig fmt: off 1230 .variable => try sema.zirVarExtended( block, extended), 1231 .struct_decl => try sema.zirStructDecl( block, extended, inst), 1232 .enum_decl => try sema.zirEnumDecl( block, extended, inst), 1233 .union_decl => try sema.zirUnionDecl( block, extended, inst), 1234 .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst), 1235 .this => try sema.zirThis( block, extended), 1236 .ret_addr => try sema.zirRetAddr( block, extended), 1237 .builtin_src => try sema.zirBuiltinSrc( block, extended), 1238 .error_return_trace => try sema.zirErrorReturnTrace( block), 1239 .frame => try sema.zirFrame( block, extended), 1240 .frame_address => try sema.zirFrameAddress( block, extended), 1241 .alloc => try sema.zirAllocExtended( block, extended), 1242 .builtin_extern => try sema.zirBuiltinExtern( block, extended), 1243 .@"asm" => try sema.zirAsm( block, extended, false), 1244 .asm_expr => try sema.zirAsm( block, extended, true), 1245 .typeof_peer => try sema.zirTypeofPeer( block, extended), 1246 .compile_log => try sema.zirCompileLog( extended), 1247 .min_multi => try sema.zirMinMaxMulti( block, extended, .min), 1248 .max_multi => try sema.zirMinMaxMulti( block, extended, .max), 1249 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1250 .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1251 .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1252 .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1253 .c_undef => try sema.zirCUndef( block, extended), 1254 .c_include => try sema.zirCInclude( block, extended), 1255 .c_define => try sema.zirCDefine( block, extended), 1256 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), 1257 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), 1258 .prefetch => try sema.zirPrefetch( block, extended), 1259 .error_cast => try sema.zirErrorCast( block, extended), 1260 .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), 1261 .select => try sema.zirSelect( block, extended), 1262 .int_from_error => try sema.zirIntFromError( block, extended), 1263 .error_from_int => try sema.zirErrorFromInt( block, extended), 1264 .reify => try sema.zirReify( block, extended, inst), 1265 .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended), 1266 .cmpxchg => try sema.zirCmpxchg( block, extended), 1267 .c_va_arg => try sema.zirCVaArg( block, extended), 1268 .c_va_copy => try sema.zirCVaCopy( block, extended), 1269 .c_va_end => try sema.zirCVaEnd( block, extended), 1270 .c_va_start => try sema.zirCVaStart( block, extended), 1271 .ptr_cast_full => try sema.zirPtrCastFull( block, extended), 1272 .ptr_cast_no_dest => try sema.zirPtrCastNoDest( block, extended), 1273 .work_item_id => try sema.zirWorkItem( block, extended, extended.opcode), 1274 .work_group_size => try sema.zirWorkItem( block, extended, extended.opcode), 1275 .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode), 1276 .in_comptime => try sema.zirInComptime( block), 1277 // zig fmt: on 1278 1279 .fence => { 1280 try sema.zirFence(block, extended); 1281 i += 1; 1282 continue; 1283 }, 1284 .set_float_mode => { 1285 try sema.zirSetFloatMode(block, extended); 1286 i += 1; 1287 continue; 1288 }, 1289 .set_align_stack => { 1290 try sema.zirSetAlignStack(block, extended); 1291 i += 1; 1292 continue; 1293 }, 1294 .set_cold => { 1295 try sema.zirSetCold(block, extended); 1296 i += 1; 1297 continue; 1298 }, 1299 .breakpoint => { 1300 if (!block.is_comptime) { 1301 _ = try block.addNoOp(.breakpoint); 1302 } 1303 i += 1; 1304 continue; 1305 }, 1306 .value_placeholder => unreachable, // never appears in a body 1307 }; 1308 }, 1309 1310 // Instructions that we know can *never* be noreturn based solely on 1311 // their tag. We avoid needlessly checking if they are noreturn and 1312 // continue the loop. 1313 // We also know that they cannot be referenced later, so we avoid 1314 // putting them into the map. 1315 .dbg_stmt => { 1316 try sema.zirDbgStmt(block, inst); 1317 i += 1; 1318 continue; 1319 }, 1320 .dbg_var_ptr => { 1321 try sema.zirDbgVar(block, inst, .dbg_var_ptr); 1322 i += 1; 1323 continue; 1324 }, 1325 .dbg_var_val => { 1326 try sema.zirDbgVar(block, inst, .dbg_var_val); 1327 i += 1; 1328 continue; 1329 }, 1330 .dbg_block_begin => { 1331 dbg_block_begins += 1; 1332 try zirDbgBlockBegin(block); 1333 i += 1; 1334 continue; 1335 }, 1336 .dbg_block_end => { 1337 dbg_block_begins -= 1; 1338 try zirDbgBlockEnd(block); 1339 i += 1; 1340 continue; 1341 }, 1342 .ensure_err_union_payload_void => { 1343 try sema.zirEnsureErrUnionPayloadVoid(block, inst); 1344 i += 1; 1345 continue; 1346 }, 1347 .ensure_result_non_error => { 1348 try sema.zirEnsureResultNonError(block, inst); 1349 i += 1; 1350 continue; 1351 }, 1352 .ensure_result_used => { 1353 try sema.zirEnsureResultUsed(block, inst); 1354 i += 1; 1355 continue; 1356 }, 1357 .set_eval_branch_quota => { 1358 try sema.zirSetEvalBranchQuota(block, inst); 1359 i += 1; 1360 continue; 1361 }, 1362 .atomic_store => { 1363 try sema.zirAtomicStore(block, inst); 1364 i += 1; 1365 continue; 1366 }, 1367 .store => { 1368 try sema.zirStore(block, inst); 1369 i += 1; 1370 continue; 1371 }, 1372 .store_node => { 1373 try sema.zirStoreNode(block, inst); 1374 i += 1; 1375 continue; 1376 }, 1377 .store_to_inferred_ptr => { 1378 try sema.zirStoreToInferredPtr(block, inst); 1379 i += 1; 1380 continue; 1381 }, 1382 .resolve_inferred_alloc => { 1383 try sema.zirResolveInferredAlloc(block, inst); 1384 i += 1; 1385 continue; 1386 }, 1387 .validate_struct_init_ty => { 1388 try sema.zirValidateStructInitTy(block, inst, false); 1389 i += 1; 1390 continue; 1391 }, 1392 .validate_struct_init_result_ty => { 1393 try sema.zirValidateStructInitTy(block, inst, true); 1394 i += 1; 1395 continue; 1396 }, 1397 .validate_array_init_ty => { 1398 try sema.zirValidateArrayInitTy(block, inst, false); 1399 i += 1; 1400 continue; 1401 }, 1402 .validate_array_init_result_ty => { 1403 try sema.zirValidateArrayInitTy(block, inst, true); 1404 i += 1; 1405 continue; 1406 }, 1407 .validate_ptr_struct_init => { 1408 try sema.zirValidatePtrStructInit(block, inst); 1409 i += 1; 1410 continue; 1411 }, 1412 .validate_ptr_array_init => { 1413 try sema.zirValidatePtrArrayInit(block, inst); 1414 i += 1; 1415 continue; 1416 }, 1417 .validate_deref => { 1418 try sema.zirValidateDeref(block, inst); 1419 i += 1; 1420 continue; 1421 }, 1422 .validate_destructure => { 1423 try sema.zirValidateDestructure(block, inst); 1424 i += 1; 1425 continue; 1426 }, 1427 .validate_ref_ty => { 1428 try sema.zirValidateRefTy(block, inst); 1429 i += 1; 1430 continue; 1431 }, 1432 .@"export" => { 1433 try sema.zirExport(block, inst); 1434 i += 1; 1435 continue; 1436 }, 1437 .export_value => { 1438 try sema.zirExportValue(block, inst); 1439 i += 1; 1440 continue; 1441 }, 1442 .set_runtime_safety => { 1443 try sema.zirSetRuntimeSafety(block, inst); 1444 i += 1; 1445 continue; 1446 }, 1447 .param => { 1448 try sema.zirParam(block, inst, false); 1449 i += 1; 1450 continue; 1451 }, 1452 .param_comptime => { 1453 try sema.zirParam(block, inst, true); 1454 i += 1; 1455 continue; 1456 }, 1457 .param_anytype => { 1458 try sema.zirParamAnytype(block, inst, false); 1459 i += 1; 1460 continue; 1461 }, 1462 .param_anytype_comptime => { 1463 try sema.zirParamAnytype(block, inst, true); 1464 i += 1; 1465 continue; 1466 }, 1467 .closure_capture => { 1468 try sema.zirClosureCapture(block, inst); 1469 i += 1; 1470 continue; 1471 }, 1472 .memcpy => { 1473 try sema.zirMemcpy(block, inst); 1474 i += 1; 1475 continue; 1476 }, 1477 .memset => { 1478 try sema.zirMemset(block, inst); 1479 i += 1; 1480 continue; 1481 }, 1482 .check_comptime_control_flow => { 1483 if (!block.is_comptime) { 1484 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 1485 const src = inst_data.src(); 1486 const inline_block = inst_data.operand.toIndex().?; 1487 1488 var check_block = block; 1489 const target_runtime_index = while (true) { 1490 if (check_block.inline_block == inline_block.toOptional()) { 1491 break check_block.runtime_index; 1492 } 1493 check_block = check_block.parent.?; 1494 }; 1495 1496 if (@intFromEnum(target_runtime_index) < @intFromEnum(block.runtime_index)) { 1497 const runtime_src = block.runtime_cond orelse block.runtime_loop.?; 1498 const msg = msg: { 1499 const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{}); 1500 errdefer msg.destroy(sema.gpa); 1501 1502 try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{}); 1503 break :msg msg; 1504 }; 1505 return sema.failWithOwnedErrorMsg(block, msg); 1506 } 1507 } 1508 i += 1; 1509 continue; 1510 }, 1511 .save_err_ret_index => { 1512 try sema.zirSaveErrRetIndex(block, inst); 1513 i += 1; 1514 continue; 1515 }, 1516 .restore_err_ret_index => { 1517 try sema.zirRestoreErrRetIndex(block, inst); 1518 i += 1; 1519 continue; 1520 }, 1521 1522 // Special case instructions to handle comptime control flow. 1523 .@"break" => { 1524 if (block.is_comptime) { 1525 break inst; // same as break_inline 1526 } else { 1527 break sema.zirBreak(block, inst); 1528 } 1529 }, 1530 .break_inline => { 1531 if (block.is_comptime) { 1532 break inst; 1533 } else { 1534 sema.comptime_break_inst = inst; 1535 return error.ComptimeBreak; 1536 } 1537 }, 1538 .repeat => { 1539 if (block.is_comptime) { 1540 // Send comptime control flow back to the beginning of this block. 1541 const src = LazySrcLoc.nodeOffset(datas[@intFromEnum(inst)].node); 1542 try sema.emitBackwardBranch(block, src); 1543 1544 // We need to construct new capture scopes for the next loop iteration so it 1545 // can capture values without clobbering the earlier iteration's captures. 1546 block.wip_capture_scope = try mod.createCaptureScope(parent_capture_scope); 1547 1548 i = 0; 1549 continue; 1550 } else { 1551 break always_noreturn; 1552 } 1553 }, 1554 .repeat_inline => { 1555 // Send comptime control flow back to the beginning of this block. 1556 const src = LazySrcLoc.nodeOffset(datas[@intFromEnum(inst)].node); 1557 try sema.emitBackwardBranch(block, src); 1558 1559 // We need to construct new capture scopes for the next loop iteration so it 1560 // can capture values without clobbering the earlier iteration's captures. 1561 block.wip_capture_scope = try mod.createCaptureScope(parent_capture_scope); 1562 1563 i = 0; 1564 continue; 1565 }, 1566 .loop => blk: { 1567 if (!block.is_comptime) break :blk try sema.zirLoop(block, inst); 1568 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220 1569 const inst_data = datas[@intFromEnum(inst)].pl_node; 1570 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 1571 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1572 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1573 break always_noreturn; 1574 if (inst == break_data.block_inst) { 1575 break :blk try sema.resolveInst(break_data.operand); 1576 } else { 1577 break break_data.inst; 1578 } 1579 }, 1580 .block, .block_comptime => blk: { 1581 if (!block.is_comptime) { 1582 break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime); 1583 } 1584 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220 1585 const inst_data = datas[@intFromEnum(inst)].pl_node; 1586 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 1587 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1588 // If this block contains a function prototype, we need to reset the 1589 // current list of parameters and restore it later. 1590 // Note: this probably needs to be resolved in a more general manner. 1591 const prev_params = block.params; 1592 block.params = .{}; 1593 defer block.params = prev_params; 1594 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1595 break always_noreturn; 1596 if (inst == break_data.block_inst) { 1597 break :blk try sema.resolveInst(break_data.operand); 1598 } else { 1599 break break_data.inst; 1600 } 1601 }, 1602 .block_inline => blk: { 1603 // Directly analyze the block body without introducing a new block. 1604 // However, in the case of a corresponding break_inline which reaches 1605 // through a runtime conditional branch, we must retroactively emit 1606 // a block, so we remember the block index here just in case. 1607 const block_index = block.instructions.items.len; 1608 const inst_data = datas[@intFromEnum(inst)].pl_node; 1609 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 1610 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1611 const gpa = sema.gpa; 1612 1613 const opt_break_data = b: { 1614 // Create a temporary child block so that this inline block is properly 1615 // labeled for any .restore_err_ret_index instructions 1616 var child_block = block.makeSubBlock(); 1617 1618 // If this block contains a function prototype, we need to reset the 1619 // current list of parameters and restore it later. 1620 // Note: this probably needs to be resolved in a more general manner. 1621 const tag_index = @intFromEnum(inline_body[inline_body.len - 1]); 1622 child_block.inline_block = (if (tags[tag_index] == .repeat_inline) 1623 inline_body[0] 1624 else 1625 inst).toOptional(); 1626 1627 var label: Block.Label = .{ 1628 .zir_block = inst, 1629 .merges = undefined, 1630 }; 1631 child_block.label = &label; 1632 1633 // Write these instructions directly into the parent block 1634 child_block.instructions = block.instructions; 1635 defer block.instructions = child_block.instructions; 1636 1637 break :b try sema.analyzeBodyBreak(&child_block, inline_body); 1638 }; 1639 1640 // A runtime conditional branch that needs a post-hoc block to be 1641 // emitted communicates this by mapping the block index into the inst map. 1642 if (map.get(inst)) |new_block_ref| ph: { 1643 // Comptime control flow populates the map, so we don't actually know 1644 // if this is a post-hoc runtime block until we check the 1645 // post_hoc_block map. 1646 const new_block_inst = new_block_ref.toIndex() orelse break :ph; 1647 const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse 1648 break :ph; 1649 1650 // In this case we need to move all the instructions starting at 1651 // block_index from the current block into this new one. 1652 1653 if (opt_break_data) |break_data| { 1654 // This is a comptime break which we now change to a runtime break 1655 // since it crosses a runtime branch. 1656 // It may pass through our currently being analyzed block_inline or it 1657 // may point directly to it. In the latter case, this modifies the 1658 // block that we are about to look up in the post_hoc_blocks map below. 1659 try sema.addRuntimeBreak(block, break_data); 1660 } else { 1661 // Here the comptime control flow ends with noreturn; however 1662 // we have runtime control flow continuing after this block. 1663 // This branch is therefore handled by the `i += 1; continue;` 1664 // logic below. 1665 } 1666 1667 try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]); 1668 block.instructions.items.len = block_index; 1669 1670 const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges); 1671 { 1672 // Destroy the ad-hoc block entry so that it does not interfere with 1673 // the next iteration of comptime control flow, if any. 1674 labeled_block.destroy(gpa); 1675 assert(sema.post_hoc_blocks.remove(new_block_inst)); 1676 } 1677 map.putAssumeCapacity(inst, block_result); 1678 i += 1; 1679 continue; 1680 } 1681 1682 const break_data = opt_break_data orelse break always_noreturn; 1683 if (inst == break_data.block_inst) { 1684 break :blk try sema.resolveInst(break_data.operand); 1685 } else { 1686 break break_data.inst; 1687 } 1688 }, 1689 .condbr => blk: { 1690 if (!block.is_comptime) break sema.zirCondbr(block, inst); 1691 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220 1692 const inst_data = datas[@intFromEnum(inst)].pl_node; 1693 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; 1694 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 1695 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); 1696 const else_body = sema.code.bodySlice( 1697 extra.end + then_body.len, 1698 extra.data.else_body_len, 1699 ); 1700 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, .{ 1701 .needed_comptime_reason = "condition in comptime branch must be comptime-known", 1702 .block_comptime_reason = block.comptime_reason, 1703 }); 1704 const inline_body = if (cond.val.toBool()) then_body else else_body; 1705 1706 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src); 1707 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1708 break always_noreturn; 1709 if (inst == break_data.block_inst) { 1710 break :blk try sema.resolveInst(break_data.operand); 1711 } else { 1712 break break_data.inst; 1713 } 1714 }, 1715 .condbr_inline => blk: { 1716 const inst_data = datas[@intFromEnum(inst)].pl_node; 1717 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; 1718 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 1719 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); 1720 const else_body = sema.code.bodySlice( 1721 extra.end + then_body.len, 1722 extra.data.else_body_len, 1723 ); 1724 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, .{ 1725 .needed_comptime_reason = "condition in comptime branch must be comptime-known", 1726 .block_comptime_reason = block.comptime_reason, 1727 }); 1728 const inline_body = if (cond.val.toBool()) then_body else else_body; 1729 1730 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src); 1731 const old_runtime_index = block.runtime_index; 1732 defer block.runtime_index = old_runtime_index; 1733 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1734 break always_noreturn; 1735 if (inst == break_data.block_inst) { 1736 break :blk try sema.resolveInst(break_data.operand); 1737 } else { 1738 break break_data.inst; 1739 } 1740 }, 1741 .@"try" => blk: { 1742 if (!block.is_comptime) break :blk try sema.zirTry(block, inst); 1743 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 1744 const src = inst_data.src(); 1745 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 1746 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 1747 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1748 const err_union = try sema.resolveInst(extra.data.operand); 1749 const err_union_ty = sema.typeOf(err_union); 1750 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 1751 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ 1752 err_union_ty.fmt(mod), 1753 }); 1754 } 1755 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); 1756 assert(is_non_err != .none); 1757 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, .{ 1758 .needed_comptime_reason = "try operand inside comptime block must be comptime-known", 1759 .block_comptime_reason = block.comptime_reason, 1760 }); 1761 if (is_non_err_val.toBool()) { 1762 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); 1763 } 1764 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1765 break always_noreturn; 1766 if (inst == break_data.block_inst) { 1767 break :blk try sema.resolveInst(break_data.operand); 1768 } else { 1769 break break_data.inst; 1770 } 1771 }, 1772 .try_ptr => blk: { 1773 if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst); 1774 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 1775 const src = inst_data.src(); 1776 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 1777 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 1778 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); 1779 const operand = try sema.resolveInst(extra.data.operand); 1780 const err_union = try sema.analyzeLoad(block, src, operand, operand_src); 1781 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); 1782 assert(is_non_err != .none); 1783 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, .{ 1784 .needed_comptime_reason = "try operand inside comptime block must be comptime-known", 1785 .block_comptime_reason = block.comptime_reason, 1786 }); 1787 if (is_non_err_val.toBool()) { 1788 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); 1789 } 1790 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1791 break always_noreturn; 1792 if (inst == break_data.block_inst) { 1793 break :blk try sema.resolveInst(break_data.operand); 1794 } else { 1795 break break_data.inst; 1796 } 1797 }, 1798 .@"defer" => blk: { 1799 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"defer"; 1800 const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len); 1801 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) { 1802 error.ComptimeBreak => sema.comptime_break_inst, 1803 else => |e| return e, 1804 }; 1805 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn; 1806 break :blk .void_value; 1807 }, 1808 .defer_err_code => blk: { 1809 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code; 1810 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; 1811 const defer_body = sema.code.bodySlice(extra.index, extra.len); 1812 const err_code = try sema.resolveInst(inst_data.err_code); 1813 map.putAssumeCapacity(extra.remapped_err_code, err_code); 1814 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) { 1815 error.ComptimeBreak => sema.comptime_break_inst, 1816 else => |e| return e, 1817 }; 1818 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn; 1819 break :blk .void_value; 1820 }, 1821 }; 1822 if (sema.isNoReturn(air_inst)) { 1823 // We're going to assume that the body itself is noreturn, so let's ensure that now 1824 assert(block.instructions.items.len > 0); 1825 assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef())); 1826 break always_noreturn; 1827 } 1828 map.putAssumeCapacity(inst, air_inst); 1829 i += 1; 1830 }; 1831 1832 // balance out dbg_block_begins in case of early noreturn 1833 if (!block.is_comptime and !block.ownerModule().strip) { 1834 const noreturn_inst = block.instructions.popOrNull(); 1835 while (dbg_block_begins > 0) { 1836 dbg_block_begins -= 1; 1837 _ = try block.addInst(.{ 1838 .tag = .dbg_block_end, 1839 .data = undefined, 1840 }); 1841 } 1842 if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some); 1843 } 1844 1845 // We may have overwritten the capture scope due to a `repeat` instruction where 1846 // the body had a capture; restore it now. 1847 block.wip_capture_scope = parent_capture_scope; 1848 1849 return result; 1850 } 1851 1852 pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { 1853 if (zir_ref == .none) { 1854 return .none; 1855 } else { 1856 return resolveInst(sema, zir_ref); 1857 } 1858 } 1859 1860 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { 1861 assert(zir_ref != .none); 1862 if (zir_ref.toIndex()) |i| { 1863 const inst = sema.inst_map.get(i).?; 1864 if (inst == .generic_poison) return error.GenericPoison; 1865 return inst; 1866 } 1867 // First section of indexes correspond to a set number of constant values. 1868 // We intentionally map the same indexes to the same values between ZIR and AIR. 1869 return @enumFromInt(@intFromEnum(zir_ref)); 1870 } 1871 1872 fn resolveConstBool( 1873 sema: *Sema, 1874 block: *Block, 1875 src: LazySrcLoc, 1876 zir_ref: Zir.Inst.Ref, 1877 reason: NeededComptimeReason, 1878 ) !bool { 1879 const air_inst = try sema.resolveInst(zir_ref); 1880 const wanted_type = Type.bool; 1881 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 1882 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 1883 return val.toBool(); 1884 } 1885 1886 pub fn resolveConstString( 1887 sema: *Sema, 1888 block: *Block, 1889 src: LazySrcLoc, 1890 zir_ref: Zir.Inst.Ref, 1891 reason: NeededComptimeReason, 1892 ) ![]u8 { 1893 const air_inst = try sema.resolveInst(zir_ref); 1894 const wanted_type = Type.slice_const_u8; 1895 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 1896 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 1897 return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod); 1898 } 1899 1900 pub fn resolveConstStringIntern( 1901 sema: *Sema, 1902 block: *Block, 1903 src: LazySrcLoc, 1904 zir_ref: Zir.Inst.Ref, 1905 reason: NeededComptimeReason, 1906 ) !InternPool.NullTerminatedString { 1907 const air_inst = try sema.resolveInst(zir_ref); 1908 const wanted_type = Type.slice_const_u8; 1909 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 1910 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); 1911 return val.toIpString(wanted_type, sema.mod); 1912 } 1913 1914 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { 1915 const air_inst = try sema.resolveInst(zir_ref); 1916 assert(air_inst != .var_args_param_type); 1917 const ty = try sema.analyzeAsType(block, src, air_inst); 1918 if (ty.isGenericPoison()) return error.GenericPoison; 1919 return ty; 1920 } 1921 1922 fn resolveDestType( 1923 sema: *Sema, 1924 block: *Block, 1925 src: LazySrcLoc, 1926 zir_ref: Zir.Inst.Ref, 1927 strat: enum { remove_eu_opt, remove_eu, remove_opt }, 1928 builtin_name: []const u8, 1929 ) !Type { 1930 const mod = sema.mod; 1931 const remove_eu = switch (strat) { 1932 .remove_eu_opt, .remove_eu => true, 1933 .remove_opt => false, 1934 }; 1935 const remove_opt = switch (strat) { 1936 .remove_eu_opt, .remove_opt => true, 1937 .remove_eu => false, 1938 }; 1939 1940 const raw_ty = sema.resolveType(block, src, zir_ref) catch |err| switch (err) { 1941 error.GenericPoison => { 1942 // Cast builtins use their result type as the destination type, but 1943 // it could be an anytype argument, which we can't catch in AstGen. 1944 const msg = msg: { 1945 const msg = try sema.errMsg(block, src, "{s} must have a known result type", .{builtin_name}); 1946 errdefer msg.destroy(sema.gpa); 1947 switch (sema.genericPoisonReason(zir_ref)) { 1948 .anytype_param => |call_src| try sema.errNote(block, call_src, msg, "result type is unknown due to anytype parameter", .{}), 1949 .anyopaque_ptr => |ptr_src| try sema.errNote(block, ptr_src, msg, "result type is unknown due to opaque pointer type", .{}), 1950 .unknown => {}, 1951 } 1952 try sema.errNote(block, src, msg, "use @as to provide explicit result type", .{}); 1953 break :msg msg; 1954 }; 1955 return sema.failWithOwnedErrorMsg(block, msg); 1956 }, 1957 else => |e| return e, 1958 }; 1959 1960 if (remove_eu and raw_ty.zigTypeTag(mod) == .ErrorUnion) { 1961 const eu_child = raw_ty.errorUnionPayload(mod); 1962 if (remove_opt and eu_child.zigTypeTag(mod) == .Optional) { 1963 return eu_child.childType(mod); 1964 } 1965 return eu_child; 1966 } 1967 if (remove_opt and raw_ty.zigTypeTag(mod) == .Optional) { 1968 return raw_ty.childType(mod); 1969 } 1970 return raw_ty; 1971 } 1972 1973 const GenericPoisonReason = union(enum) { 1974 anytype_param: LazySrcLoc, 1975 anyopaque_ptr: LazySrcLoc, 1976 unknown, 1977 }; 1978 1979 /// Backtracks through ZIR instructions to determine the reason a generic poison 1980 /// type was created. Used for error reporting. 1981 fn genericPoisonReason(sema: *Sema, ref: Zir.Inst.Ref) GenericPoisonReason { 1982 var cur = ref; 1983 while (true) { 1984 const inst = cur.toIndex() orelse return .unknown; 1985 switch (sema.code.instructions.items(.tag)[@intFromEnum(inst)]) { 1986 .validate_array_init_ref_ty => { 1987 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 1988 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; 1989 cur = extra.ptr_ty; 1990 }, 1991 .array_init_elem_type => { 1992 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 1993 cur = bin.lhs; 1994 }, 1995 .indexable_ptr_elem_type, .vector_elem_type => { 1996 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 1997 cur = un_node.operand; 1998 }, 1999 .struct_init_field_type => { 2000 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 2001 const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data; 2002 cur = extra.container_type; 2003 }, 2004 .elem_type => { 2005 // There are two cases here: the pointer type may already have been 2006 // generic poison, or it may have been an anyopaque pointer. 2007 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 2008 const operand_ref = sema.resolveInst(un_node.operand) catch |err| switch (err) { 2009 error.GenericPoison => unreachable, // this is a type, not a value 2010 }; 2011 const operand_val = operand_ref.toInterned() orelse return .unknown; 2012 if (operand_val == .generic_poison_type) { 2013 // The pointer was generic poison - keep looking. 2014 cur = un_node.operand; 2015 } else { 2016 // This must be an anyopaque pointer! 2017 return .{ .anyopaque_ptr = un_node.src() }; 2018 } 2019 }, 2020 .call, .field_call => { 2021 // A function call can never return generic poison, so we must be 2022 // evaluating an `anytype` function parameter. 2023 // TODO: better source location - function decl rather than call 2024 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 2025 return .{ .anytype_param = pl_node.src() }; 2026 }, 2027 else => return .unknown, 2028 } 2029 } 2030 } 2031 2032 fn analyzeAsType( 2033 sema: *Sema, 2034 block: *Block, 2035 src: LazySrcLoc, 2036 air_inst: Air.Inst.Ref, 2037 ) !Type { 2038 const wanted_type = Type.type; 2039 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 2040 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ 2041 .needed_comptime_reason = "types must be comptime-known", 2042 }); 2043 return val.toType(); 2044 } 2045 2046 pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { 2047 const mod = sema.mod; 2048 const comp = mod.comp; 2049 const gpa = sema.gpa; 2050 const ip = &mod.intern_pool; 2051 if (!comp.config.any_error_tracing) return; 2052 2053 assert(!block.is_comptime); 2054 var err_trace_block = block.makeSubBlock(); 2055 defer err_trace_block.instructions.deinit(gpa); 2056 2057 const src: LazySrcLoc = .unneeded; 2058 2059 // var addrs: [err_return_trace_addr_count]usize = undefined; 2060 const err_return_trace_addr_count = 32; 2061 const addr_arr_ty = try mod.arrayType(.{ 2062 .len = err_return_trace_addr_count, 2063 .child = .usize_type, 2064 }); 2065 const addrs_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(addr_arr_ty)); 2066 2067 // var st: StackTrace = undefined; 2068 const stack_trace_ty = try sema.getBuiltinType("StackTrace"); 2069 try sema.resolveTypeFields(stack_trace_ty); 2070 const st_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(stack_trace_ty)); 2071 2072 // st.instruction_addresses = &addrs; 2073 const instruction_addresses_field_name = try ip.getOrPutString(gpa, "instruction_addresses"); 2074 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true); 2075 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); 2076 2077 // st.index = 0; 2078 const index_field_name = try ip.getOrPutString(gpa, "index"); 2079 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true); 2080 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store); 2081 2082 // @errorReturnTrace() = &st; 2083 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr); 2084 2085 try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items); 2086 } 2087 2088 /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value. 2089 /// InternPool key `variable` is considered a runtime value. 2090 /// Generic poison causes `error.GenericPoison` to be returned. 2091 fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2092 const val = (try sema.resolveValueAllowVariables(inst)) orelse return null; 2093 if (val.isGenericPoison()) return error.GenericPoison; 2094 if (sema.mod.intern_pool.isVariable(val.toIntern())) return null; 2095 return val; 2096 } 2097 2098 /// Like `resolveValue`, but emits an error if the value is not comptime-known. 2099 fn resolveConstValue( 2100 sema: *Sema, 2101 block: *Block, 2102 src: LazySrcLoc, 2103 inst: Air.Inst.Ref, 2104 reason: NeededComptimeReason, 2105 ) CompileError!Value { 2106 return try sema.resolveValue(inst) orelse { 2107 return sema.failWithNeededComptime(block, src, reason); 2108 }; 2109 } 2110 2111 /// Like `resolveValue`, but emits an error if the value is comptime-known to be undefined. 2112 fn resolveDefinedValue( 2113 sema: *Sema, 2114 block: *Block, 2115 src: LazySrcLoc, 2116 air_ref: Air.Inst.Ref, 2117 ) CompileError!?Value { 2118 const mod = sema.mod; 2119 const val = try sema.resolveValue(air_ref) orelse return null; 2120 if (val.isUndef(mod)) { 2121 return sema.failWithUseOfUndef(block, src); 2122 } 2123 return val; 2124 } 2125 2126 /// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined. 2127 fn resolveConstDefinedValue( 2128 sema: *Sema, 2129 block: *Block, 2130 src: LazySrcLoc, 2131 air_ref: Air.Inst.Ref, 2132 reason: NeededComptimeReason, 2133 ) CompileError!Value { 2134 const val = try sema.resolveConstValue(block, src, air_ref, reason); 2135 if (val.isUndef(sema.mod)) return sema.failWithUseOfUndef(block, src); 2136 return val; 2137 } 2138 2139 /// Like `resolveValue`, but recursively resolves lazy values before returning. 2140 fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2141 return try sema.resolveLazyValue((try sema.resolveValue(inst)) orelse return null); 2142 } 2143 2144 /// Like `resolveValue`, but any pointer value which does not correspond 2145 /// to a comptime-known integer (e.g. a decl pointer) returns `null`. 2146 /// Lazy values are recursively resolved. 2147 fn resolveValueIntable(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2148 const val = (try sema.resolveValue(inst)) orelse return null; 2149 if (sema.mod.intern_pool.getBackingAddrTag(val.toIntern())) |addr| switch (addr) { 2150 .decl, .anon_decl, .mut_decl, .comptime_field => return null, 2151 .int => {}, 2152 .eu_payload, .opt_payload, .elem, .field => unreachable, 2153 }; 2154 return try sema.resolveLazyValue(val); 2155 } 2156 2157 /// Returns all InternPool keys representing values, including `variable`, `undef`, and `generic_poison`. 2158 fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2159 assert(inst != .none); 2160 // First section of indexes correspond to a set number of constant values. 2161 if (@intFromEnum(inst) < InternPool.static_len) { 2162 return Value.fromInterned(@as(InternPool.Index, @enumFromInt(@intFromEnum(inst)))); 2163 } 2164 2165 const air_tags = sema.air_instructions.items(.tag); 2166 if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { 2167 if (inst.toInterned()) |ip_index| { 2168 const val = Value.fromInterned(ip_index); 2169 if (val.getVariable(sema.mod) != null) return val; 2170 } 2171 return opv; 2172 } 2173 const ip_index = inst.toInterned() orelse { 2174 switch (air_tags[@intFromEnum(inst.toIndex().?)]) { 2175 .inferred_alloc => unreachable, 2176 .inferred_alloc_comptime => unreachable, 2177 else => return null, 2178 } 2179 }; 2180 const val = Value.fromInterned(ip_index); 2181 if (val.isPtrToThreadLocal(sema.mod)) return null; 2182 return val; 2183 } 2184 2185 /// Returns a compile error if the value has tag `variable`. See `resolveInstValue` for 2186 /// a function that does not. 2187 pub fn resolveInstConst( 2188 sema: *Sema, 2189 block: *Block, 2190 src: LazySrcLoc, 2191 zir_ref: Zir.Inst.Ref, 2192 reason: NeededComptimeReason, 2193 ) CompileError!TypedValue { 2194 const air_ref = try sema.resolveInst(zir_ref); 2195 const val = try sema.resolveConstDefinedValue(block, src, air_ref, reason); 2196 return .{ 2197 .ty = sema.typeOf(air_ref), 2198 .val = val, 2199 }; 2200 } 2201 2202 /// Value Tag may be `undef` or `variable`. 2203 /// See `resolveInstConst` for an alternative. 2204 pub fn resolveInstValueAllowVariables( 2205 sema: *Sema, 2206 block: *Block, 2207 src: LazySrcLoc, 2208 zir_ref: Zir.Inst.Ref, 2209 reason: NeededComptimeReason, 2210 ) CompileError!TypedValue { 2211 const air_ref = try sema.resolveInst(zir_ref); 2212 const val = try sema.resolveValueAllowVariables(air_ref) orelse { 2213 return sema.failWithNeededComptime(block, src, reason); 2214 }; 2215 if (val.isGenericPoison()) return error.GenericPoison; 2216 return .{ 2217 .ty = sema.typeOf(air_ref), 2218 .val = val, 2219 }; 2220 } 2221 2222 fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: NeededComptimeReason) CompileError { 2223 const msg = msg: { 2224 const msg = try sema.errMsg(block, src, "unable to resolve comptime value", .{}); 2225 errdefer msg.destroy(sema.gpa); 2226 try sema.errNote(block, src, msg, "{s}", .{reason.needed_comptime_reason}); 2227 2228 if (reason.block_comptime_reason) |block_comptime_reason| { 2229 try block_comptime_reason.explain(sema, msg); 2230 } 2231 break :msg msg; 2232 }; 2233 return sema.failWithOwnedErrorMsg(block, msg); 2234 } 2235 2236 fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2237 return sema.fail(block, src, "use of undefined value here causes undefined behavior", .{}); 2238 } 2239 2240 fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2241 return sema.fail(block, src, "division by zero here causes undefined behavior", .{}); 2242 } 2243 2244 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { 2245 return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ 2246 lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod), 2247 }); 2248 } 2249 2250 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError { 2251 return sema.fail(block, src, "expected optional type, found '{}'", .{optional_ty.fmt(sema.mod)}); 2252 } 2253 2254 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { 2255 const mod = sema.mod; 2256 const msg = msg: { 2257 const msg = try sema.errMsg(block, src, "type '{}' does not support array initialization syntax", .{ 2258 ty.fmt(mod), 2259 }); 2260 errdefer msg.destroy(sema.gpa); 2261 if (ty.isSlice(mod)) { 2262 try sema.errNote(block, src, msg, "inferred array length is specified with an underscore: '[_]{}'", .{ty.elemType2(mod).fmt(mod)}); 2263 } 2264 break :msg msg; 2265 }; 2266 return sema.failWithOwnedErrorMsg(block, msg); 2267 } 2268 2269 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { 2270 return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{ 2271 ty.fmt(sema.mod), 2272 }); 2273 } 2274 2275 fn failWithErrorSetCodeMissing( 2276 sema: *Sema, 2277 block: *Block, 2278 src: LazySrcLoc, 2279 dest_err_set_ty: Type, 2280 src_err_set_ty: Type, 2281 ) CompileError { 2282 return sema.fail(block, src, "expected type '{}', found type '{}'", .{ 2283 dest_err_set_ty.fmt(sema.mod), src_err_set_ty.fmt(sema.mod), 2284 }); 2285 } 2286 2287 fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: usize) CompileError { 2288 const mod = sema.mod; 2289 if (int_ty.zigTypeTag(mod) == .Vector) { 2290 const msg = msg: { 2291 const msg = try sema.errMsg(block, src, "overflow of vector type '{}' with value '{}'", .{ 2292 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod), 2293 }); 2294 errdefer msg.destroy(sema.gpa); 2295 try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index}); 2296 break :msg msg; 2297 }; 2298 return sema.failWithOwnedErrorMsg(block, msg); 2299 } 2300 return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{ 2301 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod), 2302 }); 2303 } 2304 2305 fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError { 2306 const mod = sema.mod; 2307 const msg = msg: { 2308 const msg = try sema.errMsg(block, init_src, "value stored in comptime field does not match the default value of the field", .{}); 2309 errdefer msg.destroy(sema.gpa); 2310 2311 const struct_type = mod.typeToStruct(container_ty) orelse break :msg msg; 2312 const default_value_src = mod.fieldSrcLoc(struct_type.decl.unwrap().?, .{ 2313 .index = field_index, 2314 .range = .value, 2315 }); 2316 try mod.errNoteNonLazy(default_value_src, msg, "default value set here", .{}); 2317 break :msg msg; 2318 }; 2319 return sema.failWithOwnedErrorMsg(block, msg); 2320 } 2321 2322 fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2323 const msg = msg: { 2324 const msg = try sema.errMsg(block, src, "async has not been implemented in the self-hosted compiler yet", .{}); 2325 errdefer msg.destroy(sema.gpa); 2326 break :msg msg; 2327 }; 2328 return sema.failWithOwnedErrorMsg(block, msg); 2329 } 2330 2331 fn failWithInvalidFieldAccess( 2332 sema: *Sema, 2333 block: *Block, 2334 src: LazySrcLoc, 2335 object_ty: Type, 2336 field_name: InternPool.NullTerminatedString, 2337 ) CompileError { 2338 const mod = sema.mod; 2339 const inner_ty = if (object_ty.isSinglePointer(mod)) object_ty.childType(mod) else object_ty; 2340 2341 if (inner_ty.zigTypeTag(mod) == .Optional) opt: { 2342 const child_ty = inner_ty.optionalChild(mod); 2343 if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :opt; 2344 const msg = msg: { 2345 const msg = try sema.errMsg(block, src, "optional type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); 2346 errdefer msg.destroy(sema.gpa); 2347 try sema.errNote(block, src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 2348 break :msg msg; 2349 }; 2350 return sema.failWithOwnedErrorMsg(block, msg); 2351 } else if (inner_ty.zigTypeTag(mod) == .ErrorUnion) err: { 2352 const child_ty = inner_ty.errorUnionPayload(mod); 2353 if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :err; 2354 const msg = msg: { 2355 const msg = try sema.errMsg(block, src, "error union type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); 2356 errdefer msg.destroy(sema.gpa); 2357 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); 2358 break :msg msg; 2359 }; 2360 return sema.failWithOwnedErrorMsg(block, msg); 2361 } 2362 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); 2363 } 2364 2365 fn typeSupportsFieldAccess(mod: *const Module, ty: Type, field_name: InternPool.NullTerminatedString) bool { 2366 const ip = &mod.intern_pool; 2367 switch (ty.zigTypeTag(mod)) { 2368 .Array => return ip.stringEqlSlice(field_name, "len"), 2369 .Pointer => { 2370 const ptr_info = ty.ptrInfo(mod); 2371 if (ptr_info.flags.size == .Slice) { 2372 return ip.stringEqlSlice(field_name, "ptr") or ip.stringEqlSlice(field_name, "len"); 2373 } else if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Array) { 2374 return ip.stringEqlSlice(field_name, "len"); 2375 } else return false; 2376 }, 2377 .Type, .Struct, .Union => return true, 2378 else => return false, 2379 } 2380 } 2381 2382 /// We don't return a pointer to the new error note because the pointer 2383 /// becomes invalid when you add another one. 2384 fn errNote( 2385 sema: *Sema, 2386 block: *Block, 2387 src: LazySrcLoc, 2388 parent: *Module.ErrorMsg, 2389 comptime format: []const u8, 2390 args: anytype, 2391 ) error{OutOfMemory}!void { 2392 const mod = sema.mod; 2393 const src_decl = mod.declPtr(block.src_decl); 2394 return mod.errNoteNonLazy(src.toSrcLoc(src_decl, mod), parent, format, args); 2395 } 2396 2397 fn addFieldErrNote( 2398 sema: *Sema, 2399 container_ty: Type, 2400 field_index: usize, 2401 parent: *Module.ErrorMsg, 2402 comptime format: []const u8, 2403 args: anytype, 2404 ) !void { 2405 @setCold(true); 2406 const mod = sema.mod; 2407 const decl_index = container_ty.getOwnerDecl(mod); 2408 const decl = mod.declPtr(decl_index); 2409 2410 const field_src = blk: { 2411 const tree = decl.getFileScope(mod).getTree(sema.gpa) catch |err| { 2412 log.err("unable to load AST to report compile error: {s}", .{@errorName(err)}); 2413 break :blk decl.srcLoc(mod); 2414 }; 2415 2416 const container_node = decl.relativeToNodeIndex(0); 2417 const node_tags = tree.nodes.items(.tag); 2418 var buf: [2]std.zig.Ast.Node.Index = undefined; 2419 const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc(mod); 2420 2421 var it_index: usize = 0; 2422 for (container_decl.ast.members) |member_node| { 2423 switch (node_tags[member_node]) { 2424 .container_field_init, 2425 .container_field_align, 2426 .container_field, 2427 => { 2428 if (it_index == field_index) { 2429 break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node), mod); 2430 } 2431 it_index += 1; 2432 }, 2433 else => continue, 2434 } 2435 } 2436 unreachable; 2437 }; 2438 try mod.errNoteNonLazy(field_src, parent, format, args); 2439 } 2440 2441 fn errMsg( 2442 sema: *Sema, 2443 block: *Block, 2444 src: LazySrcLoc, 2445 comptime format: []const u8, 2446 args: anytype, 2447 ) error{ NeededSourceLocation, OutOfMemory }!*Module.ErrorMsg { 2448 const mod = sema.mod; 2449 if (src == .unneeded) return error.NeededSourceLocation; 2450 const src_decl = mod.declPtr(block.src_decl); 2451 return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl, mod), format, args); 2452 } 2453 2454 pub fn fail( 2455 sema: *Sema, 2456 block: *Block, 2457 src: LazySrcLoc, 2458 comptime format: []const u8, 2459 args: anytype, 2460 ) CompileError { 2461 const err_msg = try sema.errMsg(block, src, format, args); 2462 return sema.failWithOwnedErrorMsg(block, err_msg); 2463 } 2464 2465 fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Module.ErrorMsg) error{ AnalysisFail, OutOfMemory } { 2466 @setCold(true); 2467 const gpa = sema.gpa; 2468 const mod = sema.mod; 2469 2470 ref: { 2471 errdefer err_msg.destroy(gpa); 2472 2473 if (crash_report.is_enabled and mod.comp.debug_compile_errors) { 2474 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 2475 wip_errors.init(gpa) catch unreachable; 2476 Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*) catch unreachable; 2477 std.debug.print("compile error during Sema:\n", .{}); 2478 var error_bundle = wip_errors.toOwnedBundle("") catch unreachable; 2479 error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); 2480 crash_report.compilerPanic("unexpected compile error occurred", null, null); 2481 } 2482 2483 try mod.failed_decls.ensureUnusedCapacity(gpa, 1); 2484 try mod.failed_files.ensureUnusedCapacity(gpa, 1); 2485 2486 if (block) |start_block| { 2487 var block_it = start_block; 2488 while (block_it.inlining) |inlining| { 2489 try sema.errNote( 2490 inlining.call_block, 2491 inlining.call_src, 2492 err_msg, 2493 "called from here", 2494 .{}, 2495 ); 2496 block_it = inlining.call_block; 2497 } 2498 2499 const max_references = refs: { 2500 if (mod.comp.reference_trace) |num| break :refs num; 2501 // Do not add multiple traces without explicit request. 2502 if (mod.failed_decls.count() > 0) break :ref; 2503 break :refs default_reference_trace_len; 2504 }; 2505 2506 var referenced_by = if (sema.owner_func_index != .none) 2507 mod.funcOwnerDeclIndex(sema.owner_func_index) 2508 else 2509 sema.owner_decl_index; 2510 var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa); 2511 defer reference_stack.deinit(); 2512 2513 // Avoid infinite loops. 2514 var seen = std.AutoHashMap(InternPool.DeclIndex, void).init(gpa); 2515 defer seen.deinit(); 2516 2517 while (mod.reference_table.get(referenced_by)) |ref| { 2518 const gop = try seen.getOrPut(ref.referencer); 2519 if (gop.found_existing) break; 2520 if (reference_stack.items.len < max_references) { 2521 const decl = mod.declPtr(ref.referencer); 2522 try reference_stack.append(.{ 2523 .decl = decl.name, 2524 .src_loc = ref.src.toSrcLoc(decl, mod), 2525 }); 2526 } 2527 referenced_by = ref.referencer; 2528 } 2529 err_msg.reference_trace = try reference_stack.toOwnedSlice(); 2530 err_msg.hidden_references = @intCast(seen.count() -| max_references); 2531 } 2532 } 2533 const ip = &mod.intern_pool; 2534 if (sema.owner_func_index != .none) { 2535 ip.funcAnalysis(sema.owner_func_index).state = .sema_failure; 2536 } else { 2537 sema.owner_decl.analysis = .sema_failure; 2538 sema.owner_decl.generation = mod.generation; 2539 } 2540 if (sema.func_index != .none) { 2541 ip.funcAnalysis(sema.func_index).state = .sema_failure; 2542 } 2543 const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl_index); 2544 if (gop.found_existing) { 2545 // If there are multiple errors for the same Decl, prefer the first one added. 2546 sema.err = null; 2547 err_msg.destroy(gpa); 2548 } else { 2549 sema.err = err_msg; 2550 gop.value_ptr.* = err_msg; 2551 } 2552 return error.AnalysisFail; 2553 } 2554 2555 /// Given an ErrorMsg, modify its message and source location to the given values, turning the 2556 /// original message into a note. Notes on the original message are preserved as further notes. 2557 /// Reference trace is preserved. 2558 fn reparentOwnedErrorMsg( 2559 sema: *Sema, 2560 block: *Block, 2561 src: LazySrcLoc, 2562 msg: *Module.ErrorMsg, 2563 comptime format: []const u8, 2564 args: anytype, 2565 ) !void { 2566 const mod = sema.mod; 2567 const src_decl = mod.declPtr(block.src_decl); 2568 const resolved_src = src.toSrcLoc(src_decl, mod); 2569 const msg_str = try std.fmt.allocPrint(mod.gpa, format, args); 2570 2571 const orig_notes = msg.notes.len; 2572 msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1); 2573 std.mem.copyBackwards(Module.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]); 2574 msg.notes[0] = .{ 2575 .src_loc = msg.src_loc, 2576 .msg = msg.msg, 2577 }; 2578 2579 msg.src_loc = resolved_src; 2580 msg.msg = msg_str; 2581 } 2582 2583 const align_ty = Type.u29; 2584 2585 fn analyzeAsAlign( 2586 sema: *Sema, 2587 block: *Block, 2588 src: LazySrcLoc, 2589 air_ref: Air.Inst.Ref, 2590 ) !Alignment { 2591 const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, .{ 2592 .needed_comptime_reason = "alignment must be comptime-known", 2593 }); 2594 return sema.validateAlign(block, src, alignment_big); 2595 } 2596 2597 fn validateAlign( 2598 sema: *Sema, 2599 block: *Block, 2600 src: LazySrcLoc, 2601 alignment: u64, 2602 ) !Alignment { 2603 const result = try validateAlignAllowZero(sema, block, src, alignment); 2604 if (result == .none) return sema.fail(block, src, "alignment must be >= 1", .{}); 2605 return result; 2606 } 2607 2608 fn validateAlignAllowZero( 2609 sema: *Sema, 2610 block: *Block, 2611 src: LazySrcLoc, 2612 alignment: u64, 2613 ) !Alignment { 2614 if (alignment == 0) return .none; 2615 if (!std.math.isPowerOfTwo(alignment)) { 2616 return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{ 2617 alignment, 2618 }); 2619 } 2620 return Alignment.fromNonzeroByteUnits(alignment); 2621 } 2622 2623 pub fn resolveAlign( 2624 sema: *Sema, 2625 block: *Block, 2626 src: LazySrcLoc, 2627 zir_ref: Zir.Inst.Ref, 2628 ) !Alignment { 2629 const air_ref = try sema.resolveInst(zir_ref); 2630 return sema.analyzeAsAlign(block, src, air_ref); 2631 } 2632 2633 fn resolveInt( 2634 sema: *Sema, 2635 block: *Block, 2636 src: LazySrcLoc, 2637 zir_ref: Zir.Inst.Ref, 2638 dest_ty: Type, 2639 reason: NeededComptimeReason, 2640 ) !u64 { 2641 const air_ref = try sema.resolveInst(zir_ref); 2642 return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason); 2643 } 2644 2645 fn analyzeAsInt( 2646 sema: *Sema, 2647 block: *Block, 2648 src: LazySrcLoc, 2649 air_ref: Air.Inst.Ref, 2650 dest_ty: Type, 2651 reason: NeededComptimeReason, 2652 ) !u64 { 2653 const mod = sema.mod; 2654 const coerced = try sema.coerce(block, dest_ty, air_ref, src); 2655 const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); 2656 return (try val.getUnsignedIntAdvanced(mod, sema)).?; 2657 } 2658 2659 pub fn getStructType( 2660 sema: *Sema, 2661 decl: InternPool.DeclIndex, 2662 namespace: InternPool.NamespaceIndex, 2663 zir_index: Zir.Inst.Index, 2664 ) !InternPool.Index { 2665 const mod = sema.mod; 2666 const gpa = sema.gpa; 2667 const ip = &mod.intern_pool; 2668 const extended = sema.code.instructions.items(.data)[@intFromEnum(zir_index)].extended; 2669 assert(extended.opcode == .struct_decl); 2670 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 2671 2672 var extra_index: usize = extended.operand; 2673 extra_index += @intFromBool(small.has_src_node); 2674 const fields_len = if (small.has_fields_len) blk: { 2675 const fields_len = sema.code.extra[extra_index]; 2676 extra_index += 1; 2677 break :blk fields_len; 2678 } else 0; 2679 const decls_len = if (small.has_decls_len) blk: { 2680 const decls_len = sema.code.extra[extra_index]; 2681 extra_index += 1; 2682 break :blk decls_len; 2683 } else 0; 2684 2685 if (small.has_backing_int) { 2686 const backing_int_body_len = sema.code.extra[extra_index]; 2687 extra_index += 1; // backing_int_body_len 2688 if (backing_int_body_len == 0) { 2689 extra_index += 1; // backing_int_ref 2690 } else { 2691 extra_index += backing_int_body_len; // backing_int_body_inst 2692 } 2693 } 2694 2695 extra_index = try mod.scanNamespace(namespace, extra_index, decls_len, mod.declPtr(decl)); 2696 2697 const ty = try ip.getStructType(gpa, .{ 2698 .decl = decl, 2699 .namespace = namespace.toOptional(), 2700 .zir_index = zir_index, 2701 .layout = small.layout, 2702 .known_non_opv = small.known_non_opv, 2703 .is_tuple = small.is_tuple, 2704 .fields_len = fields_len, 2705 .requires_comptime = if (small.known_comptime_only) .yes else .unknown, 2706 .any_default_inits = small.any_default_inits, 2707 .any_comptime_fields = small.any_comptime_fields, 2708 .inits_resolved = false, 2709 .any_aligned_fields = small.any_aligned_fields, 2710 }); 2711 2712 return ty; 2713 } 2714 2715 fn zirStructDecl( 2716 sema: *Sema, 2717 block: *Block, 2718 extended: Zir.Inst.Extended.InstData, 2719 inst: Zir.Inst.Index, 2720 ) CompileError!Air.Inst.Ref { 2721 const mod = sema.mod; 2722 const ip = &mod.intern_pool; 2723 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 2724 const src: LazySrcLoc = if (small.has_src_node) blk: { 2725 const node_offset: i32 = @bitCast(sema.code.extra[extended.operand]); 2726 break :blk LazySrcLoc.nodeOffset(node_offset); 2727 } else sema.src; 2728 2729 // Because these three things each reference each other, `undefined` 2730 // placeholders are used before being set after the struct type gains an 2731 // InternPool index. 2732 2733 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 2734 .ty = Type.noreturn, 2735 .val = Value.@"unreachable", 2736 }, small.name_strategy, "struct", inst); 2737 const new_decl = mod.declPtr(new_decl_index); 2738 new_decl.owns_tv = true; 2739 errdefer mod.abortAnonDecl(new_decl_index); 2740 2741 const new_namespace_index = try mod.createNamespace(.{ 2742 .parent = block.namespace.toOptional(), 2743 .ty = undefined, 2744 .file_scope = block.getFileScope(mod), 2745 }); 2746 const new_namespace = mod.namespacePtr(new_namespace_index); 2747 errdefer mod.destroyNamespace(new_namespace_index); 2748 2749 const struct_ty = ty: { 2750 const ty = try sema.getStructType(new_decl_index, new_namespace_index, inst); 2751 if (sema.builtin_type_target_index != .none) { 2752 ip.resolveBuiltinType(sema.builtin_type_target_index, ty); 2753 break :ty sema.builtin_type_target_index; 2754 } 2755 break :ty ty; 2756 }; 2757 // TODO: figure out InternPool removals for incremental compilation 2758 //errdefer ip.remove(struct_ty); 2759 2760 new_decl.ty = Type.type; 2761 new_decl.val = Value.fromInterned(struct_ty); 2762 new_namespace.ty = Type.fromInterned(struct_ty); 2763 2764 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 2765 try mod.finalizeAnonDecl(new_decl_index); 2766 return decl_val; 2767 } 2768 2769 fn createAnonymousDeclTypeNamed( 2770 sema: *Sema, 2771 block: *Block, 2772 src: LazySrcLoc, 2773 typed_value: TypedValue, 2774 name_strategy: Zir.Inst.NameStrategy, 2775 anon_prefix: []const u8, 2776 inst: ?Zir.Inst.Index, 2777 ) !InternPool.DeclIndex { 2778 const mod = sema.mod; 2779 const ip = &mod.intern_pool; 2780 const gpa = sema.gpa; 2781 const namespace = block.namespace; 2782 const src_scope = block.wip_capture_scope; 2783 const src_decl = mod.declPtr(block.src_decl); 2784 const src_node = src_decl.relativeToNodeIndex(src.node_offset.x); 2785 const new_decl_index = try mod.allocateNewDecl(namespace, src_node, src_scope); 2786 errdefer mod.destroyDecl(new_decl_index); 2787 2788 switch (name_strategy) { 2789 .anon => { 2790 // It would be neat to have "struct:line:column" but this name has 2791 // to survive incremental updates, where it may have been shifted down 2792 // or up to a different line, but unchanged, and thus not unnecessarily 2793 // semantically analyzed. 2794 // This name is also used as the key in the parent namespace so it cannot be 2795 // renamed. 2796 2797 const name = mod.intern_pool.getOrPutStringFmt(gpa, "{}__{s}_{d}", .{ 2798 src_decl.name.fmt(&mod.intern_pool), anon_prefix, @intFromEnum(new_decl_index), 2799 }) catch unreachable; 2800 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, typed_value, name); 2801 return new_decl_index; 2802 }, 2803 .parent => { 2804 const name = mod.declPtr(block.src_decl).name; 2805 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, typed_value, name); 2806 return new_decl_index; 2807 }, 2808 .func => { 2809 const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index)); 2810 const zir_tags = sema.code.instructions.items(.tag); 2811 2812 var buf = std.ArrayList(u8).init(gpa); 2813 defer buf.deinit(); 2814 2815 const writer = buf.writer(); 2816 try writer.print("{}(", .{mod.declPtr(block.src_decl).name.fmt(&mod.intern_pool)}); 2817 2818 var arg_i: usize = 0; 2819 for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) { 2820 .param, .param_comptime, .param_anytype, .param_anytype_comptime => { 2821 const arg = sema.inst_map.get(zir_inst).?; 2822 // If this is being called in a generic function then analyzeCall will 2823 // have already resolved the args and this will work. 2824 // If not then this is a struct type being returned from a non-generic 2825 // function and the name doesn't matter since it will later 2826 // result in a compile error. 2827 const arg_val = sema.resolveConstValue(block, .unneeded, arg, undefined) catch 2828 return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); 2829 2830 if (arg_i != 0) try writer.writeByte(','); 2831 try writer.print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)}); 2832 2833 arg_i += 1; 2834 continue; 2835 }, 2836 else => continue, 2837 }; 2838 2839 try writer.writeByte(')'); 2840 const name = try mod.intern_pool.getOrPutString(gpa, buf.items); 2841 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, typed_value, name); 2842 return new_decl_index; 2843 }, 2844 .dbg_var => { 2845 const ref = inst.?.toRef(); 2846 const zir_tags = sema.code.instructions.items(.tag); 2847 const zir_data = sema.code.instructions.items(.data); 2848 for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) { 2849 .dbg_var_ptr, .dbg_var_val => { 2850 if (zir_data[i].str_op.operand != ref) continue; 2851 2852 const name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}.{s}", .{ 2853 src_decl.name.fmt(&mod.intern_pool), zir_data[i].str_op.getStr(sema.code), 2854 }); 2855 2856 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, typed_value, name); 2857 return new_decl_index; 2858 }, 2859 else => {}, 2860 }; 2861 return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); 2862 }, 2863 } 2864 } 2865 2866 fn zirEnumDecl( 2867 sema: *Sema, 2868 block: *Block, 2869 extended: Zir.Inst.Extended.InstData, 2870 inst: Zir.Inst.Index, 2871 ) CompileError!Air.Inst.Ref { 2872 const tracy = trace(@src()); 2873 defer tracy.end(); 2874 2875 const mod = sema.mod; 2876 const gpa = sema.gpa; 2877 const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); 2878 var extra_index: usize = extended.operand; 2879 2880 const src: LazySrcLoc = if (small.has_src_node) blk: { 2881 const node_offset: i32 = @bitCast(sema.code.extra[extra_index]); 2882 extra_index += 1; 2883 break :blk LazySrcLoc.nodeOffset(node_offset); 2884 } else sema.src; 2885 const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; 2886 2887 const tag_type_ref = if (small.has_tag_type) blk: { 2888 const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 2889 extra_index += 1; 2890 break :blk tag_type_ref; 2891 } else .none; 2892 2893 const body_len = if (small.has_body_len) blk: { 2894 const body_len = sema.code.extra[extra_index]; 2895 extra_index += 1; 2896 break :blk body_len; 2897 } else 0; 2898 2899 const fields_len = if (small.has_fields_len) blk: { 2900 const fields_len = sema.code.extra[extra_index]; 2901 extra_index += 1; 2902 break :blk fields_len; 2903 } else 0; 2904 2905 const decls_len = if (small.has_decls_len) blk: { 2906 const decls_len = sema.code.extra[extra_index]; 2907 extra_index += 1; 2908 break :blk decls_len; 2909 } else 0; 2910 2911 // Because these three things each reference each other, `undefined` 2912 // placeholders are used before being set after the enum type gains an 2913 // InternPool index. 2914 2915 var done = false; 2916 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 2917 .ty = Type.noreturn, 2918 .val = Value.@"unreachable", 2919 }, small.name_strategy, "enum", inst); 2920 const new_decl = mod.declPtr(new_decl_index); 2921 new_decl.owns_tv = true; 2922 errdefer if (!done) mod.abortAnonDecl(new_decl_index); 2923 2924 const new_namespace_index = try mod.createNamespace(.{ 2925 .parent = block.namespace.toOptional(), 2926 .ty = undefined, 2927 .file_scope = block.getFileScope(mod), 2928 }); 2929 const new_namespace = mod.namespacePtr(new_namespace_index); 2930 errdefer if (!done) mod.destroyNamespace(new_namespace_index); 2931 2932 extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); 2933 2934 const body = sema.code.bodySlice(extra_index, body_len); 2935 extra_index += body.len; 2936 2937 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; 2938 const body_end = extra_index; 2939 extra_index += bit_bags_count; 2940 2941 const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { 2942 if (bag != 0) break true; 2943 } else false; 2944 2945 const incomplete_enum = incomplete_enum: { 2946 var incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{ 2947 .decl = new_decl_index, 2948 .namespace = new_namespace_index.toOptional(), 2949 .fields_len = fields_len, 2950 .has_values = any_values, 2951 .tag_mode = if (small.nonexhaustive) 2952 .nonexhaustive 2953 else if (tag_type_ref == .none) 2954 .auto 2955 else 2956 .explicit, 2957 }); 2958 if (sema.builtin_type_target_index != .none) { 2959 mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, incomplete_enum.index); 2960 incomplete_enum.index = sema.builtin_type_target_index; 2961 } 2962 break :incomplete_enum incomplete_enum; 2963 }; 2964 // TODO: figure out InternPool removals for incremental compilation 2965 //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index); 2966 2967 new_decl.ty = Type.type; 2968 new_decl.val = Value.fromInterned(incomplete_enum.index); 2969 new_namespace.ty = Type.fromInterned(incomplete_enum.index); 2970 2971 const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); 2972 try mod.finalizeAnonDecl(new_decl_index); 2973 done = true; 2974 2975 const int_tag_ty = ty: { 2976 // We create a block for the field type instructions because they 2977 // may need to reference Decls from inside the enum namespace. 2978 // Within the field type, default value, and alignment expressions, the "owner decl" 2979 // should be the enum itself. 2980 2981 const prev_owner_decl = sema.owner_decl; 2982 const prev_owner_decl_index = sema.owner_decl_index; 2983 sema.owner_decl = new_decl; 2984 sema.owner_decl_index = new_decl_index; 2985 defer { 2986 sema.owner_decl = prev_owner_decl; 2987 sema.owner_decl_index = prev_owner_decl_index; 2988 } 2989 2990 const prev_owner_func_index = sema.owner_func_index; 2991 sema.owner_func_index = .none; 2992 defer sema.owner_func_index = prev_owner_func_index; 2993 2994 const prev_func_index = sema.func_index; 2995 sema.func_index = .none; 2996 defer sema.func_index = prev_func_index; 2997 2998 var enum_block: Block = .{ 2999 .parent = null, 3000 .sema = sema, 3001 .src_decl = new_decl_index, 3002 .namespace = new_namespace_index, 3003 .wip_capture_scope = try mod.createCaptureScope(new_decl.src_scope), 3004 .instructions = .{}, 3005 .inlining = null, 3006 .is_comptime = true, 3007 }; 3008 defer enum_block.instructions.deinit(sema.gpa); 3009 3010 if (body.len != 0) { 3011 try sema.analyzeBody(&enum_block, body); 3012 } 3013 3014 if (tag_type_ref != .none) { 3015 const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); 3016 if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) { 3017 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); 3018 } 3019 incomplete_enum.setTagType(&mod.intern_pool, ty.toIntern()); 3020 break :ty ty; 3021 } else if (fields_len == 0) { 3022 break :ty try mod.intType(.unsigned, 0); 3023 } else { 3024 const bits = std.math.log2_int_ceil(usize, fields_len); 3025 break :ty try mod.intType(.unsigned, bits); 3026 } 3027 }; 3028 3029 if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) { 3030 if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(mod)) { 3031 return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); 3032 } 3033 } 3034 3035 var bit_bag_index: usize = body_end; 3036 var cur_bit_bag: u32 = undefined; 3037 var field_i: u32 = 0; 3038 var last_tag_val: ?Value = null; 3039 while (field_i < fields_len) : (field_i += 1) { 3040 if (field_i % 32 == 0) { 3041 cur_bit_bag = sema.code.extra[bit_bag_index]; 3042 bit_bag_index += 1; 3043 } 3044 const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0; 3045 cur_bit_bag >>= 1; 3046 3047 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]); 3048 extra_index += 1; 3049 3050 // doc comment 3051 extra_index += 1; 3052 3053 const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir); 3054 if (incomplete_enum.addFieldName(&mod.intern_pool, field_name)) |other_index| { 3055 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy; 3056 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; 3057 const msg = msg: { 3058 const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name_zir}); 3059 errdefer msg.destroy(gpa); 3060 try sema.errNote(block, other_field_src, msg, "other field here", .{}); 3061 break :msg msg; 3062 }; 3063 return sema.failWithOwnedErrorMsg(block, msg); 3064 } 3065 3066 const tag_overflow = if (has_tag_value) overflow: { 3067 const tag_val_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3068 extra_index += 1; 3069 const tag_inst = try sema.resolveInst(tag_val_ref); 3070 last_tag_val = sema.resolveConstDefinedValue(block, .unneeded, tag_inst, undefined) catch |err| switch (err) { 3071 error.NeededSourceLocation => { 3072 const value_src = mod.fieldSrcLoc(new_decl_index, .{ 3073 .index = field_i, 3074 .range = .value, 3075 }).lazy; 3076 _ = try sema.resolveConstDefinedValue(block, value_src, tag_inst, .{ 3077 .needed_comptime_reason = "enum tag value must be comptime-known", 3078 }); 3079 unreachable; 3080 }, 3081 else => |e| return e, 3082 }; 3083 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true; 3084 last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); 3085 if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| { 3086 const value_src = mod.fieldSrcLoc(new_decl_index, .{ 3087 .index = field_i, 3088 .range = .value, 3089 }).lazy; 3090 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; 3091 const msg = msg: { 3092 const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); 3093 errdefer msg.destroy(gpa); 3094 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{}); 3095 break :msg msg; 3096 }; 3097 return sema.failWithOwnedErrorMsg(block, msg); 3098 } 3099 break :overflow false; 3100 } else if (any_values) overflow: { 3101 var overflow: ?usize = null; 3102 last_tag_val = if (last_tag_val) |val| 3103 try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty, &overflow) 3104 else 3105 try mod.intValue(int_tag_ty, 0); 3106 if (overflow != null) break :overflow true; 3107 if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| { 3108 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy; 3109 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; 3110 const msg = msg: { 3111 const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); 3112 errdefer msg.destroy(gpa); 3113 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{}); 3114 break :msg msg; 3115 }; 3116 return sema.failWithOwnedErrorMsg(block, msg); 3117 } 3118 break :overflow false; 3119 } else overflow: { 3120 last_tag_val = try mod.intValue(Type.comptime_int, field_i); 3121 if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true; 3122 last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); 3123 break :overflow false; 3124 }; 3125 3126 if (tag_overflow) { 3127 const value_src = mod.fieldSrcLoc(new_decl_index, .{ 3128 .index = field_i, 3129 .range = if (has_tag_value) .value else .name, 3130 }).lazy; 3131 const msg = try sema.errMsg(block, value_src, "enumeration value '{}' too large for type '{}'", .{ 3132 last_tag_val.?.fmtValue(int_tag_ty, mod), int_tag_ty.fmt(mod), 3133 }); 3134 return sema.failWithOwnedErrorMsg(block, msg); 3135 } 3136 } 3137 return decl_val; 3138 } 3139 3140 fn zirUnionDecl( 3141 sema: *Sema, 3142 block: *Block, 3143 extended: Zir.Inst.Extended.InstData, 3144 inst: Zir.Inst.Index, 3145 ) CompileError!Air.Inst.Ref { 3146 const tracy = trace(@src()); 3147 defer tracy.end(); 3148 3149 const mod = sema.mod; 3150 const gpa = sema.gpa; 3151 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); 3152 var extra_index: usize = extended.operand; 3153 3154 const src: LazySrcLoc = if (small.has_src_node) blk: { 3155 const node_offset: i32 = @bitCast(sema.code.extra[extra_index]); 3156 extra_index += 1; 3157 break :blk LazySrcLoc.nodeOffset(node_offset); 3158 } else sema.src; 3159 3160 extra_index += @intFromBool(small.has_tag_type); 3161 extra_index += @intFromBool(small.has_body_len); 3162 const fields_len = if (small.has_fields_len) blk: { 3163 const fields_len = sema.code.extra[extra_index]; 3164 extra_index += 1; 3165 break :blk fields_len; 3166 } else 0; 3167 3168 const decls_len = if (small.has_decls_len) blk: { 3169 const decls_len = sema.code.extra[extra_index]; 3170 extra_index += 1; 3171 break :blk decls_len; 3172 } else 0; 3173 3174 // Because these three things each reference each other, `undefined` 3175 // placeholders are used before being set after the union type gains an 3176 // InternPool index. 3177 3178 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 3179 .ty = Type.noreturn, 3180 .val = Value.@"unreachable", 3181 }, small.name_strategy, "union", inst); 3182 const new_decl = mod.declPtr(new_decl_index); 3183 new_decl.owns_tv = true; 3184 errdefer mod.abortAnonDecl(new_decl_index); 3185 3186 const new_namespace_index = try mod.createNamespace(.{ 3187 .parent = block.namespace.toOptional(), 3188 .ty = undefined, 3189 .file_scope = block.getFileScope(mod), 3190 }); 3191 const new_namespace = mod.namespacePtr(new_namespace_index); 3192 errdefer mod.destroyNamespace(new_namespace_index); 3193 3194 const union_ty = ty: { 3195 const ty = try mod.intern_pool.getUnionType(gpa, .{ 3196 .flags = .{ 3197 .layout = small.layout, 3198 .status = .none, 3199 .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) 3200 .tagged 3201 else if (small.layout != .Auto) 3202 .none 3203 else switch (block.wantSafety()) { 3204 true => .safety, 3205 false => .none, 3206 }, 3207 .any_aligned_fields = small.any_aligned_fields, 3208 .requires_comptime = .unknown, 3209 .assumed_runtime_bits = false, 3210 .assumed_pointer_aligned = false, 3211 .alignment = .none, 3212 }, 3213 .decl = new_decl_index, 3214 .namespace = new_namespace_index, 3215 .zir_index = inst, 3216 .fields_len = fields_len, 3217 .enum_tag_ty = .none, 3218 .field_types = &.{}, 3219 .field_aligns = &.{}, 3220 }); 3221 if (sema.builtin_type_target_index != .none) { 3222 mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, ty); 3223 break :ty sema.builtin_type_target_index; 3224 } 3225 break :ty ty; 3226 }; 3227 // TODO: figure out InternPool removals for incremental compilation 3228 //errdefer mod.intern_pool.remove(union_ty); 3229 3230 new_decl.ty = Type.type; 3231 new_decl.val = Value.fromInterned(union_ty); 3232 new_namespace.ty = Type.fromInterned(union_ty); 3233 3234 _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); 3235 3236 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 3237 try mod.finalizeAnonDecl(new_decl_index); 3238 return decl_val; 3239 } 3240 3241 fn zirOpaqueDecl( 3242 sema: *Sema, 3243 block: *Block, 3244 extended: Zir.Inst.Extended.InstData, 3245 inst: Zir.Inst.Index, 3246 ) CompileError!Air.Inst.Ref { 3247 const tracy = trace(@src()); 3248 defer tracy.end(); 3249 3250 const mod = sema.mod; 3251 const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small); 3252 var extra_index: usize = extended.operand; 3253 3254 const src: LazySrcLoc = if (small.has_src_node) blk: { 3255 const node_offset: i32 = @bitCast(sema.code.extra[extra_index]); 3256 extra_index += 1; 3257 break :blk LazySrcLoc.nodeOffset(node_offset); 3258 } else sema.src; 3259 3260 const decls_len = if (small.has_decls_len) blk: { 3261 const decls_len = sema.code.extra[extra_index]; 3262 extra_index += 1; 3263 break :blk decls_len; 3264 } else 0; 3265 3266 // Because these three things each reference each other, `undefined` 3267 // placeholders are used in two places before being set after the opaque 3268 // type gains an InternPool index. 3269 3270 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 3271 .ty = Type.noreturn, 3272 .val = Value.@"unreachable", 3273 }, small.name_strategy, "opaque", inst); 3274 const new_decl = mod.declPtr(new_decl_index); 3275 new_decl.owns_tv = true; 3276 errdefer mod.abortAnonDecl(new_decl_index); 3277 3278 const new_namespace_index = try mod.createNamespace(.{ 3279 .parent = block.namespace.toOptional(), 3280 .ty = undefined, 3281 .file_scope = block.getFileScope(mod), 3282 }); 3283 const new_namespace = mod.namespacePtr(new_namespace_index); 3284 errdefer mod.destroyNamespace(new_namespace_index); 3285 3286 const opaque_ty = try mod.intern(.{ .opaque_type = .{ 3287 .decl = new_decl_index, 3288 .namespace = new_namespace_index, 3289 } }); 3290 // TODO: figure out InternPool removals for incremental compilation 3291 //errdefer mod.intern_pool.remove(opaque_ty); 3292 3293 new_decl.ty = Type.type; 3294 new_decl.val = Value.fromInterned(opaque_ty); 3295 new_namespace.ty = Type.fromInterned(opaque_ty); 3296 3297 extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); 3298 3299 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 3300 try mod.finalizeAnonDecl(new_decl_index); 3301 return decl_val; 3302 } 3303 3304 fn zirErrorSetDecl( 3305 sema: *Sema, 3306 block: *Block, 3307 inst: Zir.Inst.Index, 3308 name_strategy: Zir.Inst.NameStrategy, 3309 ) CompileError!Air.Inst.Ref { 3310 const tracy = trace(@src()); 3311 defer tracy.end(); 3312 3313 const mod = sema.mod; 3314 const gpa = sema.gpa; 3315 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 3316 const src = inst_data.src(); 3317 const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); 3318 3319 var names: InferredErrorSet.NameMap = .{}; 3320 try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len); 3321 3322 var extra_index: u32 = @intCast(extra.end); 3323 const extra_index_end = extra_index + (extra.data.fields_len * 2); 3324 while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string 3325 const str_index = sema.code.extra[extra_index]; 3326 const name = sema.code.nullTerminatedString(str_index); 3327 const name_ip = try mod.intern_pool.getOrPutString(gpa, name); 3328 _ = try mod.getErrorValue(name_ip); 3329 const result = names.getOrPutAssumeCapacity(name_ip); 3330 assert(!result.found_existing); // verified in AstGen 3331 } 3332 3333 const error_set_ty = try mod.errorSetFromUnsortedNames(names.keys()); 3334 3335 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 3336 .ty = Type.type, 3337 .val = error_set_ty.toValue(), 3338 }, name_strategy, "error", inst); 3339 const new_decl = mod.declPtr(new_decl_index); 3340 new_decl.owns_tv = true; 3341 errdefer mod.abortAnonDecl(new_decl_index); 3342 3343 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 3344 try mod.finalizeAnonDecl(new_decl_index); 3345 return decl_val; 3346 } 3347 3348 fn zirRetPtr(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 3349 const tracy = trace(@src()); 3350 defer tracy.end(); 3351 3352 if (block.is_comptime or try sema.typeRequiresComptime(sema.fn_ret_ty)) { 3353 try sema.resolveTypeFields(sema.fn_ret_ty); 3354 return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty, .none); 3355 } 3356 3357 const target = sema.mod.getTarget(); 3358 const ptr_type = try sema.ptrType(.{ 3359 .child = sema.fn_ret_ty.toIntern(), 3360 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 3361 }); 3362 3363 if (block.inlining != null) { 3364 // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr. 3365 // TODO when functions gain result location support, the inlining struct in 3366 // Block should contain the return pointer, and we would pass that through here. 3367 try sema.queueFullTypeResolution(sema.fn_ret_ty); 3368 return block.addTy(.alloc, ptr_type); 3369 } 3370 3371 return block.addTy(.ret_ptr, ptr_type); 3372 } 3373 3374 fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3375 const tracy = trace(@src()); 3376 defer tracy.end(); 3377 3378 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 3379 const operand = try sema.resolveInst(inst_data.operand); 3380 return sema.analyzeRef(block, inst_data.src(), operand); 3381 } 3382 3383 fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3384 const tracy = trace(@src()); 3385 defer tracy.end(); 3386 3387 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3388 const operand = try sema.resolveInst(inst_data.operand); 3389 const src = inst_data.src(); 3390 3391 return sema.ensureResultUsed(block, sema.typeOf(operand), src); 3392 } 3393 3394 fn ensureResultUsed( 3395 sema: *Sema, 3396 block: *Block, 3397 ty: Type, 3398 src: LazySrcLoc, 3399 ) CompileError!void { 3400 const mod = sema.mod; 3401 switch (ty.zigTypeTag(mod)) { 3402 .Void, .NoReturn => return, 3403 .ErrorSet, .ErrorUnion => { 3404 const msg = msg: { 3405 const msg = try sema.errMsg(block, src, "error is ignored", .{}); 3406 errdefer msg.destroy(sema.gpa); 3407 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); 3408 break :msg msg; 3409 }; 3410 return sema.failWithOwnedErrorMsg(block, msg); 3411 }, 3412 else => { 3413 const msg = msg: { 3414 const msg = try sema.errMsg(block, src, "value of type '{}' ignored", .{ty.fmt(sema.mod)}); 3415 errdefer msg.destroy(sema.gpa); 3416 try sema.errNote(block, src, msg, "all non-void values must be used", .{}); 3417 try sema.errNote(block, src, msg, "this error can be suppressed by assigning the value to '_'", .{}); 3418 break :msg msg; 3419 }; 3420 return sema.failWithOwnedErrorMsg(block, msg); 3421 }, 3422 } 3423 } 3424 3425 fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3426 const tracy = trace(@src()); 3427 defer tracy.end(); 3428 3429 const mod = sema.mod; 3430 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3431 const operand = try sema.resolveInst(inst_data.operand); 3432 const src = inst_data.src(); 3433 const operand_ty = sema.typeOf(operand); 3434 switch (operand_ty.zigTypeTag(mod)) { 3435 .ErrorSet, .ErrorUnion => { 3436 const msg = msg: { 3437 const msg = try sema.errMsg(block, src, "error is discarded", .{}); 3438 errdefer msg.destroy(sema.gpa); 3439 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); 3440 break :msg msg; 3441 }; 3442 return sema.failWithOwnedErrorMsg(block, msg); 3443 }, 3444 else => return, 3445 } 3446 } 3447 3448 fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3449 const tracy = trace(@src()); 3450 defer tracy.end(); 3451 3452 const mod = sema.mod; 3453 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3454 const src = inst_data.src(); 3455 const operand = try sema.resolveInst(inst_data.operand); 3456 const operand_ty = sema.typeOf(operand); 3457 const err_union_ty = if (operand_ty.zigTypeTag(mod) == .Pointer) 3458 operand_ty.childType(mod) 3459 else 3460 operand_ty; 3461 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) return; 3462 const payload_ty = err_union_ty.errorUnionPayload(mod).zigTypeTag(mod); 3463 if (payload_ty != .Void and payload_ty != .NoReturn) { 3464 const msg = msg: { 3465 const msg = try sema.errMsg(block, src, "error union payload is ignored", .{}); 3466 errdefer msg.destroy(sema.gpa); 3467 try sema.errNote(block, src, msg, "payload value can be explicitly ignored with '|_|'", .{}); 3468 break :msg msg; 3469 }; 3470 return sema.failWithOwnedErrorMsg(block, msg); 3471 } 3472 } 3473 3474 fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3475 const tracy = trace(@src()); 3476 defer tracy.end(); 3477 3478 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3479 const src = inst_data.src(); 3480 const object = try sema.resolveInst(inst_data.operand); 3481 3482 return indexablePtrLen(sema, block, src, object); 3483 } 3484 3485 fn indexablePtrLen( 3486 sema: *Sema, 3487 block: *Block, 3488 src: LazySrcLoc, 3489 object: Air.Inst.Ref, 3490 ) CompileError!Air.Inst.Ref { 3491 const mod = sema.mod; 3492 const object_ty = sema.typeOf(object); 3493 const is_pointer_to = object_ty.isSinglePointer(mod); 3494 const indexable_ty = if (is_pointer_to) object_ty.childType(mod) else object_ty; 3495 try checkIndexable(sema, block, src, indexable_ty); 3496 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len"); 3497 return sema.fieldVal(block, src, object, field_name, src); 3498 } 3499 3500 fn indexablePtrLenOrNone( 3501 sema: *Sema, 3502 block: *Block, 3503 src: LazySrcLoc, 3504 operand: Air.Inst.Ref, 3505 ) CompileError!Air.Inst.Ref { 3506 const mod = sema.mod; 3507 const operand_ty = sema.typeOf(operand); 3508 try checkMemOperand(sema, block, src, operand_ty); 3509 if (operand_ty.ptrSize(mod) == .Many) return .none; 3510 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len"); 3511 return sema.fieldVal(block, src, operand, field_name, src); 3512 } 3513 3514 fn zirAllocExtended( 3515 sema: *Sema, 3516 block: *Block, 3517 extended: Zir.Inst.Extended.InstData, 3518 ) CompileError!Air.Inst.Ref { 3519 const gpa = sema.gpa; 3520 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); 3521 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = extra.data.src_node }; 3522 const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = extra.data.src_node }; 3523 const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small); 3524 3525 var extra_index: usize = extra.end; 3526 3527 const var_ty: Type = if (small.has_type) blk: { 3528 const type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3529 extra_index += 1; 3530 break :blk try sema.resolveType(block, ty_src, type_ref); 3531 } else undefined; 3532 3533 const alignment = if (small.has_align) blk: { 3534 const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 3535 extra_index += 1; 3536 const alignment = try sema.resolveAlign(block, align_src, align_ref); 3537 break :blk alignment; 3538 } else .none; 3539 3540 if (block.is_comptime or small.is_comptime) { 3541 if (small.has_type) { 3542 return sema.analyzeComptimeAlloc(block, var_ty, alignment); 3543 } else { 3544 try sema.air_instructions.append(gpa, .{ 3545 .tag = .inferred_alloc_comptime, 3546 .data = .{ .inferred_alloc_comptime = .{ 3547 .decl_index = undefined, 3548 .alignment = alignment, 3549 .is_const = small.is_const, 3550 } }, 3551 }); 3552 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 3553 } 3554 } 3555 3556 if (small.has_type) { 3557 if (!small.is_const) { 3558 try sema.validateVarType(block, ty_src, var_ty, false); 3559 } 3560 const target = sema.mod.getTarget(); 3561 try sema.resolveTypeLayout(var_ty); 3562 const ptr_type = try sema.ptrType(.{ 3563 .child = var_ty.toIntern(), 3564 .flags = .{ 3565 .alignment = alignment, 3566 .address_space = target_util.defaultAddressSpace(target, .local), 3567 }, 3568 }); 3569 const ptr = try block.addTy(.alloc, ptr_type); 3570 if (small.is_const) { 3571 const ptr_inst = ptr.toIndex().?; 3572 try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); 3573 try sema.base_allocs.put(gpa, ptr_inst, ptr_inst); 3574 } 3575 return ptr; 3576 } 3577 3578 const result_index = try block.addInstAsIndex(.{ 3579 .tag = .inferred_alloc, 3580 .data = .{ .inferred_alloc = .{ 3581 .alignment = alignment, 3582 .is_const = small.is_const, 3583 } }, 3584 }); 3585 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); 3586 if (small.is_const) { 3587 try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); 3588 try sema.base_allocs.put(gpa, result_index, result_index); 3589 } 3590 return result_index.toRef(); 3591 } 3592 3593 fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3594 const tracy = trace(@src()); 3595 defer tracy.end(); 3596 3597 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3598 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; 3599 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 3600 return sema.analyzeComptimeAlloc(block, var_ty, .none); 3601 } 3602 3603 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3604 const mod = sema.mod; 3605 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3606 const alloc = try sema.resolveInst(inst_data.operand); 3607 const alloc_ty = sema.typeOf(alloc); 3608 const ptr_info = alloc_ty.ptrInfo(mod); 3609 const elem_ty = Type.fromInterned(ptr_info.child); 3610 3611 if (try sema.resolveComptimeKnownAllocValue(block, alloc, null)) |val| { 3612 const new_mut_ptr = Air.internedToRef((try mod.intern(.{ .ptr = .{ 3613 .ty = alloc_ty.toIntern(), 3614 .addr = .{ .anon_decl = .{ 3615 .val = val, 3616 .orig_ty = alloc_ty.toIntern(), 3617 } }, 3618 } }))); 3619 return sema.makePtrConst(block, new_mut_ptr); 3620 } 3621 3622 // If this is already a comptime-known allocation, we don't want to emit an error - the stores 3623 // were already performed at comptime! Just make the pointer constant as normal. 3624 implicit_ct: { 3625 const ptr_val = try sema.resolveValue(alloc) orelse break :implicit_ct; 3626 if (!ptr_val.isComptimeMutablePtr(mod)) { 3627 // It could still be a constant pointer to a decl. 3628 switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) { 3629 .anon_decl => |anon_decl| { 3630 if (mod.intern_pool.isVariable(anon_decl.val)) 3631 break :implicit_ct; 3632 }, 3633 else => { 3634 const decl_index = ptr_val.pointerDecl(mod) orelse break :implicit_ct; 3635 const decl_val = mod.declPtr(decl_index).val.toIntern(); 3636 if (mod.intern_pool.isVariable(decl_val)) break :implicit_ct; 3637 }, 3638 } 3639 } 3640 return sema.makePtrConst(block, alloc); 3641 } 3642 3643 if (try sema.typeRequiresComptime(elem_ty)) { 3644 // The value was initialized through RLS, so we didn't detect the runtime condition earlier. 3645 // TODO: source location of runtime control flow 3646 const init_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 3647 return sema.fail(block, init_src, "value with comptime-only type '{}' depends on runtime control flow", .{elem_ty.fmt(mod)}); 3648 } 3649 3650 // This is a runtime value. 3651 return sema.makePtrConst(block, alloc); 3652 } 3653 3654 /// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved 3655 /// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`. 3656 fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index { 3657 const mod = sema.mod; 3658 3659 const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc); 3660 const ptr_info = alloc_ty.ptrInfo(mod); 3661 const elem_ty = Type.fromInterned(ptr_info.child); 3662 3663 const alloc_inst = alloc.toIndex() orelse return null; 3664 const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null; 3665 const stores = comptime_info.value.stores.items; 3666 3667 // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known. 3668 // We will resolve and return its value. 3669 3670 // We expect to have emitted at least one store, unless the elem type is OPV. 3671 if (stores.len == 0) { 3672 const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern(); 3673 return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value); 3674 } 3675 3676 // In general, we want to create a comptime alloc of the correct type and 3677 // apply the stores to that alloc in order. However, before going to all 3678 // that effort, let's optimize for the common case of a single store. 3679 3680 simple: { 3681 if (stores.len != 1) break :simple; 3682 const store_inst = stores[0]; 3683 const store_data = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 3684 if (store_data.lhs != alloc) break :simple; 3685 3686 const val = store_data.rhs.toInterned().?; 3687 assert(mod.intern_pool.typeOf(val) == elem_ty.toIntern()); 3688 return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value); 3689 } 3690 3691 // The simple strategy failed: we must create a mutable comptime alloc and 3692 // perform all of the runtime store operations at comptime. 3693 3694 var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl 3695 defer anon_decl.deinit(); 3696 const decl_index = try anon_decl.finish(elem_ty, try mod.undefValue(elem_ty), ptr_info.flags.alignment); 3697 3698 const decl_ptr = try mod.intern(.{ .ptr = .{ 3699 .ty = alloc_ty.toIntern(), 3700 .addr = .{ .mut_decl = .{ 3701 .decl = decl_index, 3702 .runtime_index = block.runtime_index, 3703 } }, 3704 } }); 3705 3706 // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the mut decl. 3707 var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena); 3708 try ptr_mapping.ensureTotalCapacity(@intCast(stores.len)); 3709 ptr_mapping.putAssumeCapacity(alloc_inst, decl_ptr); 3710 3711 var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len); 3712 for (stores) |store_inst| { 3713 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 3714 to_map.appendAssumeCapacity(bin_op.lhs.toIndex().?); 3715 } 3716 3717 const tmp_air = sema.getTmpAir(); 3718 3719 while (to_map.popOrNull()) |air_ptr| { 3720 if (ptr_mapping.contains(air_ptr)) continue; 3721 const PointerMethod = union(enum) { 3722 same_addr, 3723 opt_payload, 3724 eu_payload, 3725 field: u32, 3726 elem: u64, 3727 }; 3728 const inst_tag = tmp_air.instructions.items(.tag)[@intFromEnum(air_ptr)]; 3729 const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) { 3730 .struct_field_ptr => blk: { 3731 const data = tmp_air.extraData( 3732 Air.StructField, 3733 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload, 3734 ).data; 3735 break :blk .{ 3736 data.struct_operand, 3737 .{ .field = data.field_index }, 3738 }; 3739 }, 3740 .struct_field_ptr_index_0, 3741 .struct_field_ptr_index_1, 3742 .struct_field_ptr_index_2, 3743 .struct_field_ptr_index_3, 3744 => .{ 3745 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3746 .{ .field = switch (inst_tag) { 3747 .struct_field_ptr_index_0 => 0, 3748 .struct_field_ptr_index_1 => 1, 3749 .struct_field_ptr_index_2 => 2, 3750 .struct_field_ptr_index_3 => 3, 3751 else => unreachable, 3752 } }, 3753 }, 3754 .ptr_slice_ptr_ptr => .{ 3755 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3756 .{ .field = Value.slice_ptr_index }, 3757 }, 3758 .ptr_slice_len_ptr => .{ 3759 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3760 .{ .field = Value.slice_len_index }, 3761 }, 3762 .ptr_elem_ptr => blk: { 3763 const data = tmp_air.extraData( 3764 Air.Bin, 3765 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload, 3766 ).data; 3767 const idx_val = (try sema.resolveValue(data.rhs)).?; 3768 break :blk .{ 3769 data.lhs, 3770 .{ .elem = idx_val.toUnsignedInt(mod) }, 3771 }; 3772 }, 3773 .bitcast => .{ 3774 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3775 .same_addr, 3776 }, 3777 .optional_payload_ptr_set => .{ 3778 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3779 .opt_payload, 3780 }, 3781 .errunion_payload_ptr_set => .{ 3782 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand, 3783 .eu_payload, 3784 }, 3785 else => unreachable, 3786 }; 3787 3788 const decl_parent_ptr = ptr_mapping.get(air_parent_ptr.toIndex().?) orelse { 3789 // Resolve the parent pointer first. 3790 // Note that we add in what seems like the wrong order, because we're popping from the end of this array. 3791 try to_map.appendSlice(&.{ air_ptr, air_parent_ptr.toIndex().? }); 3792 continue; 3793 }; 3794 const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &mod.intern_pool).toIntern(); 3795 const new_ptr = switch (method) { 3796 .same_addr => try mod.intern_pool.getCoerced(sema.gpa, decl_parent_ptr, new_ptr_ty), 3797 .opt_payload => try mod.intern(.{ .ptr = .{ 3798 .ty = new_ptr_ty, 3799 .addr = .{ .opt_payload = decl_parent_ptr }, 3800 } }), 3801 .eu_payload => try mod.intern(.{ .ptr = .{ 3802 .ty = new_ptr_ty, 3803 .addr = .{ .eu_payload = decl_parent_ptr }, 3804 } }), 3805 .field => |field_idx| try mod.intern(.{ .ptr = .{ 3806 .ty = new_ptr_ty, 3807 .addr = .{ .field = .{ 3808 .base = decl_parent_ptr, 3809 .index = field_idx, 3810 } }, 3811 } }), 3812 .elem => |elem_idx| (try Value.fromInterned(decl_parent_ptr).elemPtr(Type.fromInterned(new_ptr_ty), @intCast(elem_idx), mod)).toIntern(), 3813 }; 3814 try ptr_mapping.put(air_ptr, new_ptr); 3815 } 3816 3817 // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime. 3818 3819 for (stores) |store_inst| { 3820 switch (sema.air_instructions.items(.tag)[@intFromEnum(store_inst)]) { 3821 .set_union_tag => { 3822 // If this tag has an OPV payload, there won't be a corresponding 3823 // store instruction, so we must set the union payload now. 3824 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 3825 const air_ptr_inst = bin_op.lhs.toIndex().?; 3826 const tag_val = (try sema.resolveValue(bin_op.rhs)).?; 3827 const union_ty = sema.typeOf(bin_op.lhs).childType(mod); 3828 const payload_ty = union_ty.unionFieldType(tag_val, mod).?; 3829 if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| { 3830 const new_ptr = ptr_mapping.get(air_ptr_inst).?; 3831 const store_val = try mod.unionValue(union_ty, tag_val, payload_val); 3832 try sema.storePtrVal(block, .unneeded, Value.fromInterned(new_ptr), store_val, union_ty); 3833 } 3834 }, 3835 .store, .store_safe => { 3836 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 3837 const air_ptr_inst = bin_op.lhs.toIndex().?; 3838 const store_val = (try sema.resolveValue(bin_op.rhs)).?; 3839 const new_ptr = ptr_mapping.get(air_ptr_inst).?; 3840 try sema.storePtrVal(block, .unneeded, Value.fromInterned(new_ptr), store_val, Type.fromInterned(mod.intern_pool.typeOf(store_val.toIntern()))); 3841 }, 3842 else => unreachable, 3843 } 3844 } 3845 3846 // The value is finalized - load it! 3847 const val = (try sema.pointerDeref(block, .unneeded, Value.fromInterned(decl_ptr), alloc_ty)).?.toIntern(); 3848 return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value); 3849 } 3850 3851 /// Given the resolved comptime-known value, rewrites the dead AIR to not 3852 /// create a runtime stack allocation. 3853 /// Same return type as `resolveComptimeKnownAllocValue` so we can tail call. 3854 fn finishResolveComptimeKnownAllocValue(sema: *Sema, result_val: InternPool.Index, alloc_inst: Air.Inst.Index, comptime_info: MaybeComptimeAlloc) CompileError!?InternPool.Index { 3855 // We're almost done - we have the resolved comptime value. We just need to 3856 // eliminate the now-dead runtime instructions. 3857 3858 // We will rewrite the AIR to eliminate the alloc and all stores to it. 3859 // This will cause instructions deriving field pointers etc of the alloc to 3860 // become invalid, however, since we are removing all stores to those pointers, 3861 // they will be eliminated by Liveness before they reach codegen. 3862 3863 // The specifics of this instruction aren't really important: we just want 3864 // Liveness to elide it. 3865 const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{ .ty = .u8_type, .operand = .zero_u8 } } }; 3866 3867 sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst); 3868 for (comptime_info.stores.items) |store_inst| { 3869 sema.air_instructions.set(@intFromEnum(store_inst), nop_inst); 3870 } 3871 for (comptime_info.non_elideable_pointers.items) |ptr_inst| { 3872 sema.air_instructions.set(@intFromEnum(ptr_inst), nop_inst); 3873 } 3874 3875 return result_val; 3876 } 3877 3878 fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type { 3879 var ptr_info = ptr_ty.ptrInfo(sema.mod); 3880 ptr_info.flags.is_const = true; 3881 return sema.ptrType(ptr_info); 3882 } 3883 3884 fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { 3885 const alloc_ty = sema.typeOf(alloc); 3886 const const_ptr_ty = try sema.makePtrTyConst(alloc_ty); 3887 3888 // Detect if a comptime value simply needs to have its type changed. 3889 if (try sema.resolveValue(alloc)) |val| { 3890 return Air.internedToRef((try sema.mod.getCoerced(val, const_ptr_ty)).toIntern()); 3891 } 3892 3893 return block.addBitCast(const_ptr_ty, alloc); 3894 } 3895 3896 fn zirAllocInferredComptime( 3897 sema: *Sema, 3898 inst: Zir.Inst.Index, 3899 is_const: bool, 3900 ) CompileError!Air.Inst.Ref { 3901 const gpa = sema.gpa; 3902 const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node; 3903 const src = LazySrcLoc.nodeOffset(src_node); 3904 sema.src = src; 3905 3906 try sema.air_instructions.append(gpa, .{ 3907 .tag = .inferred_alloc_comptime, 3908 .data = .{ .inferred_alloc_comptime = .{ 3909 .decl_index = undefined, 3910 .alignment = .none, 3911 .is_const = is_const, 3912 } }, 3913 }); 3914 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 3915 } 3916 3917 fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3918 const tracy = trace(@src()); 3919 defer tracy.end(); 3920 3921 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3922 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; 3923 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 3924 if (block.is_comptime) { 3925 return sema.analyzeComptimeAlloc(block, var_ty, .none); 3926 } 3927 const target = sema.mod.getTarget(); 3928 const ptr_type = try sema.ptrType(.{ 3929 .child = var_ty.toIntern(), 3930 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 3931 }); 3932 try sema.queueFullTypeResolution(var_ty); 3933 const ptr = try block.addTy(.alloc, ptr_type); 3934 const ptr_inst = ptr.toIndex().?; 3935 try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); 3936 try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst); 3937 return ptr; 3938 } 3939 3940 fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3941 const tracy = trace(@src()); 3942 defer tracy.end(); 3943 3944 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 3945 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; 3946 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 3947 if (block.is_comptime) { 3948 return sema.analyzeComptimeAlloc(block, var_ty, .none); 3949 } 3950 try sema.validateVarType(block, ty_src, var_ty, false); 3951 const target = sema.mod.getTarget(); 3952 const ptr_type = try sema.ptrType(.{ 3953 .child = var_ty.toIntern(), 3954 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 3955 }); 3956 try sema.queueFullTypeResolution(var_ty); 3957 return block.addTy(.alloc, ptr_type); 3958 } 3959 3960 fn zirAllocInferred( 3961 sema: *Sema, 3962 block: *Block, 3963 inst: Zir.Inst.Index, 3964 is_const: bool, 3965 ) CompileError!Air.Inst.Ref { 3966 const tracy = trace(@src()); 3967 defer tracy.end(); 3968 3969 const gpa = sema.gpa; 3970 const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node; 3971 const src = LazySrcLoc.nodeOffset(src_node); 3972 sema.src = src; 3973 3974 if (block.is_comptime) { 3975 try sema.air_instructions.append(gpa, .{ 3976 .tag = .inferred_alloc_comptime, 3977 .data = .{ .inferred_alloc_comptime = .{ 3978 .decl_index = undefined, 3979 .alignment = .none, 3980 .is_const = is_const, 3981 } }, 3982 }); 3983 return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef(); 3984 } 3985 3986 const result_index = try block.addInstAsIndex(.{ 3987 .tag = .inferred_alloc, 3988 .data = .{ .inferred_alloc = .{ 3989 .alignment = .none, 3990 .is_const = is_const, 3991 } }, 3992 }); 3993 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); 3994 try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); 3995 try sema.base_allocs.put(sema.gpa, result_index, result_index); 3996 return result_index.toRef(); 3997 } 3998 3999 fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 4000 const tracy = trace(@src()); 4001 defer tracy.end(); 4002 4003 const mod = sema.mod; 4004 const gpa = sema.gpa; 4005 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4006 const src = inst_data.src(); 4007 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; 4008 const ptr = try sema.resolveInst(inst_data.operand); 4009 const ptr_inst = ptr.toIndex().?; 4010 const target = mod.getTarget(); 4011 4012 switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) { 4013 .inferred_alloc_comptime => { 4014 const iac = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc_comptime; 4015 const decl_index = iac.decl_index; 4016 4017 const decl = mod.declPtr(decl_index); 4018 if (iac.is_const) _ = try decl.internValue(mod); 4019 const final_elem_ty = decl.ty; 4020 const final_ptr_ty = try sema.ptrType(.{ 4021 .child = final_elem_ty.toIntern(), 4022 .flags = .{ 4023 .is_const = false, 4024 .alignment = iac.alignment, 4025 .address_space = target_util.defaultAddressSpace(target, .local), 4026 }, 4027 }); 4028 4029 if (std.debug.runtime_safety) { 4030 // The inferred_alloc_comptime should never be referenced again 4031 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ .tag = undefined, .data = undefined }); 4032 } 4033 4034 try sema.maybeQueueFuncBodyAnalysis(decl_index); 4035 4036 const interned = try mod.intern(.{ .ptr = .{ 4037 .ty = final_ptr_ty.toIntern(), 4038 .addr = if (!iac.is_const) .{ .mut_decl = .{ 4039 .decl = decl_index, 4040 .runtime_index = block.runtime_index, 4041 } } else .{ .decl = decl_index }, 4042 } }); 4043 4044 // Remap the ZIR operand to the resolved pointer value 4045 sema.inst_map.putAssumeCapacity(inst_data.operand.toIndex().?, Air.internedToRef(interned)); 4046 }, 4047 .inferred_alloc => { 4048 const ia1 = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc; 4049 const ia2 = sema.unresolved_inferred_allocs.fetchSwapRemove(ptr_inst).?.value; 4050 const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len); 4051 for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| { 4052 assert(sema.air_instructions.items(.tag)[@intFromEnum(store_inst)] == .store); 4053 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 4054 peer_val.* = bin_op.rhs; 4055 } 4056 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none); 4057 4058 const final_ptr_ty = try sema.ptrType(.{ 4059 .child = final_elem_ty.toIntern(), 4060 .flags = .{ 4061 .alignment = ia1.alignment, 4062 .address_space = target_util.defaultAddressSpace(target, .local), 4063 }, 4064 }); 4065 4066 if (!ia1.is_const) { 4067 try sema.validateVarType(block, ty_src, final_elem_ty, false); 4068 } else if (try sema.resolveComptimeKnownAllocValue(block, ptr, final_ptr_ty)) |val| { 4069 const const_ptr_ty = (try sema.makePtrTyConst(final_ptr_ty)).toIntern(); 4070 const new_const_ptr = try mod.intern(.{ .ptr = .{ 4071 .ty = const_ptr_ty, 4072 .addr = .{ .anon_decl = .{ 4073 .val = val, 4074 .orig_ty = const_ptr_ty, 4075 } }, 4076 } }); 4077 4078 // Remap the ZIR oeprand to the resolved pointer value 4079 sema.inst_map.putAssumeCapacity(inst_data.operand.toIndex().?, Air.internedToRef(new_const_ptr)); 4080 4081 // Unless the block is comptime, `alloc_inferred` always produces 4082 // a runtime constant. The final inferred type needs to be 4083 // fully resolved so it can be lowered in codegen. 4084 try sema.resolveTypeFully(final_elem_ty); 4085 4086 return; 4087 } 4088 4089 try sema.queueFullTypeResolution(final_elem_ty); 4090 4091 // Change it to a normal alloc. 4092 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ 4093 .tag = .alloc, 4094 .data = .{ .ty = final_ptr_ty }, 4095 }); 4096 4097 // Now we need to go back over all the store instructions, and do the logic as if 4098 // the new result ptr type was available. 4099 4100 for (ia2.prongs.items) |placeholder_inst| { 4101 var replacement_block = block.makeSubBlock(); 4102 defer replacement_block.instructions.deinit(gpa); 4103 4104 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .store); 4105 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].bin_op; 4106 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store); 4107 4108 // If only one instruction is produced then we can replace the store 4109 // placeholder instruction with this instruction; no need for an entire block. 4110 if (replacement_block.instructions.items.len == 1) { 4111 const only_inst = replacement_block.instructions.items[0]; 4112 sema.air_instructions.set(@intFromEnum(placeholder_inst), sema.air_instructions.get(@intFromEnum(only_inst))); 4113 continue; 4114 } 4115 4116 // Here we replace the placeholder store instruction with a block 4117 // that does the actual store logic. 4118 _ = try replacement_block.addBr(placeholder_inst, .void_value); 4119 try sema.air_extra.ensureUnusedCapacity( 4120 gpa, 4121 @typeInfo(Air.Block).Struct.fields.len + replacement_block.instructions.items.len, 4122 ); 4123 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{ 4124 .tag = .block, 4125 .data = .{ .ty_pl = .{ 4126 .ty = .void_type, 4127 .payload = sema.addExtraAssumeCapacity(Air.Block{ 4128 .body_len = @intCast(replacement_block.instructions.items.len), 4129 }), 4130 } }, 4131 }); 4132 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); 4133 } 4134 }, 4135 else => unreachable, 4136 } 4137 } 4138 4139 fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4140 const mod = sema.mod; 4141 const gpa = sema.gpa; 4142 const ip = &mod.intern_pool; 4143 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4144 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 4145 const args = sema.code.refSlice(extra.end, extra.data.operands_len); 4146 const src = inst_data.src(); 4147 4148 var len: Air.Inst.Ref = .none; 4149 var len_val: ?Value = null; 4150 var len_idx: u32 = undefined; 4151 var any_runtime = false; 4152 4153 const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, args.len); 4154 defer gpa.free(runtime_arg_lens); 4155 4156 // First pass to look for comptime values. 4157 for (args, 0..) |zir_arg, i_usize| { 4158 const i: u32 = @intCast(i_usize); 4159 runtime_arg_lens[i] = .none; 4160 if (zir_arg == .none) continue; 4161 const object = try sema.resolveInst(zir_arg); 4162 const object_ty = sema.typeOf(object); 4163 // Each arg could be an indexable, or a range, in which case the length 4164 // is passed directly as an integer. 4165 const is_int = switch (object_ty.zigTypeTag(mod)) { 4166 .Int, .ComptimeInt => true, 4167 else => false, 4168 }; 4169 const arg_src: LazySrcLoc = .{ .for_input = .{ 4170 .for_node_offset = inst_data.src_node, 4171 .input_index = i, 4172 } }; 4173 const arg_len_uncoerced = if (is_int) object else l: { 4174 if (!object_ty.isIndexable(mod)) { 4175 // Instead of using checkIndexable we customize this error. 4176 const msg = msg: { 4177 const msg = try sema.errMsg(block, arg_src, "type '{}' is not indexable and not a range", .{object_ty.fmt(sema.mod)}); 4178 errdefer msg.destroy(sema.gpa); 4179 try sema.errNote(block, arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{}); 4180 break :msg msg; 4181 }; 4182 return sema.failWithOwnedErrorMsg(block, msg); 4183 } 4184 if (!object_ty.indexableHasLen(mod)) continue; 4185 4186 break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, "len"), arg_src); 4187 }; 4188 const arg_len = try sema.coerce(block, Type.usize, arg_len_uncoerced, arg_src); 4189 if (len == .none) { 4190 len = arg_len; 4191 len_idx = i; 4192 } 4193 if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| { 4194 if (len_val) |v| { 4195 if (!(try sema.valuesEqual(arg_val, v, Type.usize))) { 4196 const msg = msg: { 4197 const msg = try sema.errMsg(block, src, "non-matching for loop lengths", .{}); 4198 errdefer msg.destroy(gpa); 4199 const a_src: LazySrcLoc = .{ .for_input = .{ 4200 .for_node_offset = inst_data.src_node, 4201 .input_index = len_idx, 4202 } }; 4203 try sema.errNote(block, a_src, msg, "length {} here", .{ 4204 v.fmtValue(Type.usize, sema.mod), 4205 }); 4206 try sema.errNote(block, arg_src, msg, "length {} here", .{ 4207 arg_val.fmtValue(Type.usize, sema.mod), 4208 }); 4209 break :msg msg; 4210 }; 4211 return sema.failWithOwnedErrorMsg(block, msg); 4212 } 4213 } else { 4214 len = arg_len; 4215 len_val = arg_val; 4216 len_idx = i; 4217 } 4218 continue; 4219 } 4220 runtime_arg_lens[i] = arg_len; 4221 any_runtime = true; 4222 } 4223 4224 if (len == .none) { 4225 const msg = msg: { 4226 const msg = try sema.errMsg(block, src, "unbounded for loop", .{}); 4227 errdefer msg.destroy(gpa); 4228 for (args, 0..) |zir_arg, i_usize| { 4229 const i: u32 = @intCast(i_usize); 4230 if (zir_arg == .none) continue; 4231 const object = try sema.resolveInst(zir_arg); 4232 const object_ty = sema.typeOf(object); 4233 // Each arg could be an indexable, or a range, in which case the length 4234 // is passed directly as an integer. 4235 switch (object_ty.zigTypeTag(mod)) { 4236 .Int, .ComptimeInt => continue, 4237 else => {}, 4238 } 4239 const arg_src: LazySrcLoc = .{ .for_input = .{ 4240 .for_node_offset = inst_data.src_node, 4241 .input_index = i, 4242 } }; 4243 try sema.errNote(block, arg_src, msg, "type '{}' has no upper bound", .{ 4244 object_ty.fmt(sema.mod), 4245 }); 4246 } 4247 break :msg msg; 4248 }; 4249 return sema.failWithOwnedErrorMsg(block, msg); 4250 } 4251 4252 // Now for the runtime checks. 4253 if (any_runtime and block.wantSafety()) { 4254 for (runtime_arg_lens, 0..) |arg_len, i| { 4255 if (arg_len == .none) continue; 4256 if (i == len_idx) continue; 4257 const ok = try block.addBinOp(.cmp_eq, len, arg_len); 4258 try sema.addSafetyCheck(block, src, ok, .for_len_mismatch); 4259 } 4260 } 4261 4262 return len; 4263 } 4264 4265 /// Given any single pointer, retrieve a pointer to the payload of any optional 4266 /// or error union pointed to, initializing these pointers along the way. 4267 /// Given a `*E!?T`, returns a (valid) `*T`. 4268 /// May invalidate already-stored payload data. 4269 fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref { 4270 const mod = sema.mod; 4271 var base_ptr = ptr; 4272 while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) { 4273 .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), 4274 .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), 4275 else => break, 4276 }; 4277 try sema.checkKnownAllocPtr(ptr, base_ptr); 4278 return base_ptr; 4279 } 4280 4281 fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4282 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4283 const ptr = try sema.resolveInst(un_node.operand); 4284 return sema.optEuBasePtrInit(block, ptr, un_node.src()); 4285 } 4286 4287 fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4288 const mod = sema.mod; 4289 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4290 const src = pl_node.src(); 4291 const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; 4292 const uncoerced_val = try sema.resolveInst(extra.rhs); 4293 const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, extra.lhs) catch |err| switch (err) { 4294 error.GenericPoison => return uncoerced_val, 4295 else => |e| return e, 4296 }; 4297 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod); 4298 assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction 4299 const elem_ty = ptr_ty.childType(mod); 4300 switch (ptr_ty.ptrSize(mod)) { 4301 .One => { 4302 const uncoerced_ty = sema.typeOf(uncoerced_val); 4303 if (elem_ty.zigTypeTag(mod) == .Array and elem_ty.childType(mod).toIntern() == uncoerced_ty.toIntern()) { 4304 // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion. 4305 return uncoerced_val; 4306 } 4307 // If the destination type is anyopaque, don't coerce - the pointer will coerce instead. 4308 if (elem_ty.toIntern() == .anyopaque_type) { 4309 return uncoerced_val; 4310 } else { 4311 return sema.coerce(block, elem_ty, uncoerced_val, src); 4312 } 4313 }, 4314 .Slice, .Many => { 4315 // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`. 4316 const val_ty = sema.typeOf(uncoerced_val); 4317 switch (val_ty.zigTypeTag(mod)) { 4318 .Array, .Vector => {}, 4319 else => if (!val_ty.isTuple(mod)) { 4320 return sema.fail(block, src, "expected array of '{}', found '{}'", .{ elem_ty.fmt(mod), val_ty.fmt(mod) }); 4321 }, 4322 } 4323 const want_ty = try mod.arrayType(.{ 4324 .len = val_ty.arrayLen(mod), 4325 .child = elem_ty.toIntern(), 4326 .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none, 4327 }); 4328 return sema.coerce(block, want_ty, uncoerced_val, src); 4329 }, 4330 .C => { 4331 // There's nothing meaningful to do here, because we don't know if this is meant to be a 4332 // single-pointer or a many-pointer. 4333 return uncoerced_val; 4334 }, 4335 } 4336 } 4337 4338 fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 4339 const mod = sema.mod; 4340 const un_tok = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 4341 const src = un_tok.src(); 4342 // In case of GenericPoison, we don't actually have a type, so this will be 4343 // treated as an untyped address-of operator. 4344 if (un_tok.operand == .var_args_param_type) return; 4345 const operand_air_inst = sema.resolveInst(un_tok.operand) catch |err| switch (err) { 4346 error.GenericPoison => return, 4347 else => |e| return e, 4348 }; 4349 if (operand_air_inst == .var_args_param_type) return; 4350 const ty_operand = sema.analyzeAsType(block, src, operand_air_inst) catch |err| switch (err) { 4351 error.GenericPoison => return, 4352 else => |e| return e, 4353 }; 4354 if (ty_operand.isGenericPoison()) return; 4355 if (ty_operand.optEuBaseType(mod).zigTypeTag(mod) != .Pointer) { 4356 return sema.failWithOwnedErrorMsg(block, msg: { 4357 const msg = try sema.errMsg(block, src, "expected type '{}', found pointer", .{ty_operand.fmt(mod)}); 4358 errdefer msg.destroy(sema.gpa); 4359 try sema.errNote(block, src, msg, "address-of operator always returns a pointer", .{}); 4360 break :msg msg; 4361 }); 4362 } 4363 } 4364 4365 fn zirValidateArrayInitRefTy( 4366 sema: *Sema, 4367 block: *Block, 4368 inst: Zir.Inst.Index, 4369 ) CompileError!Air.Inst.Ref { 4370 const mod = sema.mod; 4371 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4372 const src = pl_node.src(); 4373 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; 4374 const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, extra.ptr_ty) catch |err| switch (err) { 4375 error.GenericPoison => return .generic_poison_type, 4376 else => |e| return e, 4377 }; 4378 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod); 4379 assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction 4380 if (ptr_ty.isSlice(mod)) { 4381 // Use array of correct length 4382 const arr_ty = try mod.arrayType(.{ 4383 .len = extra.elem_count, 4384 .child = ptr_ty.childType(mod).toIntern(), 4385 .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none, 4386 }); 4387 return Air.internedToRef(arr_ty.toIntern()); 4388 } 4389 // Otherwise, we just want the pointer child type 4390 const ret_ty = ptr_ty.childType(mod); 4391 if (ret_ty.toIntern() == .anyopaque_type) { 4392 // The actual array type is unknown, which we represent with a generic poison. 4393 return .generic_poison_type; 4394 } 4395 const arr_ty = ret_ty.optEuBaseType(mod); 4396 try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty); 4397 return Air.internedToRef(ret_ty.toIntern()); 4398 } 4399 4400 fn zirValidateArrayInitTy( 4401 sema: *Sema, 4402 block: *Block, 4403 inst: Zir.Inst.Index, 4404 is_result_ty: bool, 4405 ) CompileError!void { 4406 const mod = sema.mod; 4407 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4408 const src = inst_data.src(); 4409 const ty_src: LazySrcLoc = if (is_result_ty) src else .{ .node_offset_init_ty = inst_data.src_node }; 4410 const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data; 4411 const ty = sema.resolveType(block, ty_src, extra.ty) catch |err| switch (err) { 4412 // It's okay for the type to be unknown: this will result in an anonymous array init. 4413 error.GenericPoison => return, 4414 else => |e| return e, 4415 }; 4416 const arr_ty = if (is_result_ty) ty.optEuBaseType(mod) else ty; 4417 return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty); 4418 } 4419 4420 fn validateArrayInitTy( 4421 sema: *Sema, 4422 block: *Block, 4423 src: LazySrcLoc, 4424 ty_src: LazySrcLoc, 4425 init_count: u32, 4426 ty: Type, 4427 ) CompileError!void { 4428 const mod = sema.mod; 4429 switch (ty.zigTypeTag(mod)) { 4430 .Array => { 4431 const array_len = ty.arrayLen(mod); 4432 if (init_count != array_len) { 4433 return sema.fail(block, src, "expected {d} array elements; found {d}", .{ 4434 array_len, init_count, 4435 }); 4436 } 4437 return; 4438 }, 4439 .Vector => { 4440 const array_len = ty.arrayLen(mod); 4441 if (init_count != array_len) { 4442 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{ 4443 array_len, init_count, 4444 }); 4445 } 4446 return; 4447 }, 4448 .Struct => if (ty.isTuple(mod)) { 4449 try sema.resolveTypeFields(ty); 4450 const array_len = ty.arrayLen(mod); 4451 if (init_count > array_len) { 4452 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ 4453 array_len, init_count, 4454 }); 4455 } 4456 return; 4457 }, 4458 else => {}, 4459 } 4460 return sema.failWithArrayInitNotSupported(block, ty_src, ty); 4461 } 4462 4463 fn zirValidateStructInitTy( 4464 sema: *Sema, 4465 block: *Block, 4466 inst: Zir.Inst.Index, 4467 is_result_ty: bool, 4468 ) CompileError!void { 4469 const mod = sema.mod; 4470 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 4471 const src = inst_data.src(); 4472 const ty = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) { 4473 // It's okay for the type to be unknown: this will result in an anonymous struct init. 4474 error.GenericPoison => return, 4475 else => |e| return e, 4476 }; 4477 const struct_ty = if (is_result_ty) ty.optEuBaseType(mod) else ty; 4478 4479 switch (struct_ty.zigTypeTag(mod)) { 4480 .Struct, .Union => return, 4481 else => {}, 4482 } 4483 return sema.failWithStructInitNotSupported(block, src, struct_ty); 4484 } 4485 4486 fn zirValidatePtrStructInit( 4487 sema: *Sema, 4488 block: *Block, 4489 inst: Zir.Inst.Index, 4490 ) CompileError!void { 4491 const tracy = trace(@src()); 4492 defer tracy.end(); 4493 4494 const mod = sema.mod; 4495 const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4496 const init_src = validate_inst.src(); 4497 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); 4498 const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); 4499 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node; 4500 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4501 const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); 4502 const agg_ty = sema.typeOf(object_ptr).childType(mod).optEuBaseType(mod); 4503 switch (agg_ty.zigTypeTag(mod)) { 4504 .Struct => return sema.validateStructInit( 4505 block, 4506 agg_ty, 4507 init_src, 4508 instrs, 4509 ), 4510 .Union => return sema.validateUnionInit( 4511 block, 4512 agg_ty, 4513 init_src, 4514 instrs, 4515 object_ptr, 4516 ), 4517 else => unreachable, 4518 } 4519 } 4520 4521 fn validateUnionInit( 4522 sema: *Sema, 4523 block: *Block, 4524 union_ty: Type, 4525 init_src: LazySrcLoc, 4526 instrs: []const Zir.Inst.Index, 4527 union_ptr: Air.Inst.Ref, 4528 ) CompileError!void { 4529 const mod = sema.mod; 4530 const gpa = sema.gpa; 4531 4532 if (instrs.len != 1) { 4533 const msg = msg: { 4534 const msg = try sema.errMsg( 4535 block, 4536 init_src, 4537 "cannot initialize multiple union fields at once; unions can only have one active field", 4538 .{}, 4539 ); 4540 errdefer msg.destroy(gpa); 4541 4542 for (instrs[1..]) |inst| { 4543 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4544 const inst_src: LazySrcLoc = .{ .node_offset_initializer = inst_data.src_node }; 4545 try sema.errNote(block, inst_src, msg, "additional initializer here", .{}); 4546 } 4547 try sema.addDeclaredHereNote(msg, union_ty); 4548 break :msg msg; 4549 }; 4550 return sema.failWithOwnedErrorMsg(block, msg); 4551 } 4552 4553 if (block.is_comptime and 4554 (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null) 4555 { 4556 // In this case, comptime machinery already did everything. No work to do here. 4557 return; 4558 } 4559 4560 const field_ptr = instrs[0]; 4561 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node; 4562 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node }; 4563 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4564 const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start)); 4565 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); 4566 const air_tags = sema.air_instructions.items(.tag); 4567 const air_datas = sema.air_instructions.items(.data); 4568 const field_ptr_ref = sema.inst_map.get(field_ptr).?; 4569 4570 // Our task here is to determine if the union is comptime-known. In such case, 4571 // we erase the runtime AIR instructions for initializing the union, and replace 4572 // the mapping with the comptime value. Either way, we will need to populate the tag. 4573 4574 // We expect to see something like this in the current block AIR: 4575 // %a = alloc(*const U) 4576 // %b = bitcast(*U, %a) 4577 // %c = field_ptr(..., %b) 4578 // %e!= store(%c!, %d!) 4579 // If %d is a comptime operand, the union is comptime. 4580 // If the union is comptime, we want `first_block_index` 4581 // to point at %c so that the bitcast becomes the last instruction in the block. 4582 // 4583 // In the case of a comptime-known pointer to a union, the 4584 // the field_ptr instruction is missing, so we have to pattern-match 4585 // based only on the store instructions. 4586 // `first_block_index` needs to point to the `field_ptr` if it exists; 4587 // the `store` otherwise. 4588 var first_block_index = block.instructions.items.len; 4589 var block_index = block.instructions.items.len - 1; 4590 var init_val: ?Value = null; 4591 while (block_index > 0) : (block_index -= 1) { 4592 const store_inst = block.instructions.items[block_index]; 4593 if (store_inst.toRef() == field_ptr_ref) break; 4594 switch (air_tags[@intFromEnum(store_inst)]) { 4595 .store, .store_safe => {}, 4596 else => continue, 4597 } 4598 const bin_op = air_datas[@intFromEnum(store_inst)].bin_op; 4599 var ptr_ref = bin_op.lhs; 4600 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 4601 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 4602 }; 4603 if (ptr_ref != field_ptr_ref) continue; 4604 first_block_index = @min(if (field_ptr_ref.toIndex()) |field_ptr_inst| 4605 std.mem.lastIndexOfScalar( 4606 Air.Inst.Index, 4607 block.instructions.items[0..block_index], 4608 field_ptr_inst, 4609 ).? 4610 else 4611 block_index, first_block_index); 4612 init_val = try sema.resolveValue(bin_op.rhs); 4613 break; 4614 } 4615 4616 const tag_ty = union_ty.unionTagTypeHypothetical(mod); 4617 const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); 4618 4619 if (init_val) |val| { 4620 // Our task is to delete all the `field_ptr` and `store` instructions, and insert 4621 // instead a single `store` to the result ptr with a comptime union value. 4622 block_index = first_block_index; 4623 for (block.instructions.items[first_block_index..]) |cur_inst| { 4624 switch (air_tags[@intFromEnum(cur_inst)]) { 4625 .struct_field_ptr, 4626 .struct_field_ptr_index_0, 4627 .struct_field_ptr_index_1, 4628 .struct_field_ptr_index_2, 4629 .struct_field_ptr_index_3, 4630 => if (cur_inst.toRef() == field_ptr_ref) continue, 4631 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == field_ptr_ref) continue, 4632 .store, .store_safe => { 4633 var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs; 4634 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 4635 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 4636 }; 4637 if (ptr_ref == field_ptr_ref) continue; 4638 }, 4639 else => {}, 4640 } 4641 block.instructions.items[block_index] = cur_inst; 4642 block_index += 1; 4643 } 4644 block.instructions.shrinkRetainingCapacity(block_index); 4645 4646 const union_val = try mod.intern(.{ .un = .{ 4647 .ty = union_ty.toIntern(), 4648 .tag = tag_val.toIntern(), 4649 .val = val.toIntern(), 4650 } }); 4651 const union_init = Air.internedToRef(union_val); 4652 try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store); 4653 return; 4654 } else if (try sema.typeRequiresComptime(union_ty)) { 4655 return sema.failWithNeededComptime(block, field_ptr_data.src(), .{ 4656 .needed_comptime_reason = "initializer of comptime only union must be comptime-known", 4657 }); 4658 } 4659 4660 const new_tag = Air.internedToRef(tag_val.toIntern()); 4661 const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, new_tag); 4662 try sema.checkComptimeKnownStore(block, set_tag_inst); 4663 } 4664 4665 fn validateStructInit( 4666 sema: *Sema, 4667 block: *Block, 4668 struct_ty: Type, 4669 init_src: LazySrcLoc, 4670 instrs: []const Zir.Inst.Index, 4671 ) CompileError!void { 4672 const mod = sema.mod; 4673 const gpa = sema.gpa; 4674 const ip = &mod.intern_pool; 4675 4676 const field_indices = try gpa.alloc(u32, instrs.len); 4677 defer gpa.free(field_indices); 4678 4679 // Maps field index to field_ptr index of where it was already initialized. 4680 const found_fields = try gpa.alloc(Zir.Inst.OptionalIndex, struct_ty.structFieldCount(mod)); 4681 defer gpa.free(found_fields); 4682 @memset(found_fields, .none); 4683 4684 var struct_ptr_zir_ref: Zir.Inst.Ref = undefined; 4685 4686 for (instrs, field_indices) |field_ptr, *field_index| { 4687 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node; 4688 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node }; 4689 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4690 struct_ptr_zir_ref = field_ptr_extra.lhs; 4691 const field_name = try ip.getOrPutString( 4692 gpa, 4693 sema.code.nullTerminatedString(field_ptr_extra.field_name_start), 4694 ); 4695 field_index.* = if (struct_ty.isTuple(mod)) 4696 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src) 4697 else 4698 try sema.structFieldIndex(block, struct_ty, field_name, field_src); 4699 assert(found_fields[field_index.*] == .none); 4700 found_fields[field_index.*] = field_ptr.toOptional(); 4701 } 4702 4703 var root_msg: ?*Module.ErrorMsg = null; 4704 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 4705 4706 const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref); 4707 if (block.is_comptime and 4708 (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null) 4709 { 4710 try sema.resolveStructLayout(struct_ty); 4711 // In this case the only thing we need to do is evaluate the implicit 4712 // store instructions for default field values, and report any missing fields. 4713 // Avoid the cost of the extra machinery for detecting a comptime struct init value. 4714 for (found_fields, 0..) |field_ptr, i_usize| { 4715 const i: u32 = @intCast(i_usize); 4716 if (field_ptr != .none) continue; 4717 4718 try sema.resolveStructFieldInits(struct_ty); 4719 const default_val = struct_ty.structFieldDefaultValue(i, mod); 4720 if (default_val.toIntern() == .unreachable_value) { 4721 const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse { 4722 const template = "missing tuple field with index {d}"; 4723 if (root_msg) |msg| { 4724 try sema.errNote(block, init_src, msg, template, .{i}); 4725 } else { 4726 root_msg = try sema.errMsg(block, init_src, template, .{i}); 4727 } 4728 continue; 4729 }; 4730 const template = "missing struct field: {}"; 4731 const args = .{field_name.fmt(ip)}; 4732 if (root_msg) |msg| { 4733 try sema.errNote(block, init_src, msg, template, args); 4734 } else { 4735 root_msg = try sema.errMsg(block, init_src, template, args); 4736 } 4737 continue; 4738 } 4739 4740 const field_src = init_src; // TODO better source location 4741 const default_field_ptr = if (struct_ty.isTuple(mod)) 4742 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) 4743 else 4744 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), field_src, struct_ty, true); 4745 const init = Air.internedToRef(default_val.toIntern()); 4746 try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); 4747 } 4748 4749 if (root_msg) |msg| { 4750 if (mod.typeToStruct(struct_ty)) |struct_type| { 4751 const decl = mod.declPtr(struct_type.decl.unwrap().?); 4752 const fqn = try decl.getFullyQualifiedName(mod); 4753 try mod.errNoteNonLazy( 4754 decl.srcLoc(mod), 4755 msg, 4756 "struct '{}' declared here", 4757 .{fqn.fmt(ip)}, 4758 ); 4759 } 4760 root_msg = null; 4761 return sema.failWithOwnedErrorMsg(block, msg); 4762 } 4763 4764 return; 4765 } 4766 4767 var struct_is_comptime = true; 4768 var first_block_index = block.instructions.items.len; 4769 4770 const require_comptime = try sema.typeRequiresComptime(struct_ty); 4771 const air_tags = sema.air_instructions.items(.tag); 4772 const air_datas = sema.air_instructions.items(.data); 4773 4774 try sema.resolveStructFieldInits(struct_ty); 4775 4776 // We collect the comptime field values in case the struct initialization 4777 // ends up being comptime-known. 4778 const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod)); 4779 4780 field: for (found_fields, 0..) |opt_field_ptr, i_usize| { 4781 const i: u32 = @intCast(i_usize); 4782 if (opt_field_ptr.unwrap()) |field_ptr| { 4783 // Determine whether the value stored to this pointer is comptime-known. 4784 const field_ty = struct_ty.structFieldType(i, mod); 4785 if (try sema.typeHasOnePossibleValue(field_ty)) |opv| { 4786 field_values[i] = opv.toIntern(); 4787 continue; 4788 } 4789 4790 const field_ptr_ref = sema.inst_map.get(field_ptr).?; 4791 4792 //std.debug.print("validateStructInit (field_ptr_ref=%{d}):\n", .{field_ptr_ref}); 4793 //for (block.instructions.items) |item| { 4794 // std.debug.print(" %{d} = {s}\n", .{item, @tagName(air_tags[@intFromEnum(item)])}); 4795 //} 4796 4797 // We expect to see something like this in the current block AIR: 4798 // %a = field_ptr(...) 4799 // store(%a, %b) 4800 // With an optional bitcast between the store and the field_ptr. 4801 // If %b is a comptime operand, this field is comptime. 4802 // 4803 // However, in the case of a comptime-known pointer to a struct, the 4804 // the field_ptr instruction is missing, so we have to pattern-match 4805 // based only on the store instructions. 4806 // `first_block_index` needs to point to the `field_ptr` if it exists; 4807 // the `store` otherwise. 4808 4809 // Possible performance enhancement: save the `block_index` between iterations 4810 // of the for loop. 4811 var block_index = block.instructions.items.len; 4812 while (block_index > 0) { 4813 block_index -= 1; 4814 const store_inst = block.instructions.items[block_index]; 4815 if (store_inst.toRef() == field_ptr_ref) { 4816 struct_is_comptime = false; 4817 continue :field; 4818 } 4819 switch (air_tags[@intFromEnum(store_inst)]) { 4820 .store, .store_safe => {}, 4821 else => continue, 4822 } 4823 const bin_op = air_datas[@intFromEnum(store_inst)].bin_op; 4824 var ptr_ref = bin_op.lhs; 4825 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 4826 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 4827 }; 4828 if (ptr_ref != field_ptr_ref) continue; 4829 first_block_index = @min(if (field_ptr_ref.toIndex()) |field_ptr_inst| 4830 std.mem.lastIndexOfScalar( 4831 Air.Inst.Index, 4832 block.instructions.items[0..block_index], 4833 field_ptr_inst, 4834 ).? 4835 else 4836 block_index, first_block_index); 4837 if (try sema.resolveValue(bin_op.rhs)) |val| { 4838 field_values[i] = val.toIntern(); 4839 } else if (require_comptime) { 4840 const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node; 4841 return sema.failWithNeededComptime(block, field_ptr_data.src(), .{ 4842 .needed_comptime_reason = "initializer of comptime only struct must be comptime-known", 4843 }); 4844 } else { 4845 struct_is_comptime = false; 4846 } 4847 continue :field; 4848 } 4849 struct_is_comptime = false; 4850 continue :field; 4851 } 4852 4853 const default_val = struct_ty.structFieldDefaultValue(i, mod); 4854 if (default_val.toIntern() == .unreachable_value) { 4855 const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse { 4856 const template = "missing tuple field with index {d}"; 4857 if (root_msg) |msg| { 4858 try sema.errNote(block, init_src, msg, template, .{i}); 4859 } else { 4860 root_msg = try sema.errMsg(block, init_src, template, .{i}); 4861 } 4862 continue; 4863 }; 4864 const template = "missing struct field: {}"; 4865 const args = .{field_name.fmt(ip)}; 4866 if (root_msg) |msg| { 4867 try sema.errNote(block, init_src, msg, template, args); 4868 } else { 4869 root_msg = try sema.errMsg(block, init_src, template, args); 4870 } 4871 continue; 4872 } 4873 field_values[i] = default_val.toIntern(); 4874 } 4875 4876 if (root_msg) |msg| { 4877 if (mod.typeToStruct(struct_ty)) |struct_type| { 4878 const decl = mod.declPtr(struct_type.decl.unwrap().?); 4879 const fqn = try decl.getFullyQualifiedName(mod); 4880 try mod.errNoteNonLazy( 4881 decl.srcLoc(mod), 4882 msg, 4883 "struct '{}' declared here", 4884 .{fqn.fmt(ip)}, 4885 ); 4886 } 4887 root_msg = null; 4888 return sema.failWithOwnedErrorMsg(block, msg); 4889 } 4890 4891 if (struct_is_comptime) { 4892 // Our task is to delete all the `field_ptr` and `store` instructions, and insert 4893 // instead a single `store` to the struct_ptr with a comptime struct value. 4894 var init_index: usize = 0; 4895 var field_ptr_ref = Air.Inst.Ref.none; 4896 var block_index = first_block_index; 4897 for (block.instructions.items[first_block_index..]) |cur_inst| { 4898 while (field_ptr_ref == .none and init_index < instrs.len) : (init_index += 1) { 4899 const field_ty = struct_ty.structFieldType(field_indices[init_index], mod); 4900 if (try field_ty.onePossibleValue(mod)) |_| continue; 4901 field_ptr_ref = sema.inst_map.get(instrs[init_index]).?; 4902 } 4903 switch (air_tags[@intFromEnum(cur_inst)]) { 4904 .struct_field_ptr, 4905 .struct_field_ptr_index_0, 4906 .struct_field_ptr_index_1, 4907 .struct_field_ptr_index_2, 4908 .struct_field_ptr_index_3, 4909 => if (cur_inst.toRef() == field_ptr_ref) continue, 4910 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == field_ptr_ref) continue, 4911 .store, .store_safe => { 4912 var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs; 4913 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 4914 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 4915 }; 4916 if (ptr_ref == field_ptr_ref) { 4917 field_ptr_ref = .none; 4918 continue; 4919 } 4920 }, 4921 else => {}, 4922 } 4923 block.instructions.items[block_index] = cur_inst; 4924 block_index += 1; 4925 } 4926 block.instructions.shrinkRetainingCapacity(block_index); 4927 4928 const struct_val = try mod.intern(.{ .aggregate = .{ 4929 .ty = struct_ty.toIntern(), 4930 .storage = .{ .elems = field_values }, 4931 } }); 4932 const struct_init = Air.internedToRef(struct_val); 4933 try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store); 4934 return; 4935 } 4936 try sema.resolveStructLayout(struct_ty); 4937 4938 // Our task is to insert `store` instructions for all the default field values. 4939 for (found_fields, 0..) |field_ptr, i| { 4940 if (field_ptr != .none) continue; 4941 4942 const field_src = init_src; // TODO better source location 4943 const default_field_ptr = if (struct_ty.isTuple(mod)) 4944 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) 4945 else 4946 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), field_src, struct_ty, true); 4947 try sema.checkKnownAllocPtr(struct_ptr, default_field_ptr); 4948 const init = Air.internedToRef(field_values[i]); 4949 try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); 4950 } 4951 } 4952 4953 fn zirValidatePtrArrayInit( 4954 sema: *Sema, 4955 block: *Block, 4956 inst: Zir.Inst.Index, 4957 ) CompileError!void { 4958 const mod = sema.mod; 4959 const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 4960 const init_src = validate_inst.src(); 4961 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); 4962 const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); 4963 const first_elem_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node; 4964 const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; 4965 const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr); 4966 const array_ty = sema.typeOf(array_ptr).childType(mod).optEuBaseType(mod); 4967 const array_len = array_ty.arrayLen(mod); 4968 4969 if (instrs.len != array_len) switch (array_ty.zigTypeTag(mod)) { 4970 .Struct => { 4971 var root_msg: ?*Module.ErrorMsg = null; 4972 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 4973 4974 var i = instrs.len; 4975 while (i < array_len) : (i += 1) { 4976 const default_val = array_ty.structFieldDefaultValue(i, mod); 4977 if (default_val.toIntern() == .unreachable_value) { 4978 const template = "missing tuple field with index {d}"; 4979 if (root_msg) |msg| { 4980 try sema.errNote(block, init_src, msg, template, .{i}); 4981 } else { 4982 root_msg = try sema.errMsg(block, init_src, template, .{i}); 4983 } 4984 } 4985 } 4986 4987 if (root_msg) |msg| { 4988 root_msg = null; 4989 return sema.failWithOwnedErrorMsg(block, msg); 4990 } 4991 }, 4992 .Array => { 4993 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{ 4994 array_len, instrs.len, 4995 }); 4996 }, 4997 .Vector => { 4998 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{ 4999 array_len, instrs.len, 5000 }); 5001 }, 5002 else => unreachable, 5003 }; 5004 5005 if (block.is_comptime and 5006 (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null) 5007 { 5008 // In this case the comptime machinery will have evaluated the store instructions 5009 // at comptime so we have almost nothing to do here. However, in case of a 5010 // sentinel-terminated array, the sentinel will not have been populated by 5011 // any ZIR instructions at comptime; we need to do that here. 5012 if (array_ty.sentinel(mod)) |sentinel_val| { 5013 const array_len_ref = try mod.intRef(Type.usize, array_len); 5014 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true); 5015 const sentinel = Air.internedToRef(sentinel_val.toIntern()); 5016 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store); 5017 } 5018 return; 5019 } 5020 5021 // If the array has one possible value, the value is always comptime-known. 5022 if (try sema.typeHasOnePossibleValue(array_ty)) |array_opv| { 5023 const array_init = Air.internedToRef(array_opv.toIntern()); 5024 try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store); 5025 return; 5026 } 5027 5028 var array_is_comptime = true; 5029 var first_block_index = block.instructions.items.len; 5030 5031 // Collect the comptime element values in case the array literal ends up 5032 // being comptime-known. 5033 const element_vals = try sema.arena.alloc( 5034 InternPool.Index, 5035 try sema.usizeCast(block, init_src, array_len), 5036 ); 5037 const air_tags = sema.air_instructions.items(.tag); 5038 const air_datas = sema.air_instructions.items(.data); 5039 5040 outer: for (instrs, 0..) |elem_ptr, i| { 5041 // Determine whether the value stored to this pointer is comptime-known. 5042 5043 if (array_ty.isTuple(mod)) { 5044 if (array_ty.structFieldIsComptime(i, mod)) 5045 try sema.resolveStructFieldInits(array_ty); 5046 if (try array_ty.structFieldValueComptime(mod, i)) |opv| { 5047 element_vals[i] = opv.toIntern(); 5048 continue; 5049 } 5050 } 5051 5052 const elem_ptr_ref = sema.inst_map.get(elem_ptr).?; 5053 5054 // We expect to see something like this in the current block AIR: 5055 // %a = elem_ptr(...) 5056 // store(%a, %b) 5057 // With an optional bitcast between the store and the elem_ptr. 5058 // If %b is a comptime operand, this element is comptime. 5059 // 5060 // However, in the case of a comptime-known pointer to an array, the 5061 // the elem_ptr instruction is missing, so we have to pattern-match 5062 // based only on the store instructions. 5063 // `first_block_index` needs to point to the `elem_ptr` if it exists; 5064 // the `store` otherwise. 5065 // 5066 // This is nearly identical to similar logic in `validateStructInit`. 5067 5068 // Possible performance enhancement: save the `block_index` between iterations 5069 // of the for loop. 5070 var block_index = block.instructions.items.len; 5071 while (block_index > 0) { 5072 block_index -= 1; 5073 const store_inst = block.instructions.items[block_index]; 5074 if (store_inst.toRef() == elem_ptr_ref) { 5075 array_is_comptime = false; 5076 continue :outer; 5077 } 5078 switch (air_tags[@intFromEnum(store_inst)]) { 5079 .store, .store_safe => {}, 5080 else => continue, 5081 } 5082 const bin_op = air_datas[@intFromEnum(store_inst)].bin_op; 5083 var ptr_ref = bin_op.lhs; 5084 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 5085 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 5086 }; 5087 if (ptr_ref != elem_ptr_ref) continue; 5088 first_block_index = @min(if (elem_ptr_ref.toIndex()) |elem_ptr_inst| 5089 std.mem.lastIndexOfScalar( 5090 Air.Inst.Index, 5091 block.instructions.items[0..block_index], 5092 elem_ptr_inst, 5093 ).? 5094 else 5095 block_index, first_block_index); 5096 if (try sema.resolveValue(bin_op.rhs)) |val| { 5097 element_vals[i] = val.toIntern(); 5098 } else { 5099 array_is_comptime = false; 5100 } 5101 continue :outer; 5102 } 5103 array_is_comptime = false; 5104 continue :outer; 5105 } 5106 5107 if (array_is_comptime) { 5108 if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| { 5109 switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) { 5110 .ptr => |ptr| switch (ptr.addr) { 5111 .comptime_field => return, // This store was validated by the individual elem ptrs. 5112 else => {}, 5113 }, 5114 else => {}, 5115 } 5116 } 5117 5118 // Our task is to delete all the `elem_ptr` and `store` instructions, and insert 5119 // instead a single `store` to the array_ptr with a comptime struct value. 5120 var elem_index: usize = 0; 5121 var elem_ptr_ref = Air.Inst.Ref.none; 5122 var block_index = first_block_index; 5123 for (block.instructions.items[first_block_index..]) |cur_inst| { 5124 while (elem_ptr_ref == .none and elem_index < instrs.len) : (elem_index += 1) { 5125 if (array_ty.isTuple(mod) and array_ty.structFieldIsComptime(elem_index, mod)) continue; 5126 elem_ptr_ref = sema.inst_map.get(instrs[elem_index]).?; 5127 } 5128 switch (air_tags[@intFromEnum(cur_inst)]) { 5129 .ptr_elem_ptr => if (cur_inst.toRef() == elem_ptr_ref) continue, 5130 .bitcast => if (air_datas[@intFromEnum(cur_inst)].ty_op.operand == elem_ptr_ref) continue, 5131 .store, .store_safe => { 5132 var ptr_ref = air_datas[@intFromEnum(cur_inst)].bin_op.lhs; 5133 if (ptr_ref.toIndex()) |ptr_inst| if (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 5134 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 5135 }; 5136 if (ptr_ref == elem_ptr_ref) { 5137 elem_ptr_ref = .none; 5138 continue; 5139 } 5140 }, 5141 else => {}, 5142 } 5143 block.instructions.items[block_index] = cur_inst; 5144 block_index += 1; 5145 } 5146 block.instructions.shrinkRetainingCapacity(block_index); 5147 5148 const array_val = try mod.intern(.{ .aggregate = .{ 5149 .ty = array_ty.toIntern(), 5150 .storage = .{ .elems = element_vals }, 5151 } }); 5152 const array_init = Air.internedToRef(array_val); 5153 try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store); 5154 } 5155 } 5156 5157 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5158 const mod = sema.mod; 5159 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5160 const src = inst_data.src(); 5161 const operand = try sema.resolveInst(inst_data.operand); 5162 const operand_ty = sema.typeOf(operand); 5163 5164 if (operand_ty.zigTypeTag(mod) != .Pointer) { 5165 return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(mod)}); 5166 } else switch (operand_ty.ptrSize(mod)) { 5167 .One, .C => {}, 5168 .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(mod)}), 5169 .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(mod)}), 5170 } 5171 5172 if ((try sema.typeHasOnePossibleValue(operand_ty.childType(mod))) != null) { 5173 // No need to validate the actual pointer value, we don't need it! 5174 return; 5175 } 5176 5177 const elem_ty = operand_ty.elemType2(mod); 5178 if (try sema.resolveValue(operand)) |val| { 5179 if (val.isUndef(mod)) { 5180 return sema.fail(block, src, "cannot dereference undefined value", .{}); 5181 } 5182 } else if (try sema.typeRequiresComptime(elem_ty)) { 5183 const msg = msg: { 5184 const msg = try sema.errMsg( 5185 block, 5186 src, 5187 "values of type '{}' must be comptime-known, but operand value is runtime-known", 5188 .{elem_ty.fmt(mod)}, 5189 ); 5190 errdefer msg.destroy(sema.gpa); 5191 5192 const src_decl = mod.declPtr(block.src_decl); 5193 try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), elem_ty); 5194 break :msg msg; 5195 }; 5196 return sema.failWithOwnedErrorMsg(block, msg); 5197 } 5198 } 5199 5200 fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5201 const mod = sema.mod; 5202 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5203 const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data; 5204 const src = inst_data.src(); 5205 const destructure_src = LazySrcLoc.nodeOffset(extra.destructure_node); 5206 const operand = try sema.resolveInst(extra.operand); 5207 const operand_ty = sema.typeOf(operand); 5208 5209 const can_destructure = switch (operand_ty.zigTypeTag(mod)) { 5210 .Array, .Vector => true, 5211 .Struct => operand_ty.isTuple(mod), 5212 else => false, 5213 }; 5214 5215 if (!can_destructure) { 5216 return sema.failWithOwnedErrorMsg(block, msg: { 5217 const msg = try sema.errMsg(block, src, "type '{}' cannot be destructured", .{operand_ty.fmt(mod)}); 5218 errdefer msg.destroy(sema.gpa); 5219 try sema.errNote(block, destructure_src, msg, "result destructured here", .{}); 5220 break :msg msg; 5221 }); 5222 } 5223 5224 if (operand_ty.arrayLen(mod) != extra.expect_len) { 5225 return sema.failWithOwnedErrorMsg(block, msg: { 5226 const msg = try sema.errMsg(block, src, "expected {} elements for destructure, found {}", .{ 5227 extra.expect_len, 5228 operand_ty.arrayLen(mod), 5229 }); 5230 errdefer msg.destroy(sema.gpa); 5231 try sema.errNote(block, destructure_src, msg, "result destructured here", .{}); 5232 break :msg msg; 5233 }); 5234 } 5235 } 5236 5237 fn failWithBadMemberAccess( 5238 sema: *Sema, 5239 block: *Block, 5240 agg_ty: Type, 5241 field_src: LazySrcLoc, 5242 field_name: InternPool.NullTerminatedString, 5243 ) CompileError { 5244 const mod = sema.mod; 5245 const kw_name = switch (agg_ty.zigTypeTag(mod)) { 5246 .Union => "union", 5247 .Struct => "struct", 5248 .Opaque => "opaque", 5249 .Enum => "enum", 5250 else => unreachable, 5251 }; 5252 const msg = msg: { 5253 const msg = blk: { 5254 if (agg_ty.getOwnerDeclOrNull(mod)) |some| if (mod.declIsRoot(some)) { 5255 break :blk try sema.errMsg(block, field_src, "root struct of file '{}' has no member named '{}'", .{ 5256 agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool), 5257 }); 5258 }; 5259 5260 break :blk try sema.errMsg(block, field_src, "{s} '{}' has no member named '{}'", .{ 5261 kw_name, agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool), 5262 }); 5263 }; 5264 5265 errdefer msg.destroy(sema.gpa); 5266 try sema.addDeclaredHereNote(msg, agg_ty); 5267 break :msg msg; 5268 }; 5269 return sema.failWithOwnedErrorMsg(block, msg); 5270 } 5271 5272 fn failWithBadStructFieldAccess( 5273 sema: *Sema, 5274 block: *Block, 5275 struct_type: InternPool.Key.StructType, 5276 field_src: LazySrcLoc, 5277 field_name: InternPool.NullTerminatedString, 5278 ) CompileError { 5279 const mod = sema.mod; 5280 const gpa = sema.gpa; 5281 const decl = mod.declPtr(struct_type.decl.unwrap().?); 5282 const fqn = try decl.getFullyQualifiedName(mod); 5283 5284 const msg = msg: { 5285 const msg = try sema.errMsg( 5286 block, 5287 field_src, 5288 "no field named '{}' in struct '{}'", 5289 .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) }, 5290 ); 5291 errdefer msg.destroy(gpa); 5292 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "struct declared here", .{}); 5293 break :msg msg; 5294 }; 5295 return sema.failWithOwnedErrorMsg(block, msg); 5296 } 5297 5298 fn failWithBadUnionFieldAccess( 5299 sema: *Sema, 5300 block: *Block, 5301 union_obj: InternPool.UnionType, 5302 field_src: LazySrcLoc, 5303 field_name: InternPool.NullTerminatedString, 5304 ) CompileError { 5305 const mod = sema.mod; 5306 const gpa = sema.gpa; 5307 5308 const decl = mod.declPtr(union_obj.decl); 5309 const fqn = try decl.getFullyQualifiedName(mod); 5310 5311 const msg = msg: { 5312 const msg = try sema.errMsg( 5313 block, 5314 field_src, 5315 "no field named '{}' in union '{}'", 5316 .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) }, 5317 ); 5318 errdefer msg.destroy(gpa); 5319 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "union declared here", .{}); 5320 break :msg msg; 5321 }; 5322 return sema.failWithOwnedErrorMsg(block, msg); 5323 } 5324 5325 fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void { 5326 const mod = sema.mod; 5327 const src_loc = decl_ty.declSrcLocOrNull(mod) orelse return; 5328 const category = switch (decl_ty.zigTypeTag(mod)) { 5329 .Union => "union", 5330 .Struct => "struct", 5331 .Enum => "enum", 5332 .Opaque => "opaque", 5333 .ErrorSet => "error set", 5334 else => unreachable, 5335 }; 5336 try mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category}); 5337 } 5338 5339 fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5340 const tracy = trace(@src()); 5341 defer tracy.end(); 5342 5343 const src: LazySrcLoc = sema.src; 5344 const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 5345 const ptr = try sema.resolveInst(bin_inst.lhs); 5346 const operand = try sema.resolveInst(bin_inst.rhs); 5347 const ptr_inst = ptr.toIndex().?; 5348 const air_datas = sema.air_instructions.items(.data); 5349 5350 switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) { 5351 .inferred_alloc_comptime => { 5352 const iac = &air_datas[@intFromEnum(ptr_inst)].inferred_alloc_comptime; 5353 return sema.storeToInferredAllocComptime(block, src, operand, iac); 5354 }, 5355 .inferred_alloc => { 5356 const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; 5357 return sema.storeToInferredAlloc(block, ptr, operand, ia); 5358 }, 5359 else => unreachable, 5360 } 5361 } 5362 5363 fn storeToInferredAlloc( 5364 sema: *Sema, 5365 block: *Block, 5366 ptr: Air.Inst.Ref, 5367 operand: Air.Inst.Ref, 5368 inferred_alloc: *InferredAlloc, 5369 ) CompileError!void { 5370 // Create a store instruction as a placeholder. This will be replaced by a 5371 // proper store sequence once we know the stored type. 5372 const dummy_store = try block.addBinOp(.store, ptr, operand); 5373 try sema.checkComptimeKnownStore(block, dummy_store); 5374 // Add the stored instruction to the set we will use to resolve peer types 5375 // for the inferred allocation. 5376 try inferred_alloc.prongs.append(sema.arena, dummy_store.toIndex().?); 5377 } 5378 5379 fn storeToInferredAllocComptime( 5380 sema: *Sema, 5381 block: *Block, 5382 src: LazySrcLoc, 5383 operand: Air.Inst.Ref, 5384 iac: *Air.Inst.Data.InferredAllocComptime, 5385 ) CompileError!void { 5386 const operand_ty = sema.typeOf(operand); 5387 // There will be only one store_to_inferred_ptr because we are running at comptime. 5388 // The alloc will turn into a Decl. 5389 if (try sema.resolveValue(operand)) |operand_val| { 5390 var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl 5391 defer anon_decl.deinit(); 5392 iac.decl_index = try anon_decl.finish(operand_ty, operand_val, iac.alignment); 5393 try sema.comptime_mutable_decls.append(iac.decl_index); 5394 return; 5395 } 5396 5397 return sema.failWithNeededComptime(block, src, .{ 5398 .needed_comptime_reason = "value being stored to a comptime variable must be comptime-known", 5399 }); 5400 } 5401 5402 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5403 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5404 const src = inst_data.src(); 5405 const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, Type.u32, .{ 5406 .needed_comptime_reason = "eval branch quota must be comptime-known", 5407 })); 5408 sema.branch_quota = @max(sema.branch_quota, quota); 5409 } 5410 5411 fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5412 const tracy = trace(@src()); 5413 defer tracy.end(); 5414 5415 const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 5416 const ptr = try sema.resolveInst(bin_inst.lhs); 5417 const value = try sema.resolveInst(bin_inst.rhs); 5418 return sema.storePtr(block, sema.src, ptr, value); 5419 } 5420 5421 fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5422 const tracy = trace(@src()); 5423 defer tracy.end(); 5424 5425 const mod = sema.mod; 5426 const zir_tags = sema.code.instructions.items(.tag); 5427 const zir_datas = sema.code.instructions.items(.data); 5428 const inst_data = zir_datas[@intFromEnum(inst)].pl_node; 5429 const src = inst_data.src(); 5430 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 5431 const ptr = try sema.resolveInst(extra.lhs); 5432 const operand = try sema.resolveInst(extra.rhs); 5433 5434 const is_ret = if (extra.lhs.toIndex()) |ptr_index| 5435 zir_tags[@intFromEnum(ptr_index)] == .ret_ptr 5436 else 5437 false; 5438 5439 // Check for the possibility of this pattern: 5440 // %a = ret_ptr 5441 // %b = store(%a, %c) 5442 // Where %c is an error union or error set. In such case we need to add 5443 // to the current function's inferred error set, if any. 5444 if (is_ret and sema.fn_ret_ty_ies != null) switch (sema.typeOf(operand).zigTypeTag(mod)) { 5445 .ErrorUnion, .ErrorSet => try sema.addToInferredErrorSet(operand), 5446 else => {}, 5447 }; 5448 5449 const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node }; 5450 const operand_src: LazySrcLoc = .{ .node_offset_store_operand = inst_data.src_node }; 5451 const air_tag: Air.Inst.Tag = if (is_ret) 5452 .ret_ptr 5453 else if (block.wantSafety()) 5454 .store_safe 5455 else 5456 .store; 5457 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 5458 } 5459 5460 fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5461 const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code); 5462 return sema.addStrLitNoAlias(bytes); 5463 } 5464 5465 fn addStrLit(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref { 5466 const duped_bytes = try sema.arena.dupe(u8, bytes); 5467 return addStrLitNoAlias(sema, duped_bytes); 5468 } 5469 5470 /// Safe to call when `bytes` does not point into `InternPool`. 5471 fn addStrLitNoAlias(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref { 5472 const mod = sema.mod; 5473 const array_ty = try mod.arrayType(.{ 5474 .len = bytes.len, 5475 .sentinel = .zero_u8, 5476 .child = .u8_type, 5477 }); 5478 const val = try mod.intern(.{ .aggregate = .{ 5479 .ty = array_ty.toIntern(), 5480 .storage = .{ .bytes = bytes }, 5481 } }); 5482 return anonDeclRef(sema, val); 5483 } 5484 5485 fn anonDeclRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref { 5486 return Air.internedToRef(try refValue(sema, val)); 5487 } 5488 5489 fn refValue(sema: *Sema, val: InternPool.Index) CompileError!InternPool.Index { 5490 const mod = sema.mod; 5491 const ptr_ty = (try sema.ptrType(.{ 5492 .child = mod.intern_pool.typeOf(val), 5493 .flags = .{ 5494 .alignment = .none, 5495 .is_const = true, 5496 .address_space = .generic, 5497 }, 5498 })).toIntern(); 5499 return mod.intern(.{ .ptr = .{ 5500 .ty = ptr_ty, 5501 .addr = .{ .anon_decl = .{ 5502 .val = val, 5503 .orig_ty = ptr_ty, 5504 } }, 5505 } }); 5506 } 5507 5508 fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5509 _ = block; 5510 const tracy = trace(@src()); 5511 defer tracy.end(); 5512 5513 const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].int; 5514 return sema.mod.intRef(Type.comptime_int, int); 5515 } 5516 5517 fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5518 _ = block; 5519 const tracy = trace(@src()); 5520 defer tracy.end(); 5521 5522 const mod = sema.mod; 5523 const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].str; 5524 const byte_count = int.len * @sizeOf(std.math.big.Limb); 5525 const limb_bytes = sema.code.string_bytes[int.start..][0..byte_count]; 5526 5527 // TODO: this allocation and copy is only needed because the limbs may be unaligned. 5528 // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these 5529 // two lines can be removed. 5530 const limbs = try sema.arena.alloc(std.math.big.Limb, int.len); 5531 @memcpy(mem.sliceAsBytes(limbs), limb_bytes); 5532 5533 return Air.internedToRef((try mod.intValue_big(Type.comptime_int, .{ 5534 .limbs = limbs, 5535 .positive = true, 5536 })).toIntern()); 5537 } 5538 5539 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5540 _ = block; 5541 const number = sema.code.instructions.items(.data)[@intFromEnum(inst)].float; 5542 return Air.internedToRef((try sema.mod.floatValue( 5543 Type.comptime_float, 5544 number, 5545 )).toIntern()); 5546 } 5547 5548 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5549 _ = block; 5550 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5551 const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data; 5552 const number = extra.get(); 5553 return Air.internedToRef((try sema.mod.floatValue(Type.comptime_float, number)).toIntern()); 5554 } 5555 5556 fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 5557 const tracy = trace(@src()); 5558 defer tracy.end(); 5559 5560 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5561 const src = inst_data.src(); 5562 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 5563 const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ 5564 .needed_comptime_reason = "compile error string must be comptime-known", 5565 }); 5566 return sema.fail(block, src, "{s}", .{msg}); 5567 } 5568 5569 fn zirCompileLog( 5570 sema: *Sema, 5571 extended: Zir.Inst.Extended.InstData, 5572 ) CompileError!Air.Inst.Ref { 5573 const mod = sema.mod; 5574 5575 var managed = mod.compile_log_text.toManaged(sema.gpa); 5576 defer sema.mod.compile_log_text = managed.moveToUnmanaged(); 5577 const writer = managed.writer(); 5578 5579 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 5580 const src_node = extra.data.src_node; 5581 const args = sema.code.refSlice(extra.end, extended.small); 5582 5583 for (args, 0..) |arg_ref, i| { 5584 if (i != 0) try writer.print(", ", .{}); 5585 5586 const arg = try sema.resolveInst(arg_ref); 5587 const arg_ty = sema.typeOf(arg); 5588 if (try sema.resolveValueResolveLazy(arg)) |val| { 5589 try writer.print("@as({}, {})", .{ 5590 arg_ty.fmt(mod), val.fmtValue(arg_ty, mod), 5591 }); 5592 } else { 5593 try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(mod)}); 5594 } 5595 } 5596 try writer.print("\n", .{}); 5597 5598 const decl_index = if (sema.func_index != .none) 5599 mod.funcOwnerDeclIndex(sema.func_index) 5600 else 5601 sema.owner_decl_index; 5602 const gop = try mod.compile_log_decls.getOrPut(sema.gpa, decl_index); 5603 if (!gop.found_existing) { 5604 gop.value_ptr.* = src_node; 5605 } 5606 return .void_value; 5607 } 5608 5609 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 5610 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 5611 const src = inst_data.src(); 5612 const msg_inst = try sema.resolveInst(inst_data.operand); 5613 5614 if (block.is_comptime) { 5615 return sema.fail(block, src, "encountered @panic at comptime", .{}); 5616 } 5617 try sema.panicWithMsg(block, src, msg_inst, .@"@panic"); 5618 return always_noreturn; 5619 } 5620 5621 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 5622 const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node; 5623 const src = LazySrcLoc.nodeOffset(src_node); 5624 sema.src = src; 5625 if (block.is_comptime) 5626 return sema.fail(block, src, "encountered @trap at comptime", .{}); 5627 _ = try block.addNoOp(.trap); 5628 return always_noreturn; 5629 } 5630 5631 fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5632 const tracy = trace(@src()); 5633 defer tracy.end(); 5634 5635 const mod = sema.mod; 5636 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5637 const src = inst_data.src(); 5638 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 5639 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5640 const gpa = sema.gpa; 5641 5642 // AIR expects a block outside the loop block too. 5643 // Reserve space for a Loop instruction so that generated Break instructions can 5644 // point to it, even if it doesn't end up getting used because the code ends up being 5645 // comptime evaluated. 5646 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 5647 const loop_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1); 5648 try sema.air_instructions.ensureUnusedCapacity(gpa, 2); 5649 sema.air_instructions.appendAssumeCapacity(.{ 5650 .tag = .block, 5651 .data = undefined, 5652 }); 5653 sema.air_instructions.appendAssumeCapacity(.{ 5654 .tag = .loop, 5655 .data = .{ .ty_pl = .{ 5656 .ty = .noreturn_type, 5657 .payload = undefined, 5658 } }, 5659 }); 5660 var label: Block.Label = .{ 5661 .zir_block = inst, 5662 .merges = .{ 5663 .src_locs = .{}, 5664 .results = .{}, 5665 .br_list = .{}, 5666 .block_inst = block_inst, 5667 }, 5668 }; 5669 var child_block = parent_block.makeSubBlock(); 5670 child_block.label = &label; 5671 child_block.runtime_cond = null; 5672 child_block.runtime_loop = src; 5673 child_block.runtime_index.increment(); 5674 const merges = &child_block.label.?.merges; 5675 5676 defer child_block.instructions.deinit(gpa); 5677 defer merges.deinit(gpa); 5678 5679 var loop_block = child_block.makeSubBlock(); 5680 defer loop_block.instructions.deinit(gpa); 5681 5682 try sema.analyzeBody(&loop_block, body); 5683 5684 const loop_block_len = loop_block.instructions.items.len; 5685 if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(mod)) { 5686 // If the loop ended with a noreturn terminator, then there is no way for it to loop, 5687 // so we can just use the block instead. 5688 try child_block.instructions.appendSlice(gpa, loop_block.instructions.items); 5689 } else { 5690 try child_block.instructions.append(gpa, loop_inst); 5691 5692 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + loop_block_len); 5693 sema.air_instructions.items(.data)[@intFromEnum(loop_inst)].ty_pl.payload = sema.addExtraAssumeCapacity( 5694 Air.Block{ .body_len = @intCast(loop_block_len) }, 5695 ); 5696 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items)); 5697 } 5698 return sema.analyzeBlockBody(parent_block, src, &child_block, merges); 5699 } 5700 5701 fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5702 const tracy = trace(@src()); 5703 defer tracy.end(); 5704 5705 const mod = sema.mod; 5706 const comp = mod.comp; 5707 const gpa = sema.gpa; 5708 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5709 const src = pl_node.src(); 5710 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 5711 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5712 5713 // we check this here to avoid undefined symbols 5714 if (!@import("build_options").have_llvm) 5715 return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{}); 5716 5717 var c_import_buf = std.ArrayList(u8).init(gpa); 5718 defer c_import_buf.deinit(); 5719 5720 var comptime_reason: Block.ComptimeReason = .{ .c_import = .{ 5721 .block = parent_block, 5722 .src = src, 5723 } }; 5724 var child_block: Block = .{ 5725 .parent = parent_block, 5726 .sema = sema, 5727 .src_decl = parent_block.src_decl, 5728 .namespace = parent_block.namespace, 5729 .wip_capture_scope = parent_block.wip_capture_scope, 5730 .instructions = .{}, 5731 .inlining = parent_block.inlining, 5732 .is_comptime = true, 5733 .comptime_reason = &comptime_reason, 5734 .c_import_buf = &c_import_buf, 5735 .runtime_cond = parent_block.runtime_cond, 5736 .runtime_loop = parent_block.runtime_loop, 5737 .runtime_index = parent_block.runtime_index, 5738 }; 5739 defer child_block.instructions.deinit(gpa); 5740 5741 // Ignore the result, all the relevant operations have written to c_import_buf already. 5742 _ = try sema.analyzeBodyBreak(&child_block, body); 5743 5744 var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err| 5745 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5746 defer c_import_res.deinit(gpa); 5747 5748 if (c_import_res.errors.errorMessageCount() != 0) { 5749 const msg = msg: { 5750 const msg = try sema.errMsg(&child_block, src, "C import failed", .{}); 5751 errdefer msg.destroy(gpa); 5752 5753 if (!comp.config.link_libc) 5754 try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{}); 5755 5756 const gop = try mod.cimport_errors.getOrPut(gpa, sema.owner_decl_index); 5757 if (!gop.found_existing) { 5758 gop.value_ptr.* = c_import_res.errors; 5759 c_import_res.errors = std.zig.ErrorBundle.empty; 5760 } 5761 break :msg msg; 5762 }; 5763 return sema.failWithOwnedErrorMsg(&child_block, msg); 5764 } 5765 const parent_mod = parent_block.ownerModule(); 5766 const c_import_mod = Package.Module.create(comp.arena, .{ 5767 .global_cache_directory = comp.global_cache_directory, 5768 .paths = .{ 5769 .root = .{ 5770 .root_dir = Compilation.Directory.cwd(), 5771 .sub_path = std.fs.path.dirname(c_import_res.out_zig_path) orelse "", 5772 }, 5773 .root_src_path = std.fs.path.basename(c_import_res.out_zig_path), 5774 }, 5775 .fully_qualified_name = c_import_res.out_zig_path, 5776 .cc_argv = parent_mod.cc_argv, 5777 .inherited = .{}, 5778 .global = comp.config, 5779 .parent = parent_mod, 5780 .builtin_mod = parent_mod.getBuiltinDependency(), 5781 }) catch |err| switch (err) { 5782 // None of these are possible because we are creating a package with 5783 // the exact same configuration as the parent package, which already 5784 // passed these checks. 5785 error.ValgrindUnsupportedOnTarget => unreachable, 5786 error.TargetRequiresSingleThreaded => unreachable, 5787 error.BackendRequiresSingleThreaded => unreachable, 5788 error.TargetRequiresPic => unreachable, 5789 error.PieRequiresPic => unreachable, 5790 error.DynamicLinkingRequiresPic => unreachable, 5791 error.TargetHasNoRedZone => unreachable, 5792 error.StackCheckUnsupportedByTarget => unreachable, 5793 error.StackProtectorUnsupportedByTarget => unreachable, 5794 error.StackProtectorUnavailableWithoutLibC => unreachable, 5795 5796 else => |e| return e, 5797 }; 5798 5799 const result = mod.importPkg(c_import_mod) catch |err| 5800 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5801 5802 mod.astGenFile(result.file) catch |err| 5803 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5804 5805 try mod.semaFile(result.file); 5806 const file_root_decl_index = result.file.root_decl.unwrap().?; 5807 return sema.analyzeDeclVal(parent_block, src, file_root_decl_index); 5808 } 5809 5810 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5811 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5812 const src = inst_data.src(); 5813 return sema.failWithUseOfAsync(parent_block, src); 5814 } 5815 5816 fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_comptime: bool) CompileError!Air.Inst.Ref { 5817 const tracy = trace(@src()); 5818 defer tracy.end(); 5819 5820 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 5821 const src = pl_node.src(); 5822 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 5823 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 5824 const gpa = sema.gpa; 5825 5826 // Reserve space for a Block instruction so that generated Break instructions can 5827 // point to it, even if it doesn't end up getting used because the code ends up being 5828 // comptime evaluated or is an unlabeled block. 5829 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 5830 try sema.air_instructions.append(gpa, .{ 5831 .tag = .block, 5832 .data = undefined, 5833 }); 5834 5835 var label: Block.Label = .{ 5836 .zir_block = inst, 5837 .merges = .{ 5838 .src_locs = .{}, 5839 .results = .{}, 5840 .br_list = .{}, 5841 .block_inst = block_inst, 5842 }, 5843 }; 5844 5845 var child_block: Block = .{ 5846 .parent = parent_block, 5847 .sema = sema, 5848 .src_decl = parent_block.src_decl, 5849 .namespace = parent_block.namespace, 5850 .wip_capture_scope = parent_block.wip_capture_scope, 5851 .instructions = .{}, 5852 .label = &label, 5853 .inlining = parent_block.inlining, 5854 .is_comptime = parent_block.is_comptime or force_comptime, 5855 .comptime_reason = parent_block.comptime_reason, 5856 .is_typeof = parent_block.is_typeof, 5857 .want_safety = parent_block.want_safety, 5858 .float_mode = parent_block.float_mode, 5859 .c_import_buf = parent_block.c_import_buf, 5860 .runtime_cond = parent_block.runtime_cond, 5861 .runtime_loop = parent_block.runtime_loop, 5862 .runtime_index = parent_block.runtime_index, 5863 .error_return_trace_index = parent_block.error_return_trace_index, 5864 }; 5865 5866 defer child_block.instructions.deinit(gpa); 5867 defer label.merges.deinit(gpa); 5868 5869 return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges); 5870 } 5871 5872 fn resolveBlockBody( 5873 sema: *Sema, 5874 parent_block: *Block, 5875 src: LazySrcLoc, 5876 child_block: *Block, 5877 body: []const Zir.Inst.Index, 5878 /// This is the instruction that a break instruction within `body` can 5879 /// use to return from the body. 5880 body_inst: Zir.Inst.Index, 5881 merges: *Block.Merges, 5882 ) CompileError!Air.Inst.Ref { 5883 if (child_block.is_comptime) { 5884 return sema.resolveBody(child_block, body, body_inst); 5885 } else { 5886 if (sema.analyzeBodyInner(child_block, body)) |_| { 5887 return sema.analyzeBlockBody(parent_block, src, child_block, merges); 5888 } else |err| switch (err) { 5889 error.ComptimeBreak => { 5890 // Comptime control flow is happening, however child_block may still contain 5891 // runtime instructions which need to be copied to the parent block. 5892 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items); 5893 5894 const break_inst = sema.comptime_break_inst; 5895 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; 5896 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 5897 if (extra.block_inst == body_inst) { 5898 return try sema.resolveInst(break_data.operand); 5899 } else { 5900 return error.ComptimeBreak; 5901 } 5902 }, 5903 else => |e| return e, 5904 } 5905 } 5906 } 5907 5908 fn analyzeBlockBody( 5909 sema: *Sema, 5910 parent_block: *Block, 5911 src: LazySrcLoc, 5912 child_block: *Block, 5913 merges: *Block.Merges, 5914 ) CompileError!Air.Inst.Ref { 5915 const tracy = trace(@src()); 5916 defer tracy.end(); 5917 5918 const gpa = sema.gpa; 5919 const mod = sema.mod; 5920 5921 // Blocks must terminate with noreturn instruction. 5922 assert(child_block.instructions.items.len != 0); 5923 assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(mod)); 5924 5925 if (merges.results.items.len == 0) { 5926 // No need for a block instruction. We can put the new instructions 5927 // directly into the parent block. 5928 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items); 5929 return child_block.instructions.items[child_block.instructions.items.len - 1].toRef(); 5930 } 5931 if (merges.results.items.len == 1) { 5932 const last_inst_index = child_block.instructions.items.len - 1; 5933 const last_inst = child_block.instructions.items[last_inst_index]; 5934 if (sema.getBreakBlock(last_inst)) |br_block| { 5935 if (br_block == merges.block_inst) { 5936 // No need for a block instruction. We can put the new instructions directly 5937 // into the parent block. Here we omit the break instruction. 5938 const without_break = child_block.instructions.items[0..last_inst_index]; 5939 try parent_block.instructions.appendSlice(gpa, without_break); 5940 return merges.results.items[0]; 5941 } 5942 } 5943 } 5944 // It is impossible to have the number of results be > 1 in a comptime scope. 5945 assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition. 5946 5947 // Need to set the type and emit the Block instruction. This allows machine code generation 5948 // to emit a jump instruction to after the block when it encounters the break. 5949 try parent_block.instructions.append(gpa, merges.block_inst); 5950 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items }); 5951 // TODO add note "missing else causes void value" 5952 5953 const type_src = src; // TODO: better source location 5954 if (try sema.typeRequiresComptime(resolved_ty)) { 5955 const msg = msg: { 5956 const msg = try sema.errMsg(child_block, type_src, "value with comptime-only type '{}' depends on runtime control flow", .{resolved_ty.fmt(mod)}); 5957 errdefer msg.destroy(sema.gpa); 5958 5959 const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?; 5960 try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{}); 5961 5962 const child_src_decl = mod.declPtr(child_block.src_decl); 5963 try sema.explainWhyTypeIsComptime(msg, type_src.toSrcLoc(child_src_decl, mod), resolved_ty); 5964 5965 break :msg msg; 5966 }; 5967 return sema.failWithOwnedErrorMsg(child_block, msg); 5968 } 5969 const ty_inst = Air.internedToRef(resolved_ty.toIntern()); 5970 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + 5971 child_block.instructions.items.len); 5972 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ 5973 .ty = ty_inst, 5974 .payload = sema.addExtraAssumeCapacity(Air.Block{ 5975 .body_len = @intCast(child_block.instructions.items.len), 5976 }), 5977 } }; 5978 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 5979 // Now that the block has its type resolved, we need to go back into all the break 5980 // instructions, and insert type coercion on the operands. 5981 for (merges.br_list.items) |br| { 5982 const br_operand = sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand; 5983 const br_operand_src = src; 5984 const br_operand_ty = sema.typeOf(br_operand); 5985 if (br_operand_ty.eql(resolved_ty, mod)) { 5986 // No type coercion needed. 5987 continue; 5988 } 5989 var coerce_block = parent_block.makeSubBlock(); 5990 defer coerce_block.instructions.deinit(gpa); 5991 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src); 5992 // If no instructions were produced, such as in the case of a coercion of a 5993 // constant value to a new type, we can simply point the br operand to it. 5994 if (coerce_block.instructions.items.len == 0) { 5995 sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand = coerced_operand; 5996 continue; 5997 } 5998 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1].toRef() == coerced_operand); 5999 6000 // Convert the br instruction to a block instruction that has the coercion 6001 // and then a new br inside that returns the coerced instruction. 6002 const sub_block_len: u32 = @intCast(coerce_block.instructions.items.len + 1); 6003 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + 6004 sub_block_len); 6005 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 6006 const sub_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 6007 6008 sema.air_instructions.items(.tag)[@intFromEnum(br)] = .block; 6009 sema.air_instructions.items(.data)[@intFromEnum(br)] = .{ .ty_pl = .{ 6010 .ty = .noreturn_type, 6011 .payload = sema.addExtraAssumeCapacity(Air.Block{ 6012 .body_len = sub_block_len, 6013 }), 6014 } }; 6015 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); 6016 sema.air_extra.appendAssumeCapacity(@intFromEnum(sub_br_inst)); 6017 6018 sema.air_instructions.appendAssumeCapacity(.{ 6019 .tag = .br, 6020 .data = .{ .br = .{ 6021 .block_inst = merges.block_inst, 6022 .operand = coerced_operand, 6023 } }, 6024 }); 6025 } 6026 return merges.block_inst.toRef(); 6027 } 6028 6029 fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6030 const tracy = trace(@src()); 6031 defer tracy.end(); 6032 6033 const mod = sema.mod; 6034 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 6035 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; 6036 const src = inst_data.src(); 6037 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 6038 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 6039 const decl_name = try mod.intern_pool.getOrPutString(mod.gpa, sema.code.nullTerminatedString(extra.decl_name)); 6040 const decl_index = if (extra.namespace != .none) index_blk: { 6041 const container_ty = try sema.resolveType(block, operand_src, extra.namespace); 6042 const container_namespace = container_ty.getNamespaceIndex(mod).unwrap().?; 6043 6044 const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false); 6045 break :index_blk maybe_index orelse 6046 return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name); 6047 } else try sema.lookupIdentifier(block, operand_src, decl_name); 6048 const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) { 6049 error.NeededSourceLocation => { 6050 _ = try sema.resolveExportOptions(block, options_src, extra.options); 6051 unreachable; 6052 }, 6053 else => |e| return e, 6054 }; 6055 { 6056 try mod.ensureDeclAnalyzed(decl_index); 6057 const exported_decl = mod.declPtr(decl_index); 6058 if (exported_decl.val.getFunction(mod)) |function| { 6059 return sema.analyzeExport(block, src, options, function.owner_decl); 6060 } 6061 } 6062 try sema.analyzeExport(block, src, options, decl_index); 6063 } 6064 6065 fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6066 const tracy = trace(@src()); 6067 defer tracy.end(); 6068 6069 const mod = sema.mod; 6070 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 6071 const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data; 6072 const src = inst_data.src(); 6073 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 6074 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 6075 const operand = try sema.resolveInstConst(block, operand_src, extra.operand, .{ 6076 .needed_comptime_reason = "export target must be comptime-known", 6077 }); 6078 const options = try sema.resolveExportOptions(block, options_src, extra.options); 6079 if (options.linkage == .Internal) 6080 return; 6081 if (operand.val.getFunction(mod)) |function| { 6082 const decl_index = function.owner_decl; 6083 return sema.analyzeExport(block, src, options, decl_index); 6084 } 6085 6086 try addExport(mod, .{ 6087 .opts = options, 6088 .src = src, 6089 .owner_decl = sema.owner_decl_index, 6090 .src_decl = block.src_decl, 6091 .exported = .{ .value = operand.val.toIntern() }, 6092 .status = .in_progress, 6093 }); 6094 } 6095 6096 pub fn analyzeExport( 6097 sema: *Sema, 6098 block: *Block, 6099 src: LazySrcLoc, 6100 options: Module.Export.Options, 6101 exported_decl_index: InternPool.DeclIndex, 6102 ) !void { 6103 const gpa = sema.gpa; 6104 const mod = sema.mod; 6105 6106 if (options.linkage == .Internal) 6107 return; 6108 6109 try mod.ensureDeclAnalyzed(exported_decl_index); 6110 const exported_decl = mod.declPtr(exported_decl_index); 6111 6112 if (!try sema.validateExternType(exported_decl.ty, .other)) { 6113 const msg = msg: { 6114 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(mod)}); 6115 errdefer msg.destroy(gpa); 6116 6117 const src_decl = mod.declPtr(block.src_decl); 6118 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other); 6119 6120 try sema.addDeclaredHereNote(msg, exported_decl.ty); 6121 break :msg msg; 6122 }; 6123 return sema.failWithOwnedErrorMsg(block, msg); 6124 } 6125 6126 // TODO: some backends might support re-exporting extern decls 6127 if (exported_decl.isExtern(mod)) { 6128 return sema.fail(block, src, "export target cannot be extern", .{}); 6129 } 6130 6131 // This decl is alive no matter what, since it's being exported 6132 try mod.markDeclAlive(exported_decl); 6133 try sema.maybeQueueFuncBodyAnalysis(exported_decl_index); 6134 6135 try addExport(mod, .{ 6136 .opts = options, 6137 .src = src, 6138 .owner_decl = sema.owner_decl_index, 6139 .src_decl = block.src_decl, 6140 .exported = .{ .decl_index = exported_decl_index }, 6141 .status = .in_progress, 6142 }); 6143 } 6144 6145 fn addExport(mod: *Module, export_init: Module.Export) error{OutOfMemory}!void { 6146 const gpa = mod.gpa; 6147 6148 try mod.decl_exports.ensureUnusedCapacity(gpa, 1); 6149 try mod.value_exports.ensureUnusedCapacity(gpa, 1); 6150 try mod.export_owners.ensureUnusedCapacity(gpa, 1); 6151 6152 const new_export = try gpa.create(Module.Export); 6153 errdefer gpa.destroy(new_export); 6154 6155 new_export.* = export_init; 6156 6157 const eo_gop = mod.export_owners.getOrPutAssumeCapacity(export_init.owner_decl); 6158 if (!eo_gop.found_existing) eo_gop.value_ptr.* = .{}; 6159 try eo_gop.value_ptr.append(gpa, new_export); 6160 errdefer _ = eo_gop.value_ptr.pop(); 6161 6162 switch (export_init.exported) { 6163 .decl_index => |decl_index| { 6164 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(decl_index); 6165 if (!de_gop.found_existing) de_gop.value_ptr.* = .{}; 6166 try de_gop.value_ptr.append(gpa, new_export); 6167 }, 6168 .value => |value| { 6169 const ve_gop = mod.value_exports.getOrPutAssumeCapacity(value); 6170 if (!ve_gop.found_existing) ve_gop.value_ptr.* = .{}; 6171 try ve_gop.value_ptr.append(gpa, new_export); 6172 }, 6173 } 6174 } 6175 6176 fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 6177 const mod = sema.mod; 6178 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 6179 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 6180 const src = LazySrcLoc.nodeOffset(extra.node); 6181 const alignment = try sema.resolveAlign(block, operand_src, extra.operand); 6182 if (alignment.order(Alignment.fromNonzeroByteUnits(256)).compare(.gt)) { 6183 return sema.fail(block, src, "attempt to @setAlignStack({d}); maximum is 256", .{ 6184 alignment.toByteUnitsOptional().?, 6185 }); 6186 } 6187 6188 const fn_owner_decl = mod.funcOwnerDeclPtr(sema.func_index); 6189 switch (fn_owner_decl.ty.fnCallingConvention(mod)) { 6190 .Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}), 6191 .Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}), 6192 else => if (block.inlining != null) { 6193 return sema.fail(block, src, "@setAlignStack in inline call", .{}); 6194 }, 6195 } 6196 6197 if (sema.prev_stack_alignment_src) |prev_src| { 6198 const msg = msg: { 6199 const msg = try sema.errMsg(block, src, "multiple @setAlignStack in the same function body", .{}); 6200 errdefer msg.destroy(sema.gpa); 6201 try sema.errNote(block, prev_src, msg, "other instance here", .{}); 6202 break :msg msg; 6203 }; 6204 return sema.failWithOwnedErrorMsg(block, msg); 6205 } 6206 sema.prev_stack_alignment_src = src; 6207 6208 const ip = &mod.intern_pool; 6209 const a = ip.funcAnalysis(sema.func_index); 6210 if (a.stack_alignment != .none) { 6211 a.stack_alignment = @enumFromInt(@max( 6212 @intFromEnum(alignment), 6213 @intFromEnum(a.stack_alignment), 6214 )); 6215 } 6216 } 6217 6218 fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 6219 const mod = sema.mod; 6220 const ip = &mod.intern_pool; 6221 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 6222 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 6223 const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, .{ 6224 .needed_comptime_reason = "operand to @setCold must be comptime-known", 6225 }); 6226 if (sema.func_index == .none) return; // does nothing outside a function 6227 ip.funcAnalysis(sema.func_index).is_cold = is_cold; 6228 } 6229 6230 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 6231 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 6232 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 6233 block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode", .{ 6234 .needed_comptime_reason = "operand to @setFloatMode must be comptime-known", 6235 }); 6236 } 6237 6238 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6239 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 6240 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 6241 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{ 6242 .needed_comptime_reason = "operand to @setRuntimeSafety must be comptime-known", 6243 }); 6244 } 6245 6246 fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 6247 if (block.is_comptime) return; 6248 6249 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 6250 const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 6251 const order = try sema.resolveAtomicOrder(block, order_src, extra.operand, .{ 6252 .needed_comptime_reason = "atomic order of @fence must be comptime-known", 6253 }); 6254 6255 if (@intFromEnum(order) < @intFromEnum(std.builtin.AtomicOrder.Acquire)) { 6256 return sema.fail(block, order_src, "atomic ordering must be Acquire or stricter", .{}); 6257 } 6258 6259 _ = try block.addInst(.{ 6260 .tag = .fence, 6261 .data = .{ .fence = order }, 6262 }); 6263 } 6264 6265 fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 6266 const tracy = trace(@src()); 6267 defer tracy.end(); 6268 6269 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break"; 6270 const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; 6271 const operand = try sema.resolveInst(inst_data.operand); 6272 const zir_block = extra.block_inst; 6273 6274 var block = start_block; 6275 while (true) { 6276 if (block.label) |label| { 6277 if (label.zir_block == zir_block) { 6278 const br_ref = try start_block.addBr(label.merges.block_inst, operand); 6279 const src_loc = if (extra.operand_src_node != Zir.Inst.Break.no_src_node) 6280 LazySrcLoc.nodeOffset(extra.operand_src_node) 6281 else 6282 null; 6283 try label.merges.src_locs.append(sema.gpa, src_loc); 6284 try label.merges.results.append(sema.gpa, operand); 6285 try label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); 6286 block.runtime_index.increment(); 6287 if (block.runtime_cond == null and block.runtime_loop == null) { 6288 block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; 6289 block.runtime_loop = start_block.runtime_loop; 6290 } 6291 return inst; 6292 } 6293 } 6294 block = block.parent.?; 6295 } 6296 } 6297 6298 fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6299 // We do not set sema.src here because dbg_stmt instructions are only emitted for 6300 // ZIR code that possibly will need to generate runtime code. So error messages 6301 // and other source locations must not rely on sema.src being set from dbg_stmt 6302 // instructions. 6303 if (block.is_comptime or block.ownerModule().strip) return; 6304 6305 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; 6306 6307 if (block.instructions.items.len != 0) { 6308 const idx = block.instructions.items[block.instructions.items.len - 1]; 6309 if (sema.air_instructions.items(.tag)[@intFromEnum(idx)] == .dbg_stmt) { 6310 // The previous dbg_stmt didn't correspond to any actual code, so replace it. 6311 sema.air_instructions.items(.data)[@intFromEnum(idx)].dbg_stmt = .{ 6312 .line = inst_data.line, 6313 .column = inst_data.column, 6314 }; 6315 return; 6316 } 6317 } 6318 6319 _ = try block.addInst(.{ 6320 .tag = .dbg_stmt, 6321 .data = .{ .dbg_stmt = .{ 6322 .line = inst_data.line, 6323 .column = inst_data.column, 6324 } }, 6325 }); 6326 } 6327 6328 fn zirDbgBlockBegin(block: *Block) CompileError!void { 6329 if (block.is_comptime or block.ownerModule().strip) return; 6330 6331 _ = try block.addInst(.{ 6332 .tag = .dbg_block_begin, 6333 .data = undefined, 6334 }); 6335 } 6336 6337 fn zirDbgBlockEnd(block: *Block) CompileError!void { 6338 if (block.is_comptime or block.ownerModule().strip) return; 6339 6340 _ = try block.addInst(.{ 6341 .tag = .dbg_block_end, 6342 .data = undefined, 6343 }); 6344 } 6345 6346 fn zirDbgVar( 6347 sema: *Sema, 6348 block: *Block, 6349 inst: Zir.Inst.Index, 6350 air_tag: Air.Inst.Tag, 6351 ) CompileError!void { 6352 if (block.is_comptime or block.ownerModule().strip) return; 6353 6354 const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op; 6355 const operand = try sema.resolveInst(str_op.operand); 6356 const name = str_op.getStr(sema.code); 6357 try sema.addDbgVar(block, operand, air_tag, name); 6358 } 6359 6360 fn addDbgVar( 6361 sema: *Sema, 6362 block: *Block, 6363 operand: Air.Inst.Ref, 6364 air_tag: Air.Inst.Tag, 6365 name: []const u8, 6366 ) CompileError!void { 6367 const mod = sema.mod; 6368 const operand_ty = sema.typeOf(operand); 6369 const val_ty = switch (air_tag) { 6370 .dbg_var_ptr => operand_ty.childType(mod), 6371 .dbg_var_val => operand_ty, 6372 else => unreachable, 6373 }; 6374 if (try sema.typeRequiresComptime(val_ty)) return; 6375 if (!(try sema.typeHasRuntimeBits(val_ty))) return; 6376 6377 try sema.queueFullTypeResolution(operand_ty); 6378 6379 // Add the name to the AIR. 6380 const name_extra_index: u32 = @intCast(sema.air_extra.items.len); 6381 const elements_used = name.len / 4 + 1; 6382 try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements_used); 6383 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 6384 @memcpy(buffer[0..name.len], name); 6385 buffer[name.len] = 0; 6386 sema.air_extra.items.len += elements_used; 6387 6388 _ = try block.addInst(.{ 6389 .tag = air_tag, 6390 .data = .{ .pl_op = .{ 6391 .payload = name_extra_index, 6392 .operand = operand, 6393 } }, 6394 }); 6395 } 6396 6397 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 6398 const mod = sema.mod; 6399 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 6400 const src = inst_data.src(); 6401 const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 6402 const decl_index = try sema.lookupIdentifier(block, src, decl_name); 6403 try sema.addReferencedBy(block, src, decl_index); 6404 return sema.analyzeDeclRef(decl_index); 6405 } 6406 6407 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 6408 const mod = sema.mod; 6409 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 6410 const src = inst_data.src(); 6411 const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 6412 const decl = try sema.lookupIdentifier(block, src, decl_name); 6413 return sema.analyzeDeclVal(block, src, decl); 6414 } 6415 6416 fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPool.NullTerminatedString) !InternPool.DeclIndex { 6417 const mod = sema.mod; 6418 var namespace = block.namespace; 6419 while (true) { 6420 if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| { 6421 return decl_index; 6422 } 6423 namespace = mod.namespacePtr(namespace).parent.unwrap() orelse break; 6424 } 6425 unreachable; // AstGen detects use of undeclared identifiers. 6426 } 6427 6428 /// This looks up a member of a specific namespace. It is affected by `usingnamespace` but 6429 /// only for ones in the specified namespace. 6430 fn lookupInNamespace( 6431 sema: *Sema, 6432 block: *Block, 6433 src: LazySrcLoc, 6434 namespace_index: InternPool.NamespaceIndex, 6435 ident_name: InternPool.NullTerminatedString, 6436 observe_usingnamespace: bool, 6437 ) CompileError!?InternPool.DeclIndex { 6438 const mod = sema.mod; 6439 6440 const namespace = mod.namespacePtr(namespace_index); 6441 const namespace_decl_index = namespace.getDeclIndex(mod); 6442 const namespace_decl = mod.declPtr(namespace_decl_index); 6443 if (namespace_decl.analysis == .file_failure) { 6444 return error.AnalysisFail; 6445 } 6446 6447 if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) { 6448 const src_file = mod.namespacePtr(block.namespace).file_scope; 6449 6450 const gpa = sema.gpa; 6451 var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, bool) = .{}; 6452 defer checked_namespaces.deinit(gpa); 6453 6454 // Keep track of name conflicts for error notes. 6455 var candidates: std.ArrayListUnmanaged(InternPool.DeclIndex) = .{}; 6456 defer candidates.deinit(gpa); 6457 6458 try checked_namespaces.put(gpa, namespace, namespace.file_scope == src_file); 6459 var check_i: usize = 0; 6460 6461 while (check_i < checked_namespaces.count()) : (check_i += 1) { 6462 const check_ns = checked_namespaces.keys()[check_i]; 6463 if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| { 6464 // Skip decls which are not marked pub, which are in a different 6465 // file than the `a.b`/`@hasDecl` syntax. 6466 const decl = mod.declPtr(decl_index); 6467 if (decl.is_pub or (src_file == decl.getFileScope(mod) and checked_namespaces.values()[check_i])) { 6468 try candidates.append(gpa, decl_index); 6469 } 6470 } 6471 var it = check_ns.usingnamespace_set.iterator(); 6472 while (it.next()) |entry| { 6473 const sub_usingnamespace_decl_index = entry.key_ptr.*; 6474 // Skip the decl we're currently analysing. 6475 if (sub_usingnamespace_decl_index == sema.owner_decl_index) continue; 6476 const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index); 6477 const sub_is_pub = entry.value_ptr.*; 6478 if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope(mod)) { 6479 // Skip usingnamespace decls which are not marked pub, which are in 6480 // a different file than the `a.b`/`@hasDecl` syntax. 6481 continue; 6482 } 6483 try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index); 6484 const ns_ty = sub_usingnamespace_decl.val.toType(); 6485 const sub_ns = ns_ty.getNamespace(mod).?; 6486 try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope(mod)); 6487 } 6488 } 6489 6490 { 6491 var i: usize = 0; 6492 while (i < candidates.items.len) { 6493 if (candidates.items[i] == sema.owner_decl_index) { 6494 _ = candidates.orderedRemove(i); 6495 } else { 6496 i += 1; 6497 } 6498 } 6499 } 6500 6501 switch (candidates.items.len) { 6502 0 => {}, 6503 1 => { 6504 const decl_index = candidates.items[0]; 6505 return decl_index; 6506 }, 6507 else => { 6508 const msg = msg: { 6509 const msg = try sema.errMsg(block, src, "ambiguous reference", .{}); 6510 errdefer msg.destroy(gpa); 6511 for (candidates.items) |candidate_index| { 6512 const candidate = mod.declPtr(candidate_index); 6513 const src_loc = candidate.srcLoc(mod); 6514 try mod.errNoteNonLazy(src_loc, msg, "declared here", .{}); 6515 } 6516 break :msg msg; 6517 }; 6518 return sema.failWithOwnedErrorMsg(block, msg); 6519 }, 6520 } 6521 } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| { 6522 return decl_index; 6523 } 6524 6525 return null; 6526 } 6527 6528 fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?*Decl { 6529 const mod = sema.mod; 6530 const func_val = (try sema.resolveValue(func_inst)) orelse return null; 6531 if (func_val.isUndef(mod)) return null; 6532 const owner_decl_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { 6533 .extern_func => |extern_func| extern_func.decl, 6534 .func => |func| func.owner_decl, 6535 .ptr => |ptr| switch (ptr.addr) { 6536 .decl => |decl| mod.declPtr(decl).val.getFunction(mod).?.owner_decl, 6537 else => return null, 6538 }, 6539 else => return null, 6540 }; 6541 return mod.declPtr(owner_decl_index); 6542 } 6543 6544 pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref { 6545 const mod = sema.mod; 6546 const gpa = sema.gpa; 6547 const src = sema.src; 6548 6549 if (!block.ownerModule().error_tracing) return .none; 6550 6551 if (block.is_comptime) 6552 return .none; 6553 6554 const stack_trace_ty = sema.getBuiltinType("StackTrace") catch |err| switch (err) { 6555 error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, 6556 else => |e| return e, 6557 }; 6558 sema.resolveTypeFields(stack_trace_ty) catch |err| switch (err) { 6559 error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, 6560 else => |e| return e, 6561 }; 6562 const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); 6563 const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, src) catch |err| switch (err) { 6564 error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, 6565 else => |e| return e, 6566 }; 6567 6568 return try block.addInst(.{ 6569 .tag = .save_err_return_trace_index, 6570 .data = .{ .ty_pl = .{ 6571 .ty = Air.internedToRef(stack_trace_ty.toIntern()), 6572 .payload = @intCast(field_index), 6573 } }, 6574 }); 6575 } 6576 6577 /// Add instructions to block to "pop" the error return trace. 6578 /// If `operand` is provided, only pops if operand is non-error. 6579 fn popErrorReturnTrace( 6580 sema: *Sema, 6581 block: *Block, 6582 src: LazySrcLoc, 6583 operand: Air.Inst.Ref, 6584 saved_error_trace_index: Air.Inst.Ref, 6585 ) CompileError!void { 6586 const mod = sema.mod; 6587 const gpa = sema.gpa; 6588 var is_non_error: ?bool = null; 6589 var is_non_error_inst: Air.Inst.Ref = undefined; 6590 if (operand != .none) { 6591 is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand); 6592 if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val| 6593 is_non_error = cond_val.toBool(); 6594 } else is_non_error = true; // no operand means pop unconditionally 6595 6596 if (is_non_error == true) { 6597 // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or 6598 // the result is comptime-known to be a non-error. Either way, pop unconditionally. 6599 6600 const stack_trace_ty = try sema.getBuiltinType("StackTrace"); 6601 try sema.resolveTypeFields(stack_trace_ty); 6602 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 6603 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); 6604 const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); 6605 const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true); 6606 try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store); 6607 } else if (is_non_error == null) { 6608 // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need 6609 // to pop any error trace that may have been propagated from our arguments. 6610 6611 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len); 6612 const cond_block_inst = try block.addInstAsIndex(.{ 6613 .tag = .block, 6614 .data = .{ 6615 .ty_pl = .{ 6616 .ty = .void_type, 6617 .payload = undefined, // updated below 6618 }, 6619 }, 6620 }); 6621 6622 var then_block = block.makeSubBlock(); 6623 defer then_block.instructions.deinit(gpa); 6624 6625 // If non-error, then pop the error return trace by restoring the index. 6626 const stack_trace_ty = try sema.getBuiltinType("StackTrace"); 6627 try sema.resolveTypeFields(stack_trace_ty); 6628 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 6629 const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); 6630 const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); 6631 const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true); 6632 try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store); 6633 _ = try then_block.addBr(cond_block_inst, .void_value); 6634 6635 // Otherwise, do nothing 6636 var else_block = block.makeSubBlock(); 6637 defer else_block.instructions.deinit(gpa); 6638 _ = try else_block.addBr(cond_block_inst, .void_value); 6639 6640 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 6641 then_block.instructions.items.len + else_block.instructions.items.len + 6642 @typeInfo(Air.Block).Struct.fields.len + 1); // +1 for the sole .cond_br instruction in the .block 6643 6644 const cond_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 6645 try sema.air_instructions.append(gpa, .{ .tag = .cond_br, .data = .{ .pl_op = .{ 6646 .operand = is_non_error_inst, 6647 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 6648 .then_body_len = @intCast(then_block.instructions.items.len), 6649 .else_body_len = @intCast(else_block.instructions.items.len), 6650 }), 6651 } } }); 6652 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 6653 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 6654 6655 sema.air_instructions.items(.data)[@intFromEnum(cond_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); 6656 sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst)); 6657 } 6658 } 6659 6660 fn zirCall( 6661 sema: *Sema, 6662 block: *Block, 6663 inst: Zir.Inst.Index, 6664 comptime kind: enum { direct, field }, 6665 ) CompileError!Air.Inst.Ref { 6666 const tracy = trace(@src()); 6667 defer tracy.end(); 6668 6669 const mod = sema.mod; 6670 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 6671 const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; 6672 const call_src = inst_data.src(); 6673 const ExtraType = switch (kind) { 6674 .direct => Zir.Inst.Call, 6675 .field => Zir.Inst.FieldCall, 6676 }; 6677 const extra = sema.code.extraData(ExtraType, inst_data.payload_index); 6678 const args_len = extra.data.flags.args_len; 6679 6680 const modifier: std.builtin.CallModifier = @enumFromInt(extra.data.flags.packed_modifier); 6681 const ensure_result_used = extra.data.flags.ensure_result_used; 6682 const pop_error_return_trace = extra.data.flags.pop_error_return_trace; 6683 6684 const callee: ResolvedFieldCallee = switch (kind) { 6685 .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, 6686 .field => blk: { 6687 const object_ptr = try sema.resolveInst(extra.data.obj_ptr); 6688 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.data.field_name_start)); 6689 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; 6690 break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); 6691 }, 6692 }; 6693 const func: Air.Inst.Ref = switch (callee) { 6694 .direct => |func_inst| func_inst, 6695 .method => |method| method.func_inst, 6696 }; 6697 6698 const callee_ty = sema.typeOf(func); 6699 const total_args = args_len + @intFromBool(callee == .method); 6700 const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method); 6701 6702 // The block index before the call, so we can potentially insert an error trace save here later. 6703 const block_index: Air.Inst.Index = @enumFromInt(block.instructions.items.len); 6704 6705 // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the 6706 // error trace potentially dirty). 6707 var input_is_error = false; 6708 6709 const args_info: CallArgsInfo = .{ .zir_call = .{ 6710 .bound_arg = switch (callee) { 6711 .direct => .none, 6712 .method => |method| method.arg0_inst, 6713 }, 6714 .bound_arg_src = callee_src, 6715 .call_inst = inst, 6716 .call_node_offset = inst_data.src_node, 6717 .num_args = args_len, 6718 .args_body = @ptrCast(sema.code.extra[extra.end..]), 6719 .any_arg_is_error = &input_is_error, 6720 } }; 6721 6722 // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction. 6723 const call_dbg_node: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); 6724 const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call); 6725 6726 if (sema.owner_func_index == .none or 6727 !mod.intern_pool.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn) 6728 { 6729 // No errorable fn actually called; we have no error return trace 6730 input_is_error = false; 6731 } 6732 6733 if (block.ownerModule().error_tracing and 6734 !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace)) 6735 { 6736 const return_ty = sema.typeOf(call_inst); 6737 if (modifier != .always_tail and return_ty.isNoReturn(mod)) 6738 return call_inst; // call to "fn (...) noreturn", don't pop 6739 6740 // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it 6741 // *before* the recursive call. This will be a bit tricky to do and probably requires 6742 // moving this logic into analyzeCall. But that's probably a good idea anyway. 6743 if (modifier == .always_tail) 6744 return call_inst; 6745 6746 // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only 6747 // need to clean-up our own trace if we were passed to a non-error-handling expression. 6748 if (input_is_error or (pop_error_return_trace and return_ty.isError(mod))) { 6749 const stack_trace_ty = try sema.getBuiltinType("StackTrace"); 6750 try sema.resolveTypeFields(stack_trace_ty); 6751 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index"); 6752 const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); 6753 6754 // Insert a save instruction before the arg resolution + call instructions we just generated 6755 const save_inst = try block.insertInst(block_index, .{ 6756 .tag = .save_err_return_trace_index, 6757 .data = .{ .ty_pl = .{ 6758 .ty = Air.internedToRef(stack_trace_ty.toIntern()), 6759 .payload = @intCast(field_index), 6760 } }, 6761 }); 6762 6763 // Pop the error return trace, testing the result for non-error if necessary 6764 const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst; 6765 try sema.popErrorReturnTrace(block, call_src, operand, save_inst); 6766 } 6767 6768 return call_inst; 6769 } else { 6770 return call_inst; 6771 } 6772 } 6773 6774 fn checkCallArgumentCount( 6775 sema: *Sema, 6776 block: *Block, 6777 func: Air.Inst.Ref, 6778 func_src: LazySrcLoc, 6779 callee_ty: Type, 6780 total_args: usize, 6781 member_fn: bool, 6782 ) !Type { 6783 const mod = sema.mod; 6784 const func_ty = func_ty: { 6785 switch (callee_ty.zigTypeTag(mod)) { 6786 .Fn => break :func_ty callee_ty, 6787 .Pointer => { 6788 const ptr_info = callee_ty.ptrInfo(mod); 6789 if (ptr_info.flags.size == .One and Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Fn) { 6790 break :func_ty Type.fromInterned(ptr_info.child); 6791 } 6792 }, 6793 .Optional => { 6794 const opt_child = callee_ty.optionalChild(mod); 6795 if (opt_child.zigTypeTag(mod) == .Fn or (opt_child.isSinglePointer(mod) and 6796 opt_child.childType(mod).zigTypeTag(mod) == .Fn)) 6797 { 6798 const msg = msg: { 6799 const msg = try sema.errMsg(block, func_src, "cannot call optional type '{}'", .{ 6800 callee_ty.fmt(mod), 6801 }); 6802 errdefer msg.destroy(sema.gpa); 6803 try sema.errNote(block, func_src, msg, "consider using '.?', 'orelse' or 'if'", .{}); 6804 break :msg msg; 6805 }; 6806 return sema.failWithOwnedErrorMsg(block, msg); 6807 } 6808 }, 6809 else => {}, 6810 } 6811 return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(mod)}); 6812 }; 6813 6814 const func_ty_info = mod.typeToFunc(func_ty).?; 6815 const fn_params_len = func_ty_info.param_types.len; 6816 const args_len = total_args - @intFromBool(member_fn); 6817 if (func_ty_info.is_var_args) { 6818 assert(callConvSupportsVarArgs(func_ty_info.cc)); 6819 if (total_args >= fn_params_len) return func_ty; 6820 } else if (fn_params_len == total_args) { 6821 return func_ty; 6822 } 6823 6824 const maybe_decl = try sema.funcDeclSrc(func); 6825 const member_str = if (member_fn) "member function " else ""; 6826 const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; 6827 const msg = msg: { 6828 const msg = try sema.errMsg( 6829 block, 6830 func_src, 6831 "{s}expected {s}{d} argument(s), found {d}", 6832 .{ 6833 member_str, 6834 variadic_str, 6835 fn_params_len - @intFromBool(member_fn), 6836 args_len, 6837 }, 6838 ); 6839 errdefer msg.destroy(sema.gpa); 6840 6841 if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{}); 6842 break :msg msg; 6843 }; 6844 return sema.failWithOwnedErrorMsg(block, msg); 6845 } 6846 6847 fn callBuiltin( 6848 sema: *Sema, 6849 block: *Block, 6850 call_src: LazySrcLoc, 6851 builtin_fn: Air.Inst.Ref, 6852 modifier: std.builtin.CallModifier, 6853 args: []const Air.Inst.Ref, 6854 operation: CallOperation, 6855 ) !void { 6856 const mod = sema.mod; 6857 const callee_ty = sema.typeOf(builtin_fn); 6858 const func_ty = func_ty: { 6859 switch (callee_ty.zigTypeTag(mod)) { 6860 .Fn => break :func_ty callee_ty, 6861 .Pointer => { 6862 const ptr_info = callee_ty.ptrInfo(mod); 6863 if (ptr_info.flags.size == .One and Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Fn) { 6864 break :func_ty Type.fromInterned(ptr_info.child); 6865 } 6866 }, 6867 else => {}, 6868 } 6869 std.debug.panic("type '{}' is not a function calling builtin fn", .{callee_ty.fmt(mod)}); 6870 }; 6871 6872 const func_ty_info = mod.typeToFunc(func_ty).?; 6873 const fn_params_len = func_ty_info.param_types.len; 6874 if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) { 6875 std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len }); 6876 } 6877 6878 _ = try sema.analyzeCall( 6879 block, 6880 builtin_fn, 6881 func_ty, 6882 call_src, 6883 call_src, 6884 modifier, 6885 false, 6886 .{ .resolved = .{ .src = call_src, .args = args } }, 6887 null, 6888 operation, 6889 ); 6890 } 6891 6892 const CallOperation = enum { 6893 call, 6894 @"@call", 6895 @"@panic", 6896 @"safety check", 6897 @"error return", 6898 }; 6899 6900 const CallArgsInfo = union(enum) { 6901 /// The full list of resolved (but uncoerced) arguments is known ahead of time. 6902 resolved: struct { 6903 src: LazySrcLoc, 6904 args: []const Air.Inst.Ref, 6905 }, 6906 6907 /// The list of resolved (but uncoerced) arguments is known ahead of time, but 6908 /// originated from a usage of the @call builtin at the given node offset. 6909 call_builtin: struct { 6910 call_node_offset: i32, 6911 args: []const Air.Inst.Ref, 6912 }, 6913 6914 /// This call corresponds to a ZIR call instruction. The arguments have not yet been 6915 /// resolved. They must be resolved by `analyzeCall` so that argument resolution and 6916 /// generic instantiation may be interleaved. This is required for RLS to work on 6917 /// generic parameters. 6918 zir_call: struct { 6919 /// This may be `none`, in which case it is ignored. Otherwise, it is the 6920 /// already-resolved value of the first argument, from method call syntax. 6921 bound_arg: Air.Inst.Ref, 6922 /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`. 6923 bound_arg_src: LazySrcLoc, 6924 /// The ZIR call instruction. The parameter type is placed at this index while 6925 /// analyzing arguments. 6926 call_inst: Zir.Inst.Index, 6927 /// The node offset of `call_inst`. 6928 call_node_offset: i32, 6929 /// The number of arguments to this call, not including `bound_arg`. 6930 num_args: u32, 6931 /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it 6932 /// is not `none`). Format is precisely the same as trailing data of ZIR `call`. 6933 args_body: []const Zir.Inst.Index, 6934 /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type. 6935 /// This is used by the caller to restore the error return trace when necessary. 6936 any_arg_is_error: *bool, 6937 }, 6938 6939 fn count(cai: CallArgsInfo) usize { 6940 return switch (cai) { 6941 inline .resolved, .call_builtin => |resolved| resolved.args.len, 6942 .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none), 6943 }; 6944 } 6945 6946 fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc { 6947 return switch (cai) { 6948 .resolved => |resolved| resolved.src, 6949 .call_builtin => |call_builtin| .{ .call_arg = .{ 6950 .decl = block.src_decl, 6951 .call_node_offset = call_builtin.call_node_offset, 6952 .arg_index = @intCast(arg_index), 6953 } }, 6954 .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) { 6955 return zir_call.bound_arg_src; 6956 } else .{ .call_arg = .{ 6957 .decl = block.src_decl, 6958 .call_node_offset = zir_call.call_node_offset, 6959 .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)), 6960 } }, 6961 }; 6962 } 6963 6964 /// Analyzes the arg at `arg_index` and coerces it to `param_ty`. 6965 /// `param_ty` may be `generic_poison` or `var_args_param`. 6966 /// `func_ty_info` may be the type before instantiation, even if a generic 6967 /// instantiation has been partially completed. 6968 fn analyzeArg( 6969 cai: CallArgsInfo, 6970 sema: *Sema, 6971 block: *Block, 6972 arg_index: usize, 6973 param_ty: Type, 6974 func_ty_info: InternPool.Key.FuncType, 6975 func_inst: Air.Inst.Ref, 6976 ) CompileError!Air.Inst.Ref { 6977 const mod = sema.mod; 6978 const param_count = func_ty_info.param_types.len; 6979 switch (param_ty.toIntern()) { 6980 .generic_poison_type, .var_args_param_type => {}, 6981 else => try sema.queueFullTypeResolution(param_ty), 6982 } 6983 const uncoerced_arg: Air.Inst.Ref = switch (cai) { 6984 inline .resolved, .call_builtin => |resolved| resolved.args[arg_index], 6985 .zir_call => |zir_call| arg_val: { 6986 const has_bound_arg = zir_call.bound_arg != .none; 6987 if (arg_index == 0 and has_bound_arg) { 6988 break :arg_val zir_call.bound_arg; 6989 } 6990 const real_arg_idx = arg_index - @intFromBool(has_bound_arg); 6991 6992 const arg_body = if (real_arg_idx == 0) blk: { 6993 const start = zir_call.num_args; 6994 const end = @intFromEnum(zir_call.args_body[0]); 6995 break :blk zir_call.args_body[start..end]; 6996 } else blk: { 6997 const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]); 6998 const end = @intFromEnum(zir_call.args_body[real_arg_idx]); 6999 break :blk zir_call.args_body[start..end]; 7000 }; 7001 7002 // Generate args to comptime params in comptime block 7003 const parent_comptime = block.is_comptime; 7004 defer block.is_comptime = parent_comptime; 7005 // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx` 7006 if (arg_index < @min(param_count, 32) and func_ty_info.paramIsComptime(@intCast(arg_index))) { 7007 block.is_comptime = true; 7008 // TODO set comptime_reason 7009 } 7010 // Give the arg its result type 7011 sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(param_ty.toIntern())); 7012 // Resolve the arg! 7013 const uncoerced_arg = try sema.resolveBody(block, arg_body, zir_call.call_inst); 7014 7015 if (sema.typeOf(uncoerced_arg).zigTypeTag(mod) == .NoReturn) { 7016 // This terminates resolution of arguments. The caller should 7017 // propagate this. 7018 return uncoerced_arg; 7019 } 7020 7021 if (sema.typeOf(uncoerced_arg).isError(mod)) { 7022 zir_call.any_arg_is_error.* = true; 7023 } 7024 7025 break :arg_val uncoerced_arg; 7026 }, 7027 }; 7028 switch (param_ty.toIntern()) { 7029 .generic_poison_type => return uncoerced_arg, 7030 .var_args_param_type => return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index)), 7031 else => return sema.coerceExtra( 7032 block, 7033 param_ty, 7034 uncoerced_arg, 7035 cai.argSrc(block, arg_index), 7036 .{ .param_src = .{ 7037 .func_inst = func_inst, 7038 .param_i = @intCast(arg_index), 7039 } }, 7040 ) catch |err| switch (err) { 7041 error.NotCoercible => unreachable, 7042 else => |e| return e, 7043 }, 7044 } 7045 } 7046 }; 7047 7048 /// While performing an inline call, we need to switch between two Sema states a few times: the 7049 /// state for the caller (with the callee's `code`, `fn_ret_ty`, etc), and the state for the callee. 7050 /// These cannot be two separate Sema instances as they must share AIR. 7051 /// Therefore, this struct acts as a helper to switch between the two. 7052 /// This switching is required during argument evaluation, where function argument analysis must be 7053 /// interleaved with resolving generic parameter types. 7054 const InlineCallSema = struct { 7055 sema: *Sema, 7056 cur: enum { 7057 caller, 7058 callee, 7059 }, 7060 7061 other_code: Zir, 7062 other_func_index: InternPool.Index, 7063 other_fn_ret_ty: Type, 7064 other_fn_ret_ty_ies: ?*InferredErrorSet, 7065 other_inst_map: InstMap, 7066 other_error_return_trace_index_on_fn_entry: Air.Inst.Ref, 7067 other_generic_owner: InternPool.Index, 7068 other_generic_call_src: LazySrcLoc, 7069 other_generic_call_decl: InternPool.OptionalDeclIndex, 7070 7071 /// Sema should currently be set up for the caller (i.e. unchanged yet). This init will not 7072 /// change that. The other parameters contain data for the callee Sema. The other modified 7073 /// Sema fields are all initialized to default values for the callee. 7074 /// Must call deinit on the result. 7075 fn init( 7076 sema: *Sema, 7077 callee_code: Zir, 7078 callee_func_index: InternPool.Index, 7079 callee_error_return_trace_index_on_fn_entry: Air.Inst.Ref, 7080 ) InlineCallSema { 7081 return .{ 7082 .sema = sema, 7083 .cur = .caller, 7084 .other_code = callee_code, 7085 .other_func_index = callee_func_index, 7086 .other_fn_ret_ty = Type.void, 7087 .other_fn_ret_ty_ies = null, 7088 .other_inst_map = .{}, 7089 .other_error_return_trace_index_on_fn_entry = callee_error_return_trace_index_on_fn_entry, 7090 .other_generic_owner = .none, 7091 .other_generic_call_src = .unneeded, 7092 .other_generic_call_decl = .none, 7093 }; 7094 } 7095 7096 /// Switch back to the caller Sema if necessary and free all temporary state of the callee Sema. 7097 fn deinit(ics: *InlineCallSema) void { 7098 switch (ics.cur) { 7099 .caller => {}, 7100 .callee => ics.swap(), 7101 } 7102 // Callee Sema owns the inst_map memory 7103 ics.other_inst_map.deinit(ics.sema.gpa); 7104 ics.* = undefined; 7105 } 7106 7107 /// Returns a Sema instance suitable for usage from the caller context. 7108 fn caller(ics: *InlineCallSema) *Sema { 7109 switch (ics.cur) { 7110 .caller => {}, 7111 .callee => ics.swap(), 7112 } 7113 return ics.sema; 7114 } 7115 7116 /// Returns a Sema instance suitable for usage from the callee context. 7117 fn callee(ics: *InlineCallSema) *Sema { 7118 switch (ics.cur) { 7119 .caller => ics.swap(), 7120 .callee => {}, 7121 } 7122 return ics.sema; 7123 } 7124 7125 /// Internal use only. Swaps to the other Sema state. 7126 fn swap(ics: *InlineCallSema) void { 7127 ics.cur = switch (ics.cur) { 7128 .caller => .callee, 7129 .callee => .caller, 7130 }; 7131 // zig fmt: off 7132 std.mem.swap(Zir, &ics.sema.code, &ics.other_code); 7133 std.mem.swap(InternPool.Index, &ics.sema.func_index, &ics.other_func_index); 7134 std.mem.swap(Type, &ics.sema.fn_ret_ty, &ics.other_fn_ret_ty); 7135 std.mem.swap(?*InferredErrorSet, &ics.sema.fn_ret_ty_ies, &ics.other_fn_ret_ty_ies); 7136 std.mem.swap(InstMap, &ics.sema.inst_map, &ics.other_inst_map); 7137 std.mem.swap(InternPool.Index, &ics.sema.generic_owner, &ics.other_generic_owner); 7138 std.mem.swap(LazySrcLoc, &ics.sema.generic_call_src, &ics.other_generic_call_src); 7139 std.mem.swap(InternPool.OptionalDeclIndex, &ics.sema.generic_call_decl, &ics.other_generic_call_decl); 7140 std.mem.swap(Air.Inst.Ref, &ics.sema.error_return_trace_index_on_fn_entry, &ics.other_error_return_trace_index_on_fn_entry); 7141 // zig fmt: on 7142 } 7143 }; 7144 7145 fn analyzeCall( 7146 sema: *Sema, 7147 block: *Block, 7148 func: Air.Inst.Ref, 7149 func_ty: Type, 7150 func_src: LazySrcLoc, 7151 call_src: LazySrcLoc, 7152 modifier: std.builtin.CallModifier, 7153 ensure_result_used: bool, 7154 args_info: CallArgsInfo, 7155 call_dbg_node: ?Zir.Inst.Index, 7156 operation: CallOperation, 7157 ) CompileError!Air.Inst.Ref { 7158 const mod = sema.mod; 7159 const ip = &mod.intern_pool; 7160 7161 const callee_ty = sema.typeOf(func); 7162 const func_ty_info = mod.typeToFunc(func_ty).?; 7163 const cc = func_ty_info.cc; 7164 if (try sema.resolveValue(func)) |func_val| 7165 if (func_val.isUndef(mod)) 7166 return sema.failWithUseOfUndef(block, call_src); 7167 if (cc == .Naked) { 7168 const maybe_decl = try sema.funcDeclSrc(func); 7169 const msg = msg: { 7170 const msg = try sema.errMsg( 7171 block, 7172 func_src, 7173 "unable to call function with naked calling convention", 7174 .{}, 7175 ); 7176 errdefer msg.destroy(sema.gpa); 7177 7178 if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{}); 7179 break :msg msg; 7180 }; 7181 return sema.failWithOwnedErrorMsg(block, msg); 7182 } 7183 7184 const call_tag: Air.Inst.Tag = switch (modifier) { 7185 .auto, 7186 .always_inline, 7187 .compile_time, 7188 .no_async, 7189 => Air.Inst.Tag.call, 7190 7191 .never_tail => Air.Inst.Tag.call_never_tail, 7192 .never_inline => Air.Inst.Tag.call_never_inline, 7193 .always_tail => Air.Inst.Tag.call_always_tail, 7194 7195 .async_kw => return sema.failWithUseOfAsync(block, call_src), 7196 }; 7197 7198 if (modifier == .never_inline and func_ty_info.cc == .Inline) { 7199 return sema.fail(block, call_src, "'never_inline' call of inline function", .{}); 7200 } 7201 if (modifier == .always_inline and func_ty_info.is_noinline) { 7202 return sema.fail(block, call_src, "'always_inline' call of noinline function", .{}); 7203 } 7204 7205 const gpa = sema.gpa; 7206 7207 var is_generic_call = func_ty_info.is_generic; 7208 var is_comptime_call = block.is_comptime or modifier == .compile_time; 7209 var comptime_reason: ?*const Block.ComptimeReason = null; 7210 if (!is_comptime_call) { 7211 if (sema.typeRequiresComptime(Type.fromInterned(func_ty_info.return_type))) |ct| { 7212 is_comptime_call = ct; 7213 if (ct) { 7214 comptime_reason = &.{ .comptime_ret_ty = .{ 7215 .block = block, 7216 .func = func, 7217 .func_src = func_src, 7218 .return_ty = Type.fromInterned(func_ty_info.return_type), 7219 } }; 7220 } 7221 } else |err| switch (err) { 7222 error.GenericPoison => is_generic_call = true, 7223 else => |e| return e, 7224 } 7225 } 7226 var is_inline_call = is_comptime_call or modifier == .always_inline or 7227 func_ty_info.cc == .Inline; 7228 7229 if (sema.func_is_naked and !is_inline_call and !is_comptime_call) { 7230 const msg = msg: { 7231 const msg = try sema.errMsg(block, call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); 7232 errdefer msg.destroy(sema.gpa); 7233 7234 switch (operation) { 7235 .call, .@"@call", .@"@panic", .@"error return" => {}, 7236 .@"safety check" => try sema.errNote(block, call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}), 7237 } 7238 break :msg msg; 7239 }; 7240 return sema.failWithOwnedErrorMsg(block, msg); 7241 } 7242 7243 if (!is_inline_call and is_generic_call) { 7244 if (sema.instantiateGenericCall( 7245 block, 7246 func, 7247 func_src, 7248 call_src, 7249 ensure_result_used, 7250 args_info, 7251 call_tag, 7252 call_dbg_node, 7253 )) |some| { 7254 return some; 7255 } else |err| switch (err) { 7256 error.GenericPoison => { 7257 is_inline_call = true; 7258 }, 7259 error.ComptimeReturn => { 7260 is_inline_call = true; 7261 is_comptime_call = true; 7262 comptime_reason = &.{ .comptime_ret_ty = .{ 7263 .block = block, 7264 .func = func, 7265 .func_src = func_src, 7266 .return_ty = Type.fromInterned(func_ty_info.return_type), 7267 } }; 7268 }, 7269 else => |e| return e, 7270 } 7271 } 7272 7273 if (is_comptime_call and modifier == .never_inline) { 7274 return sema.fail(block, call_src, "unable to perform 'never_inline' call at compile-time", .{}); 7275 } 7276 7277 const result: Air.Inst.Ref = if (is_inline_call) res: { 7278 const func_val = try sema.resolveConstDefinedValue(block, func_src, func, .{ 7279 .needed_comptime_reason = "function being called at comptime must be comptime-known", 7280 .block_comptime_reason = comptime_reason, 7281 }); 7282 const prev_fn_index = sema.func_index; 7283 const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { 7284 .extern_func => return sema.fail(block, call_src, "{s} call of extern function", .{ 7285 @as([]const u8, if (is_comptime_call) "comptime" else "inline"), 7286 }), 7287 .func => func_val.toIntern(), 7288 .ptr => |ptr| switch (ptr.addr) { 7289 .decl => |decl| blk: { 7290 const func_val_ptr = mod.declPtr(decl).val.toIntern(); 7291 const intern_index = mod.intern_pool.indexToKey(func_val_ptr); 7292 if (intern_index == .extern_func or (intern_index == .variable and intern_index.variable.is_extern)) 7293 return sema.fail(block, call_src, "{s} call of extern function pointer", .{ 7294 @as([]const u8, if (is_comptime_call) "comptime" else "inline"), 7295 }); 7296 break :blk func_val_ptr; 7297 }, 7298 else => { 7299 assert(callee_ty.isPtrAtRuntime(mod)); 7300 return sema.fail(block, call_src, "{s} call of function pointer", .{ 7301 @as([]const u8, if (is_comptime_call) "comptime" else "inline"), 7302 }); 7303 }, 7304 }, 7305 else => unreachable, 7306 }; 7307 if (func_ty_info.is_var_args) { 7308 return sema.fail(block, call_src, "{s} call of variadic function", .{ 7309 @as([]const u8, if (is_comptime_call) "comptime" else "inline"), 7310 }); 7311 } 7312 7313 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function 7314 // or an inlined call depending on what union tag the `label` field is 7315 // set to in the `Block`. 7316 // This block instruction will be used to capture the return value from the 7317 // inlined function. 7318 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 7319 try sema.air_instructions.append(gpa, .{ 7320 .tag = .block, 7321 .data = undefined, 7322 }); 7323 // This one is shared among sub-blocks within the same callee, but not 7324 // shared among the entire inline/comptime call stack. 7325 var inlining: Block.Inlining = .{ 7326 .call_block = block, 7327 .call_src = call_src, 7328 .has_comptime_args = false, 7329 .func = module_fn_index, 7330 .comptime_result = undefined, 7331 .merges = .{ 7332 .src_locs = .{}, 7333 .results = .{}, 7334 .br_list = .{}, 7335 .block_inst = block_inst, 7336 }, 7337 }; 7338 7339 const module_fn = mod.funcInfo(module_fn_index); 7340 const fn_owner_decl = mod.declPtr(module_fn.owner_decl); 7341 7342 // We effectively want a child Sema here, but can't literally do that, because we need AIR 7343 // to be shared. InlineCallSema is a wrapper which handles this for us. While `ics` is in 7344 // scope, we should use its `caller`/`callee` methods rather than using `sema` directly 7345 // whenever performing an operation where the difference matters. 7346 var ics = InlineCallSema.init( 7347 sema, 7348 fn_owner_decl.getFileScope(mod).zir, 7349 module_fn_index, 7350 block.error_return_trace_index, 7351 ); 7352 defer ics.deinit(); 7353 7354 var child_block: Block = .{ 7355 .parent = null, 7356 .sema = sema, 7357 .src_decl = module_fn.owner_decl, 7358 .namespace = fn_owner_decl.src_namespace, 7359 .wip_capture_scope = try mod.createCaptureScope(fn_owner_decl.src_scope), 7360 .instructions = .{}, 7361 .label = null, 7362 .inlining = &inlining, 7363 .is_typeof = block.is_typeof, 7364 .is_comptime = is_comptime_call, 7365 .comptime_reason = comptime_reason, 7366 .error_return_trace_index = block.error_return_trace_index, 7367 }; 7368 7369 const merges = &child_block.inlining.?.merges; 7370 7371 defer child_block.instructions.deinit(gpa); 7372 defer merges.deinit(gpa); 7373 7374 try sema.emitBackwardBranch(block, call_src); 7375 7376 // Whether this call should be memoized, set to false if the call can 7377 // mutate comptime state. 7378 var should_memoize = true; 7379 7380 // If it's a comptime function call, we need to memoize it as long as no external 7381 // comptime memory is mutated. 7382 const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); 7383 7384 const owner_info = mod.typeToFunc(fn_owner_decl.ty).?; 7385 const new_param_types = try sema.arena.alloc(InternPool.Index, owner_info.param_types.len); 7386 var new_fn_info: InternPool.GetFuncTypeKey = .{ 7387 .param_types = new_param_types, 7388 .return_type = owner_info.return_type, 7389 .noalias_bits = owner_info.noalias_bits, 7390 .alignment = if (owner_info.align_is_generic) null else owner_info.alignment, 7391 .cc = if (owner_info.cc_is_generic) null else owner_info.cc, 7392 .is_var_args = owner_info.is_var_args, 7393 .is_noinline = owner_info.is_noinline, 7394 .section_is_generic = owner_info.section_is_generic, 7395 .addrspace_is_generic = owner_info.addrspace_is_generic, 7396 .is_generic = owner_info.is_generic, 7397 }; 7398 7399 // This will have return instructions analyzed as break instructions to 7400 // the block_inst above. Here we are performing "comptime/inline semantic analysis" 7401 // for a function body, which means we must map the parameter ZIR instructions to 7402 // the AIR instructions of the callsite. The callee could be a generic function 7403 // which means its parameter type expressions must be resolved in order and used 7404 // to successively coerce the arguments. 7405 const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst); 7406 try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); 7407 7408 var arg_i: u32 = 0; 7409 for (fn_info.param_body) |inst| { 7410 const opt_noreturn_ref = try analyzeInlineCallArg( 7411 &ics, 7412 block, 7413 &child_block, 7414 inst, 7415 new_param_types, 7416 &arg_i, 7417 args_info, 7418 is_comptime_call, 7419 &should_memoize, 7420 memoized_arg_values, 7421 func_ty_info, 7422 func, 7423 ); 7424 if (opt_noreturn_ref) |ref| { 7425 // Analyzing this argument gave a ref of a noreturn type. Terminate argument analysis here. 7426 return ref; 7427 } 7428 } 7429 7430 // From here, we only really need to use the callee Sema. Make it the active one, then we 7431 // can just use `sema` directly. 7432 _ = ics.callee(); 7433 7434 if (!inlining.has_comptime_args) { 7435 if (module_fn.analysis(ip).state == .sema_failure) 7436 return error.AnalysisFail; 7437 7438 var block_it = block; 7439 while (block_it.inlining) |parent_inlining| { 7440 if (!parent_inlining.has_comptime_args and parent_inlining.func == module_fn_index) { 7441 const err_msg = try sema.errMsg(block, call_src, "inline call is recursive", .{}); 7442 return sema.failWithOwnedErrorMsg(null, err_msg); 7443 } 7444 block_it = parent_inlining.call_block; 7445 } 7446 } 7447 7448 // In case it is a generic function with an expression for the return type that depends 7449 // on parameters, we must now do the same for the return type as we just did with 7450 // each of the parameters, resolving the return type and providing it to the child 7451 // `Sema` so that it can be used for the `ret_ptr` instruction. 7452 const ret_ty_inst = if (fn_info.ret_ty_body.len != 0) 7453 try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst) 7454 else 7455 try sema.resolveInst(fn_info.ret_ty_ref); 7456 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 7457 sema.fn_ret_ty = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst); 7458 if (module_fn.analysis(ip).inferred_error_set) { 7459 // Create a fresh inferred error set type for inline/comptime calls. 7460 const ies = try sema.arena.create(InferredErrorSet); 7461 ies.* = .{ .func = .none }; 7462 sema.fn_ret_ty_ies = ies; 7463 sema.fn_ret_ty = Type.fromInterned((try ip.get(gpa, .{ .error_union_type = .{ 7464 .error_set_type = .adhoc_inferred_error_set_type, 7465 .payload_type = sema.fn_ret_ty.toIntern(), 7466 } }))); 7467 } 7468 7469 // This `res2` is here instead of directly breaking from `res` due to a stage1 7470 // bug generating invalid LLVM IR. 7471 const res2: Air.Inst.Ref = res2: { 7472 if (should_memoize and is_comptime_call) { 7473 if (mod.intern_pool.getIfExists(.{ .memoized_call = .{ 7474 .func = module_fn_index, 7475 .arg_values = memoized_arg_values, 7476 .result = .none, 7477 } })) |memoized_call_index| { 7478 const memoized_call = mod.intern_pool.indexToKey(memoized_call_index).memoized_call; 7479 break :res2 Air.internedToRef(memoized_call.result); 7480 } 7481 } 7482 7483 new_fn_info.return_type = sema.fn_ret_ty.toIntern(); 7484 const new_func_resolved_ty = try mod.funcType(new_fn_info); 7485 if (!is_comptime_call and !block.is_typeof) { 7486 try emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin); 7487 7488 const zir_tags = sema.code.instructions.items(.tag); 7489 for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) { 7490 .param, .param_comptime => { 7491 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].pl_tok; 7492 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); 7493 const param_name = sema.code.nullTerminatedString(extra.data.name); 7494 const inst = sema.inst_map.get(param).?; 7495 7496 try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name); 7497 }, 7498 .param_anytype, .param_anytype_comptime => { 7499 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok; 7500 const param_name = inst_data.get(sema.code); 7501 const inst = sema.inst_map.get(param).?; 7502 7503 try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name); 7504 }, 7505 else => continue, 7506 }; 7507 } 7508 7509 if (is_comptime_call and ensure_result_used) { 7510 try sema.ensureResultUsed(block, sema.fn_ret_ty, call_src); 7511 } 7512 7513 const result = result: { 7514 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) { 7515 error.ComptimeReturn => break :result inlining.comptime_result, 7516 else => |e| return e, 7517 }; 7518 break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); 7519 }; 7520 7521 if (!is_comptime_call and !block.is_typeof and 7522 sema.typeOf(result).zigTypeTag(mod) != .NoReturn) 7523 { 7524 try emitDbgInline( 7525 block, 7526 module_fn_index, 7527 prev_fn_index, 7528 mod.funcOwnerDeclPtr(sema.func_index).ty, 7529 .dbg_inline_end, 7530 ); 7531 } 7532 7533 if (should_memoize and is_comptime_call) { 7534 const result_val = try sema.resolveConstValue(block, .unneeded, result, undefined); 7535 const result_interned = try result_val.intern2(sema.fn_ret_ty, mod); 7536 7537 // Transform ad-hoc inferred error set types into concrete error sets. 7538 const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned); 7539 7540 // TODO: check whether any external comptime memory was mutated by the 7541 // comptime function call. If so, then do not memoize the call here. 7542 _ = try mod.intern(.{ .memoized_call = .{ 7543 .func = module_fn_index, 7544 .arg_values = memoized_arg_values, 7545 .result = result_transformed, 7546 } }); 7547 7548 break :res2 Air.internedToRef(result_transformed); 7549 } 7550 7551 if (try sema.resolveValue(result)) |result_val| { 7552 const result_interned = try result_val.intern2(sema.fn_ret_ty, mod); 7553 const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned); 7554 break :res2 Air.internedToRef(result_transformed); 7555 } 7556 7557 const new_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result).toIntern()); 7558 if (new_ty != .none) { 7559 // TODO: mutate in place the previous instruction if possible 7560 // rather than adding a bitcast instruction. 7561 break :res2 try block.addBitCast(Type.fromInterned(new_ty), result); 7562 } 7563 7564 break :res2 result; 7565 }; 7566 7567 break :res res2; 7568 } else res: { 7569 assert(!func_ty_info.is_generic); 7570 7571 const args = try sema.arena.alloc(Air.Inst.Ref, args_info.count()); 7572 for (args, 0..) |*arg_out, arg_idx| { 7573 // Non-generic, so param types are already resolved 7574 const param_ty = if (arg_idx < func_ty_info.param_types.len) ty: { 7575 break :ty Type.fromInterned(func_ty_info.param_types.get(ip)[arg_idx]); 7576 } else Type.fromInterned(InternPool.Index.var_args_param_type); 7577 assert(!param_ty.isGenericPoison()); 7578 arg_out.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, func); 7579 if (sema.typeOf(arg_out.*).zigTypeTag(mod) == .NoReturn) { 7580 return arg_out.*; 7581 } 7582 } 7583 7584 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); 7585 7586 try sema.queueFullTypeResolution(Type.fromInterned(func_ty_info.return_type)); 7587 if (sema.owner_func_index != .none and Type.fromInterned(func_ty_info.return_type).isError(mod)) { 7588 ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn = true; 7589 } 7590 7591 if (try sema.resolveValue(func)) |func_val| { 7592 if (mod.intern_pool.isFuncBody(func_val.toIntern())) { 7593 try mod.ensureFuncBodyAnalysisQueued(func_val.toIntern()); 7594 } 7595 } 7596 7597 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + 7598 args.len); 7599 const func_inst = try block.addInst(.{ 7600 .tag = call_tag, 7601 .data = .{ .pl_op = .{ 7602 .operand = func, 7603 .payload = sema.addExtraAssumeCapacity(Air.Call{ 7604 .args_len = @intCast(args.len), 7605 }), 7606 } }, 7607 }); 7608 sema.appendRefsAssumeCapacity(args); 7609 7610 if (call_tag == .call_always_tail) { 7611 if (ensure_result_used) { 7612 try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src); 7613 } 7614 return sema.handleTailCall(block, call_src, func_ty, func_inst); 7615 } 7616 if (block.wantSafety() and func_ty_info.return_type == .noreturn_type) skip_safety: { 7617 // Function pointers and extern functions aren't guaranteed to 7618 // actually be noreturn so we add a safety check for them. 7619 if (try sema.resolveValue(func)) |func_val| { 7620 switch (mod.intern_pool.indexToKey(func_val.toIntern())) { 7621 .func => break :skip_safety, 7622 .ptr => |ptr| switch (ptr.addr) { 7623 .decl => |decl| if (!mod.declPtr(decl).isExtern(mod)) break :skip_safety, 7624 else => {}, 7625 }, 7626 else => {}, 7627 } 7628 } 7629 try sema.safetyPanic(block, call_src, .noreturn_returned); 7630 return .unreachable_value; 7631 } 7632 if (func_ty_info.return_type == .noreturn_type) { 7633 _ = try block.addNoOp(.unreach); 7634 return .unreachable_value; 7635 } 7636 break :res func_inst; 7637 }; 7638 7639 if (ensure_result_used) { 7640 try sema.ensureResultUsed(block, sema.typeOf(result), call_src); 7641 } 7642 return result; 7643 } 7644 7645 fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref { 7646 const mod = sema.mod; 7647 const target = mod.getTarget(); 7648 const backend = mod.comp.getZigBackend(); 7649 if (!target_util.supportsTailCall(target, backend)) { 7650 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", .{ 7651 @tagName(backend), @tagName(target.cpu.arch), 7652 }); 7653 } 7654 const func_decl = mod.funcOwnerDeclPtr(sema.owner_func_index); 7655 if (!func_ty.eql(func_decl.ty, mod)) { 7656 return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{ 7657 func_ty.fmt(mod), func_decl.ty.fmt(mod), 7658 }); 7659 } 7660 _ = try block.addUnOp(.ret, result); 7661 return .unreachable_value; 7662 } 7663 7664 /// Usually, returns null. If an argument was noreturn, returns that ref (which should become the call result). 7665 fn analyzeInlineCallArg( 7666 ics: *InlineCallSema, 7667 arg_block: *Block, 7668 param_block: *Block, 7669 inst: Zir.Inst.Index, 7670 new_param_types: []InternPool.Index, 7671 arg_i: *u32, 7672 args_info: CallArgsInfo, 7673 is_comptime_call: bool, 7674 should_memoize: *bool, 7675 memoized_arg_values: []InternPool.Index, 7676 func_ty_info: InternPool.Key.FuncType, 7677 func_inst: Air.Inst.Ref, 7678 ) !?Air.Inst.Ref { 7679 const mod = ics.sema.mod; 7680 const ip = &mod.intern_pool; 7681 const zir_tags = ics.callee().code.instructions.items(.tag); 7682 switch (zir_tags[@intFromEnum(inst)]) { 7683 .param_comptime, .param_anytype_comptime => param_block.inlining.?.has_comptime_args = true, 7684 else => {}, 7685 } 7686 switch (zir_tags[@intFromEnum(inst)]) { 7687 .param, .param_comptime => { 7688 // Evaluate the parameter type expression now that previous ones have 7689 // been mapped, and coerce the corresponding argument to it. 7690 const pl_tok = ics.callee().code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; 7691 const param_src = pl_tok.src(); 7692 const extra = ics.callee().code.extraData(Zir.Inst.Param, pl_tok.payload_index); 7693 const param_body = ics.callee().code.bodySlice(extra.end, extra.data.body_len); 7694 const param_ty = param_ty: { 7695 const raw_param_ty = func_ty_info.param_types.get(ip)[arg_i.*]; 7696 if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty; 7697 const param_ty_inst = try ics.callee().resolveBody(param_block, param_body, inst); 7698 const param_ty = try ics.callee().analyzeAsType(param_block, param_src, param_ty_inst); 7699 break :param_ty param_ty.toIntern(); 7700 }; 7701 new_param_types[arg_i.*] = param_ty; 7702 const casted_arg = try args_info.analyzeArg(ics.caller(), arg_block, arg_i.*, Type.fromInterned(param_ty), func_ty_info, func_inst); 7703 if (ics.caller().typeOf(casted_arg).zigTypeTag(mod) == .NoReturn) { 7704 return casted_arg; 7705 } 7706 const arg_src = args_info.argSrc(arg_block, arg_i.*); 7707 if (try ics.callee().typeRequiresComptime(Type.fromInterned(param_ty))) { 7708 _ = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{ 7709 .needed_comptime_reason = "argument to parameter with comptime-only type must be comptime-known", 7710 .block_comptime_reason = param_block.comptime_reason, 7711 }); 7712 } else if (!is_comptime_call and zir_tags[@intFromEnum(inst)] == .param_comptime) { 7713 _ = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{ 7714 .needed_comptime_reason = "parameter is comptime", 7715 }); 7716 } 7717 7718 if (is_comptime_call) { 7719 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, casted_arg); 7720 const arg_val = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{ 7721 .needed_comptime_reason = "argument to function being called at comptime must be comptime-known", 7722 .block_comptime_reason = param_block.comptime_reason, 7723 }); 7724 switch (arg_val.toIntern()) { 7725 .generic_poison, .generic_poison_type => { 7726 // This function is currently evaluated as part of an as-of-yet unresolvable 7727 // parameter or return type. 7728 return error.GenericPoison; 7729 }, 7730 else => {}, 7731 } 7732 // Needed so that lazy values do not trigger 7733 // assertion due to type not being resolved 7734 // when the hash function is called. 7735 const resolved_arg_val = try ics.caller().resolveLazyValue(arg_val); 7736 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod); 7737 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(Type.fromInterned(param_ty), mod); 7738 } else { 7739 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, casted_arg); 7740 } 7741 7742 if (try ics.caller().resolveValue(casted_arg)) |_| { 7743 param_block.inlining.?.has_comptime_args = true; 7744 } 7745 7746 arg_i.* += 1; 7747 }, 7748 .param_anytype, .param_anytype_comptime => { 7749 // No coercion needed. 7750 const uncasted_arg = try args_info.analyzeArg(ics.caller(), arg_block, arg_i.*, Type.generic_poison, func_ty_info, func_inst); 7751 if (ics.caller().typeOf(uncasted_arg).zigTypeTag(mod) == .NoReturn) { 7752 return uncasted_arg; 7753 } 7754 const arg_src = args_info.argSrc(arg_block, arg_i.*); 7755 new_param_types[arg_i.*] = ics.caller().typeOf(uncasted_arg).toIntern(); 7756 7757 if (is_comptime_call) { 7758 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); 7759 const arg_val = try ics.caller().resolveConstValue(arg_block, arg_src, uncasted_arg, .{ 7760 .needed_comptime_reason = "argument to function being called at comptime must be comptime-known", 7761 .block_comptime_reason = param_block.comptime_reason, 7762 }); 7763 switch (arg_val.toIntern()) { 7764 .generic_poison, .generic_poison_type => { 7765 // This function is currently evaluated as part of an as-of-yet unresolvable 7766 // parameter or return type. 7767 return error.GenericPoison; 7768 }, 7769 else => {}, 7770 } 7771 // Needed so that lazy values do not trigger 7772 // assertion due to type not being resolved 7773 // when the hash function is called. 7774 const resolved_arg_val = try ics.caller().resolveLazyValue(arg_val); 7775 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod); 7776 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(ics.caller().typeOf(uncasted_arg), mod); 7777 } else { 7778 if (zir_tags[@intFromEnum(inst)] == .param_anytype_comptime) { 7779 _ = try ics.caller().resolveConstValue(arg_block, arg_src, uncasted_arg, .{ 7780 .needed_comptime_reason = "parameter is comptime", 7781 }); 7782 } 7783 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); 7784 } 7785 7786 if (try ics.caller().resolveValue(uncasted_arg)) |_| { 7787 param_block.inlining.?.has_comptime_args = true; 7788 } 7789 7790 arg_i.* += 1; 7791 }, 7792 else => {}, 7793 } 7794 7795 return null; 7796 } 7797 7798 fn instantiateGenericCall( 7799 sema: *Sema, 7800 block: *Block, 7801 func: Air.Inst.Ref, 7802 func_src: LazySrcLoc, 7803 call_src: LazySrcLoc, 7804 ensure_result_used: bool, 7805 args_info: CallArgsInfo, 7806 call_tag: Air.Inst.Tag, 7807 call_dbg_node: ?Zir.Inst.Index, 7808 ) CompileError!Air.Inst.Ref { 7809 const mod = sema.mod; 7810 const gpa = sema.gpa; 7811 const ip = &mod.intern_pool; 7812 7813 const func_val = try sema.resolveConstDefinedValue(block, func_src, func, .{ 7814 .needed_comptime_reason = "generic function being called must be comptime-known", 7815 }); 7816 const generic_owner = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { 7817 .func => func_val.toIntern(), 7818 .ptr => |ptr| mod.declPtr(ptr.addr.decl).val.toIntern(), 7819 else => unreachable, 7820 }; 7821 const generic_owner_func = mod.intern_pool.indexToKey(generic_owner).func; 7822 const generic_owner_ty_info = mod.typeToFunc(Type.fromInterned(generic_owner_func.ty)).?; 7823 7824 // Even though there may already be a generic instantiation corresponding 7825 // to this callsite, we must evaluate the expressions of the generic 7826 // function signature with the values of the callsite plugged in. 7827 // Importantly, this may include type coercions that determine whether the 7828 // instantiation is a match of a previous instantiation. 7829 // The actual monomorphization happens via adding `func_instance` to 7830 // `InternPool`. 7831 7832 const fn_owner_decl = mod.declPtr(generic_owner_func.owner_decl); 7833 const namespace_index = fn_owner_decl.src_namespace; 7834 const namespace = mod.namespacePtr(namespace_index); 7835 const fn_zir = namespace.file_scope.zir; 7836 const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst); 7837 7838 const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count()); 7839 @memset(comptime_args, .none); 7840 7841 // We may overestimate the number of runtime args, but this will definitely be sufficient. 7842 const max_runtime_args = args_info.count() - @popCount(generic_owner_ty_info.comptime_bits); 7843 var runtime_args = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(sema.arena, max_runtime_args); 7844 7845 // Re-run the block that creates the function, with the comptime parameters 7846 // pre-populated inside `inst_map`. This causes `param_comptime` and 7847 // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a 7848 // new, monomorphized function, with the comptime parameters elided. 7849 var child_sema: Sema = .{ 7850 .mod = mod, 7851 .gpa = gpa, 7852 .arena = sema.arena, 7853 .code = fn_zir, 7854 // We pass the generic callsite's owner decl here because whatever `Decl` 7855 // dependencies are chased at this point should be attached to the 7856 // callsite, not the `Decl` associated with the `func_instance`. 7857 .owner_decl = sema.owner_decl, 7858 .owner_decl_index = sema.owner_decl_index, 7859 .func_index = sema.owner_func_index, 7860 // This may not be known yet, since the calling convention could be generic, but there 7861 // should be no illegal instructions encountered while creating the function anyway. 7862 .func_is_naked = false, 7863 .fn_ret_ty = Type.void, 7864 .fn_ret_ty_ies = null, 7865 .owner_func_index = .none, 7866 .comptime_args = comptime_args, 7867 .generic_owner = generic_owner, 7868 .generic_call_src = call_src, 7869 .generic_call_decl = block.src_decl.toOptional(), 7870 .branch_quota = sema.branch_quota, 7871 .branch_count = sema.branch_count, 7872 .comptime_mutable_decls = sema.comptime_mutable_decls, 7873 }; 7874 defer child_sema.deinit(); 7875 7876 var child_block: Block = .{ 7877 .parent = null, 7878 .sema = &child_sema, 7879 .src_decl = generic_owner_func.owner_decl, 7880 .namespace = namespace_index, 7881 .wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope), 7882 .instructions = .{}, 7883 .inlining = null, 7884 .is_comptime = true, 7885 }; 7886 defer child_block.instructions.deinit(gpa); 7887 7888 try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); 7889 7890 for (fn_info.param_body[0..args_info.count()], 0..) |param_inst, arg_index| { 7891 const param_tag = fn_zir.instructions.items(.tag)[@intFromEnum(param_inst)]; 7892 7893 const param_ty = switch (generic_owner_ty_info.param_types.get(ip)[arg_index]) { 7894 else => |ty| Type.fromInterned(ty), // parameter is not generic, so type is already resolved 7895 .generic_poison_type => param_ty: { 7896 // We have every parameter before this one, so can resolve this parameter's type now. 7897 // However, first check the param type, since it may be anytype. 7898 switch (param_tag) { 7899 .param_anytype, .param_anytype_comptime => { 7900 // The parameter doesn't have a type. 7901 break :param_ty Type.generic_poison; 7902 }, 7903 .param, .param_comptime => { 7904 // We now know every prior parameter, so can resolve this 7905 // parameter's type. The child sema has these types. 7906 const param_data = fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok; 7907 const param_extra = fn_zir.extraData(Zir.Inst.Param, param_data.payload_index); 7908 const param_ty_body = fn_zir.bodySlice(param_extra.end, param_extra.data.body_len); 7909 7910 // Make sure any nested instructions don't clobber our work. 7911 const prev_params = child_block.params; 7912 const prev_no_partial_func_ty = child_sema.no_partial_func_ty; 7913 const prev_generic_owner = child_sema.generic_owner; 7914 const prev_generic_call_src = child_sema.generic_call_src; 7915 const prev_generic_call_decl = child_sema.generic_call_decl; 7916 child_block.params = .{}; 7917 child_sema.no_partial_func_ty = true; 7918 child_sema.generic_owner = .none; 7919 child_sema.generic_call_src = .unneeded; 7920 child_sema.generic_call_decl = .none; 7921 defer { 7922 child_block.params = prev_params; 7923 child_sema.no_partial_func_ty = prev_no_partial_func_ty; 7924 child_sema.generic_owner = prev_generic_owner; 7925 child_sema.generic_call_src = prev_generic_call_src; 7926 child_sema.generic_call_decl = prev_generic_call_decl; 7927 } 7928 7929 const param_ty_inst = try child_sema.resolveBody(&child_block, param_ty_body, param_inst); 7930 break :param_ty try child_sema.analyzeAsType(&child_block, param_data.src(), param_ty_inst); 7931 }, 7932 else => unreachable, 7933 } 7934 }, 7935 }; 7936 const arg_ref = try args_info.analyzeArg(sema, block, arg_index, param_ty, generic_owner_ty_info, func); 7937 const arg_ty = sema.typeOf(arg_ref); 7938 if (arg_ty.zigTypeTag(mod) == .NoReturn) { 7939 // This terminates argument analysis. 7940 return arg_ref; 7941 } 7942 7943 const arg_is_comptime = switch (param_tag) { 7944 .param_comptime, .param_anytype_comptime => true, 7945 .param, .param_anytype => try sema.typeRequiresComptime(arg_ty), 7946 else => unreachable, 7947 }; 7948 7949 if (arg_is_comptime) { 7950 if (try sema.resolveValue(arg_ref)) |arg_val| { 7951 comptime_args[arg_index] = arg_val.toIntern(); 7952 child_sema.inst_map.putAssumeCapacityNoClobber( 7953 param_inst, 7954 Air.internedToRef(arg_val.toIntern()), 7955 ); 7956 } else switch (param_tag) { 7957 .param_comptime, 7958 .param_anytype_comptime, 7959 => return sema.failWithOwnedErrorMsg(block, msg: { 7960 const arg_src = args_info.argSrc(block, arg_index); 7961 const msg = try sema.errMsg(block, arg_src, "runtime-known argument passed to comptime parameter", .{}); 7962 errdefer msg.destroy(sema.gpa); 7963 const param_src = switch (param_tag) { 7964 .param_comptime => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok.src(), 7965 .param_anytype_comptime => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.src(), 7966 else => unreachable, 7967 }; 7968 try child_sema.errNote(&child_block, param_src, msg, "declared comptime here", .{}); 7969 break :msg msg; 7970 }), 7971 7972 .param, 7973 .param_anytype, 7974 => return sema.failWithOwnedErrorMsg(block, msg: { 7975 const arg_src = args_info.argSrc(block, arg_index); 7976 const msg = try sema.errMsg(block, arg_src, "runtime-known argument passed to parameter of comptime-only type", .{}); 7977 errdefer msg.destroy(sema.gpa); 7978 const param_src = switch (param_tag) { 7979 .param => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok.src(), 7980 .param_anytype => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.src(), 7981 else => unreachable, 7982 }; 7983 try child_sema.errNote(&child_block, param_src, msg, "declared here", .{}); 7984 const src_decl = mod.declPtr(block.src_decl); 7985 try sema.explainWhyTypeIsComptime(msg, arg_src.toSrcLoc(src_decl, mod), arg_ty); 7986 break :msg msg; 7987 }), 7988 7989 else => unreachable, 7990 } 7991 } else { 7992 // The parameter is runtime-known. 7993 try sema.queueFullTypeResolution(arg_ty); 7994 child_sema.inst_map.putAssumeCapacityNoClobber(param_inst, try child_block.addInst(.{ 7995 .tag = .arg, 7996 .data = .{ .arg = .{ 7997 .ty = Air.internedToRef(arg_ty.toIntern()), 7998 .src_index = @intCast(arg_index), 7999 } }, 8000 })); 8001 const param_name: Zir.NullTerminatedString = switch (param_tag) { 8002 .param_anytype => @enumFromInt(fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.start), 8003 .param => name: { 8004 const inst_data = fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok; 8005 const extra = fn_zir.extraData(Zir.Inst.Param, inst_data.payload_index); 8006 break :name @enumFromInt(extra.data.name); 8007 }, 8008 else => unreachable, 8009 }; 8010 try child_block.params.append(sema.arena, .{ 8011 .ty = arg_ty.toIntern(), // This is the type after coercion 8012 .is_comptime = false, // We're adding only runtime args to the instantiation 8013 .name = param_name, 8014 }); 8015 runtime_args.appendAssumeCapacity(arg_ref); 8016 } 8017 } 8018 8019 // We've already handled parameters, so don't resolve the whole body. Instead, just 8020 // do the instructions after the params (i.e. the func itself). 8021 const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body[args_info.count()..], fn_info.param_body_inst); 8022 const callee_index = (child_sema.resolveConstDefinedValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable).toIntern(); 8023 8024 const callee = mod.funcInfo(callee_index); 8025 callee.branchQuota(ip).* = @max(callee.branchQuota(ip).*, sema.branch_quota); 8026 8027 try sema.addReferencedBy(block, call_src, callee.owner_decl); 8028 8029 // Make a runtime call to the new function, making sure to omit the comptime args. 8030 const func_ty = Type.fromInterned(callee.ty); 8031 const func_ty_info = mod.typeToFunc(func_ty).?; 8032 8033 // If the call evaluated to a return type that requires comptime, never mind 8034 // our generic instantiation. Instead we need to perform a comptime call. 8035 if (try sema.typeRequiresComptime(Type.fromInterned(func_ty_info.return_type))) { 8036 return error.ComptimeReturn; 8037 } 8038 // Similarly, if the call evaluated to a generic type we need to instead 8039 // call it inline. 8040 if (func_ty_info.is_generic or func_ty_info.cc == .Inline) { 8041 return error.GenericPoison; 8042 } 8043 8044 try sema.queueFullTypeResolution(Type.fromInterned(func_ty_info.return_type)); 8045 8046 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); 8047 8048 if (sema.owner_func_index != .none and 8049 Type.fromInterned(func_ty_info.return_type).isError(mod)) 8050 { 8051 ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn = true; 8052 } 8053 8054 try mod.ensureFuncBodyAnalysisQueued(callee_index); 8055 8056 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + runtime_args.items.len); 8057 const result = try block.addInst(.{ 8058 .tag = call_tag, 8059 .data = .{ .pl_op = .{ 8060 .operand = Air.internedToRef(callee_index), 8061 .payload = sema.addExtraAssumeCapacity(Air.Call{ 8062 .args_len = @intCast(runtime_args.items.len), 8063 }), 8064 } }, 8065 }); 8066 sema.appendRefsAssumeCapacity(runtime_args.items); 8067 8068 if (ensure_result_used) { 8069 try sema.ensureResultUsed(block, sema.typeOf(result), call_src); 8070 } 8071 if (call_tag == .call_always_tail) { 8072 return sema.handleTailCall(block, call_src, func_ty, result); 8073 } 8074 if (func_ty.fnReturnType(mod).isNoReturn(mod)) { 8075 _ = try block.addNoOp(.unreach); 8076 return .unreachable_value; 8077 } 8078 return result; 8079 } 8080 8081 fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 8082 const mod = sema.mod; 8083 const ip = &mod.intern_pool; 8084 const tuple = switch (ip.indexToKey(ty.toIntern())) { 8085 .anon_struct_type => |tuple| tuple, 8086 else => return, 8087 }; 8088 for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| { 8089 try sema.resolveTupleLazyValues(block, src, Type.fromInterned(field_ty)); 8090 if (field_val == .none) continue; 8091 // TODO: mutate in intern pool 8092 _ = try sema.resolveLazyValue(Value.fromInterned(field_val)); 8093 } 8094 } 8095 8096 fn emitDbgInline( 8097 block: *Block, 8098 old_func: InternPool.Index, 8099 new_func: InternPool.Index, 8100 new_func_ty: Type, 8101 tag: Air.Inst.Tag, 8102 ) CompileError!void { 8103 if (block.ownerModule().strip) return; 8104 8105 // Recursive inline call; no dbg_inline needed. 8106 if (old_func == new_func) return; 8107 8108 _ = try block.addInst(.{ 8109 .tag = tag, 8110 .data = .{ .ty_fn = .{ 8111 .ty = Air.internedToRef(new_func_ty.toIntern()), 8112 .func = new_func, 8113 } }, 8114 }); 8115 } 8116 8117 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8118 const mod = sema.mod; 8119 const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type; 8120 const ty = try mod.intType(int_type.signedness, int_type.bit_count); 8121 return Air.internedToRef(ty.toIntern()); 8122 } 8123 8124 fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8125 const tracy = trace(@src()); 8126 defer tracy.end(); 8127 8128 const mod = sema.mod; 8129 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8130 const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 8131 const child_type = try sema.resolveType(block, operand_src, inst_data.operand); 8132 if (child_type.zigTypeTag(mod) == .Opaque) { 8133 return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(mod)}); 8134 } else if (child_type.zigTypeTag(mod) == .Null) { 8135 return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(mod)}); 8136 } 8137 const opt_type = try mod.optionalType(child_type.toIntern()); 8138 8139 return Air.internedToRef(opt_type.toIntern()); 8140 } 8141 8142 fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8143 const mod = sema.mod; 8144 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 8145 const maybe_wrapped_indexable_ty = sema.resolveType(block, .unneeded, bin.lhs) catch |err| switch (err) { 8146 // Since this is a ZIR instruction that returns a type, encountering 8147 // generic poison should not result in a failed compilation, but the 8148 // generic poison type. This prevents unnecessary failures when 8149 // constructing types at compile-time. 8150 error.GenericPoison => return .generic_poison_type, 8151 else => |e| return e, 8152 }; 8153 const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(mod); 8154 try sema.resolveTypeFields(indexable_ty); 8155 assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction 8156 if (indexable_ty.zigTypeTag(mod) == .Struct) { 8157 const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod); 8158 return Air.internedToRef(elem_type.toIntern()); 8159 } else { 8160 const elem_type = indexable_ty.elemType2(mod); 8161 return Air.internedToRef(elem_type.toIntern()); 8162 } 8163 } 8164 8165 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8166 const mod = sema.mod; 8167 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8168 const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, un_node.operand) catch |err| switch (err) { 8169 error.GenericPoison => return .generic_poison_type, 8170 else => |e| return e, 8171 }; 8172 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod); 8173 assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction 8174 const elem_ty = ptr_ty.childType(mod); 8175 if (elem_ty.toIntern() == .anyopaque_type) { 8176 // The pointer's actual child type is effectively unknown, so it makes 8177 // sense to represent it with a generic poison. 8178 return .generic_poison_type; 8179 } 8180 return Air.internedToRef(ptr_ty.childType(mod).toIntern()); 8181 } 8182 8183 fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8184 const mod = sema.mod; 8185 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8186 const src = un_node.src(); 8187 const ptr_ty = sema.resolveType(block, src, un_node.operand) catch |err| switch (err) { 8188 error.GenericPoison => return .generic_poison_type, 8189 else => |e| return e, 8190 }; 8191 try sema.checkMemOperand(block, src, ptr_ty); 8192 const elem_ty = switch (ptr_ty.ptrSize(mod)) { 8193 .Slice, .Many, .C => ptr_ty.childType(mod), 8194 .One => ptr_ty.childType(mod).childType(mod), 8195 }; 8196 return Air.internedToRef(elem_ty.toIntern()); 8197 } 8198 8199 fn zirVectorElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8200 const mod = sema.mod; 8201 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8202 const vec_ty = sema.resolveType(block, .unneeded, un_node.operand) catch |err| switch (err) { 8203 // Since this is a ZIR instruction that returns a type, encountering 8204 // generic poison should not result in a failed compilation, but the 8205 // generic poison type. This prevents unnecessary failures when 8206 // constructing types at compile-time. 8207 error.GenericPoison => return .generic_poison_type, 8208 else => |e| return e, 8209 }; 8210 if (!vec_ty.isVector(mod)) { 8211 return sema.fail(block, un_node.src(), "expected vector type, found '{}'", .{vec_ty.fmt(mod)}); 8212 } 8213 return Air.internedToRef(vec_ty.childType(mod).toIntern()); 8214 } 8215 8216 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8217 const mod = sema.mod; 8218 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8219 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 8220 const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 8221 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8222 const len: u32 = @intCast(try sema.resolveInt(block, len_src, extra.lhs, Type.u32, .{ 8223 .needed_comptime_reason = "vector length must be comptime-known", 8224 })); 8225 const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs); 8226 try sema.checkVectorElemType(block, elem_type_src, elem_type); 8227 const vector_type = try mod.vectorType(.{ 8228 .len = len, 8229 .child = elem_type.toIntern(), 8230 }); 8231 return Air.internedToRef(vector_type.toIntern()); 8232 } 8233 8234 fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8235 const tracy = trace(@src()); 8236 defer tracy.end(); 8237 8238 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8239 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8240 const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; 8241 const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; 8242 const len = try sema.resolveInt(block, len_src, extra.lhs, Type.usize, .{ 8243 .needed_comptime_reason = "array length must be comptime-known", 8244 }); 8245 const elem_type = try sema.resolveType(block, elem_src, extra.rhs); 8246 try sema.validateArrayElemType(block, elem_type, elem_src); 8247 const array_ty = try sema.mod.arrayType(.{ 8248 .len = len, 8249 .child = elem_type.toIntern(), 8250 }); 8251 8252 return Air.internedToRef(array_ty.toIntern()); 8253 } 8254 8255 fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8256 const tracy = trace(@src()); 8257 defer tracy.end(); 8258 8259 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8260 const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; 8261 const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; 8262 const sentinel_src: LazySrcLoc = .{ .node_offset_array_type_sentinel = inst_data.src_node }; 8263 const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; 8264 const len = try sema.resolveInt(block, len_src, extra.len, Type.usize, .{ 8265 .needed_comptime_reason = "array length must be comptime-known", 8266 }); 8267 const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); 8268 try sema.validateArrayElemType(block, elem_type, elem_src); 8269 const uncasted_sentinel = try sema.resolveInst(extra.sentinel); 8270 const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); 8271 const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ 8272 .needed_comptime_reason = "array sentinel value must be comptime-known", 8273 }); 8274 const array_ty = try sema.mod.arrayType(.{ 8275 .len = len, 8276 .sentinel = sentinel_val.toIntern(), 8277 .child = elem_type.toIntern(), 8278 }); 8279 8280 return Air.internedToRef(array_ty.toIntern()); 8281 } 8282 8283 fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void { 8284 const mod = sema.mod; 8285 if (elem_type.zigTypeTag(mod) == .Opaque) { 8286 return sema.fail(block, elem_src, "array of opaque type '{}' not allowed", .{elem_type.fmt(mod)}); 8287 } else if (elem_type.zigTypeTag(mod) == .NoReturn) { 8288 return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{}); 8289 } 8290 } 8291 8292 fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8293 const tracy = trace(@src()); 8294 defer tracy.end(); 8295 8296 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8297 if (true) { 8298 return sema.failWithUseOfAsync(block, inst_data.src()); 8299 } 8300 const mod = sema.mod; 8301 const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node }; 8302 const return_type = try sema.resolveType(block, operand_src, inst_data.operand); 8303 const anyframe_type = try mod.anyframeType(return_type); 8304 8305 return Air.internedToRef(anyframe_type.toIntern()); 8306 } 8307 8308 fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8309 const tracy = trace(@src()); 8310 defer tracy.end(); 8311 8312 const mod = sema.mod; 8313 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8314 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8315 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 8316 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 8317 const error_set = try sema.resolveType(block, lhs_src, extra.lhs); 8318 const payload = try sema.resolveType(block, rhs_src, extra.rhs); 8319 8320 if (error_set.zigTypeTag(mod) != .ErrorSet) { 8321 return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{ 8322 error_set.fmt(mod), 8323 }); 8324 } 8325 try sema.validateErrorUnionPayloadType(block, payload, rhs_src); 8326 const err_union_ty = try mod.errorUnionType(error_set, payload); 8327 return Air.internedToRef(err_union_ty.toIntern()); 8328 } 8329 8330 fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void { 8331 const mod = sema.mod; 8332 if (payload_ty.zigTypeTag(mod) == .Opaque) { 8333 return sema.fail(block, payload_src, "error union with payload of opaque type '{}' not allowed", .{ 8334 payload_ty.fmt(mod), 8335 }); 8336 } else if (payload_ty.zigTypeTag(mod) == .ErrorSet) { 8337 return sema.fail(block, payload_src, "error union with payload of error set type '{}' not allowed", .{ 8338 payload_ty.fmt(mod), 8339 }); 8340 } 8341 } 8342 8343 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8344 _ = block; 8345 const mod = sema.mod; 8346 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 8347 const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 8348 _ = try mod.getErrorValue(name); 8349 // Create an error set type with only this error value, and return the value. 8350 const error_set_type = try mod.singleErrorSetType(name); 8351 return Air.internedToRef((try mod.intern(.{ .err = .{ 8352 .ty = error_set_type.toIntern(), 8353 .name = name, 8354 } }))); 8355 } 8356 8357 fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 8358 const tracy = trace(@src()); 8359 defer tracy.end(); 8360 8361 const mod = sema.mod; 8362 const ip = &mod.intern_pool; 8363 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 8364 const src = LazySrcLoc.nodeOffset(extra.node); 8365 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 8366 const uncasted_operand = try sema.resolveInst(extra.operand); 8367 const operand = try sema.coerce(block, Type.anyerror, uncasted_operand, operand_src); 8368 const err_int_ty = try mod.errorIntType(); 8369 8370 if (try sema.resolveValue(operand)) |val| { 8371 if (val.isUndef(mod)) { 8372 return mod.undefRef(err_int_ty); 8373 } 8374 const err_name = ip.indexToKey(val.toIntern()).err.name; 8375 return Air.internedToRef((try mod.intValue( 8376 err_int_ty, 8377 try mod.getErrorValue(err_name), 8378 )).toIntern()); 8379 } 8380 8381 const op_ty = sema.typeOf(uncasted_operand); 8382 switch (try sema.resolveInferredErrorSetTy(block, src, op_ty.toIntern())) { 8383 .anyerror_type => {}, 8384 else => |err_set_ty_index| { 8385 const names = ip.indexToKey(err_set_ty_index).error_set_type.names; 8386 switch (names.len) { 8387 0 => return Air.internedToRef((try mod.intValue(err_int_ty, 0)).toIntern()), 8388 1 => { 8389 const int: Module.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[0]).?); 8390 return mod.intRef(err_int_ty, int); 8391 }, 8392 else => {}, 8393 } 8394 }, 8395 } 8396 8397 try sema.requireRuntimeBlock(block, src, operand_src); 8398 return block.addBitCast(err_int_ty, operand); 8399 } 8400 8401 fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 8402 const tracy = trace(@src()); 8403 defer tracy.end(); 8404 8405 const mod = sema.mod; 8406 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 8407 const src = LazySrcLoc.nodeOffset(extra.node); 8408 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 8409 const uncasted_operand = try sema.resolveInst(extra.operand); 8410 const err_int_ty = try mod.errorIntType(); 8411 const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src); 8412 8413 if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { 8414 const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(mod)); 8415 if (int > mod.global_error_set.count() or int == 0) 8416 return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int}); 8417 return Air.internedToRef((try mod.intern(.{ .err = .{ 8418 .ty = .anyerror_type, 8419 .name = mod.global_error_set.keys()[int], 8420 } }))); 8421 } 8422 try sema.requireRuntimeBlock(block, src, operand_src); 8423 if (block.wantSafety()) { 8424 const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); 8425 const zero_val = Air.internedToRef((try mod.intValue(err_int_ty, 0)).toIntern()); 8426 const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val); 8427 const ok = try block.addBinOp(.bool_and, is_lt_len, is_non_zero); 8428 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 8429 } 8430 return block.addInst(.{ 8431 .tag = .bitcast, 8432 .data = .{ .ty_op = .{ 8433 .ty = .anyerror_type, 8434 .operand = operand, 8435 } }, 8436 }); 8437 } 8438 8439 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8440 const tracy = trace(@src()); 8441 defer tracy.end(); 8442 8443 const mod = sema.mod; 8444 const ip = &mod.intern_pool; 8445 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8446 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8447 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 8448 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 8449 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 8450 const lhs = try sema.resolveInst(extra.lhs); 8451 const rhs = try sema.resolveInst(extra.rhs); 8452 if (sema.typeOf(lhs).zigTypeTag(mod) == .Bool and sema.typeOf(rhs).zigTypeTag(mod) == .Bool) { 8453 const msg = msg: { 8454 const msg = try sema.errMsg(block, lhs_src, "expected error set type, found 'bool'", .{}); 8455 errdefer msg.destroy(sema.gpa); 8456 try sema.errNote(block, src, msg, "'||' merges error sets; 'or' performs boolean OR", .{}); 8457 break :msg msg; 8458 }; 8459 return sema.failWithOwnedErrorMsg(block, msg); 8460 } 8461 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); 8462 const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); 8463 if (lhs_ty.zigTypeTag(mod) != .ErrorSet) 8464 return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{lhs_ty.fmt(mod)}); 8465 if (rhs_ty.zigTypeTag(mod) != .ErrorSet) 8466 return sema.fail(block, rhs_src, "expected error set type, found '{}'", .{rhs_ty.fmt(mod)}); 8467 8468 // Anything merged with anyerror is anyerror. 8469 if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) { 8470 return .anyerror_type; 8471 } 8472 8473 if (ip.isInferredErrorSetType(lhs_ty.toIntern())) { 8474 switch (try sema.resolveInferredErrorSet(block, src, lhs_ty.toIntern())) { 8475 // isAnyError might have changed from a false negative to a true 8476 // positive after resolution. 8477 .anyerror_type => return .anyerror_type, 8478 else => {}, 8479 } 8480 } 8481 if (ip.isInferredErrorSetType(rhs_ty.toIntern())) { 8482 switch (try sema.resolveInferredErrorSet(block, src, rhs_ty.toIntern())) { 8483 // isAnyError might have changed from a false negative to a true 8484 // positive after resolution. 8485 .anyerror_type => return .anyerror_type, 8486 else => {}, 8487 } 8488 } 8489 8490 const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty); 8491 return Air.internedToRef(err_set_ty.toIntern()); 8492 } 8493 8494 fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8495 _ = block; 8496 const tracy = trace(@src()); 8497 defer tracy.end(); 8498 8499 const mod = sema.mod; 8500 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 8501 const name = inst_data.get(sema.code); 8502 return Air.internedToRef((try mod.intern(.{ 8503 .enum_literal = try mod.intern_pool.getOrPutString(sema.gpa, name), 8504 }))); 8505 } 8506 8507 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8508 const mod = sema.mod; 8509 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8510 const src = inst_data.src(); 8511 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 8512 const operand = try sema.resolveInst(inst_data.operand); 8513 const operand_ty = sema.typeOf(operand); 8514 8515 const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(mod)) { 8516 .Enum => operand, 8517 .Union => blk: { 8518 try sema.resolveTypeFields(operand_ty); 8519 const tag_ty = operand_ty.unionTagType(mod) orelse { 8520 return sema.fail( 8521 block, 8522 operand_src, 8523 "untagged union '{}' cannot be converted to integer", 8524 .{src}, 8525 ); 8526 }; 8527 break :blk try sema.unionToTag(block, tag_ty, operand, operand_src); 8528 }, 8529 else => { 8530 return sema.fail(block, operand_src, "expected enum or tagged union, found '{}'", .{ 8531 operand_ty.fmt(mod), 8532 }); 8533 }, 8534 }; 8535 const enum_tag_ty = sema.typeOf(enum_tag); 8536 8537 const int_tag_ty = enum_tag_ty.intTagType(mod); 8538 8539 if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| { 8540 return Air.internedToRef((try mod.getCoerced(opv, int_tag_ty)).toIntern()); 8541 } 8542 8543 if (try sema.resolveValue(enum_tag)) |enum_tag_val| { 8544 const val = try enum_tag_val.intFromEnum(enum_tag_ty, mod); 8545 return Air.internedToRef(val.toIntern()); 8546 } 8547 8548 try sema.requireRuntimeBlock(block, src, operand_src); 8549 return block.addBitCast(int_tag_ty, enum_tag); 8550 } 8551 8552 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8553 const mod = sema.mod; 8554 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8555 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8556 const src = inst_data.src(); 8557 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 8558 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt"); 8559 const operand = try sema.resolveInst(extra.rhs); 8560 8561 if (dest_ty.zigTypeTag(mod) != .Enum) { 8562 return sema.fail(block, src, "expected enum, found '{}'", .{dest_ty.fmt(mod)}); 8563 } 8564 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); 8565 8566 if (try sema.resolveValue(operand)) |int_val| { 8567 if (dest_ty.isNonexhaustiveEnum(mod)) { 8568 const int_tag_ty = dest_ty.intTagType(mod); 8569 if (try sema.intFitsInType(int_val, int_tag_ty, null)) { 8570 return Air.internedToRef((try mod.getCoerced(int_val, dest_ty)).toIntern()); 8571 } 8572 const msg = msg: { 8573 const msg = try sema.errMsg( 8574 block, 8575 src, 8576 "int value '{}' out of range of non-exhaustive enum '{}'", 8577 .{ int_val.fmtValue(sema.typeOf(operand), mod), dest_ty.fmt(mod) }, 8578 ); 8579 errdefer msg.destroy(sema.gpa); 8580 try sema.addDeclaredHereNote(msg, dest_ty); 8581 break :msg msg; 8582 }; 8583 return sema.failWithOwnedErrorMsg(block, msg); 8584 } 8585 if (int_val.isUndef(mod)) { 8586 return sema.failWithUseOfUndef(block, operand_src); 8587 } 8588 if (!(try sema.enumHasInt(dest_ty, int_val))) { 8589 const msg = msg: { 8590 const msg = try sema.errMsg( 8591 block, 8592 src, 8593 "enum '{}' has no tag with value '{}'", 8594 .{ dest_ty.fmt(mod), int_val.fmtValue(sema.typeOf(operand), mod) }, 8595 ); 8596 errdefer msg.destroy(sema.gpa); 8597 try sema.addDeclaredHereNote(msg, dest_ty); 8598 break :msg msg; 8599 }; 8600 return sema.failWithOwnedErrorMsg(block, msg); 8601 } 8602 return Air.internedToRef((try mod.getCoerced(int_val, dest_ty)).toIntern()); 8603 } 8604 8605 if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| { 8606 const result = Air.internedToRef(opv.toIntern()); 8607 // The operand is runtime-known but the result is comptime-known. In 8608 // this case we still need a safety check. 8609 // TODO add a safety check here. we can't use is_named_enum_value - 8610 // it needs to convert the enum back to int and make sure it equals the operand int. 8611 return result; 8612 } 8613 8614 try sema.requireRuntimeBlock(block, src, operand_src); 8615 const result = try block.addTyOp(.intcast, dest_ty, operand); 8616 if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum(mod) and 8617 mod.backendSupportsFeature(.is_named_enum_value)) 8618 { 8619 const ok = try block.addUnOp(.is_named_enum_value, result); 8620 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); 8621 } 8622 return result; 8623 } 8624 8625 /// Pointer in, pointer out. 8626 fn zirOptionalPayloadPtr( 8627 sema: *Sema, 8628 block: *Block, 8629 inst: Zir.Inst.Index, 8630 safety_check: bool, 8631 ) CompileError!Air.Inst.Ref { 8632 const tracy = trace(@src()); 8633 defer tracy.end(); 8634 8635 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8636 const optional_ptr = try sema.resolveInst(inst_data.operand); 8637 const src = inst_data.src(); 8638 8639 return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); 8640 } 8641 8642 fn analyzeOptionalPayloadPtr( 8643 sema: *Sema, 8644 block: *Block, 8645 src: LazySrcLoc, 8646 optional_ptr: Air.Inst.Ref, 8647 safety_check: bool, 8648 initializing: bool, 8649 ) CompileError!Air.Inst.Ref { 8650 const mod = sema.mod; 8651 const optional_ptr_ty = sema.typeOf(optional_ptr); 8652 assert(optional_ptr_ty.zigTypeTag(mod) == .Pointer); 8653 8654 const opt_type = optional_ptr_ty.childType(mod); 8655 if (opt_type.zigTypeTag(mod) != .Optional) { 8656 return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(mod)}); 8657 } 8658 8659 const child_type = opt_type.optionalChild(mod); 8660 const child_pointer = try sema.ptrType(.{ 8661 .child = child_type.toIntern(), 8662 .flags = .{ 8663 .is_const = optional_ptr_ty.isConstPtr(mod), 8664 .address_space = optional_ptr_ty.ptrAddressSpace(mod), 8665 }, 8666 }); 8667 8668 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| { 8669 if (initializing) { 8670 if (!ptr_val.isComptimeMutablePtr(mod)) { 8671 // If the pointer resulting from this function was stored at comptime, 8672 // the optional non-null bit would be set that way. But in this case, 8673 // we need to emit a runtime instruction to do it. 8674 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); 8675 try sema.checkKnownAllocPtr(optional_ptr, opt_payload_ptr); 8676 } 8677 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 8678 .ty = child_pointer.toIntern(), 8679 .addr = .{ .opt_payload = ptr_val.toIntern() }, 8680 } }))); 8681 } 8682 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| { 8683 if (val.isNull(mod)) { 8684 return sema.fail(block, src, "unable to unwrap null", .{}); 8685 } 8686 // The same Value represents the pointer to the optional and the payload. 8687 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 8688 .ty = child_pointer.toIntern(), 8689 .addr = .{ .opt_payload = ptr_val.toIntern() }, 8690 } }))); 8691 } 8692 } 8693 8694 try sema.requireRuntimeBlock(block, src, null); 8695 if (safety_check and block.wantSafety()) { 8696 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); 8697 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 8698 } 8699 8700 if (initializing) { 8701 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); 8702 try sema.checkKnownAllocPtr(optional_ptr, opt_payload_ptr); 8703 return opt_payload_ptr; 8704 } else { 8705 return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr); 8706 } 8707 } 8708 8709 /// Value in, value out. 8710 fn zirOptionalPayload( 8711 sema: *Sema, 8712 block: *Block, 8713 inst: Zir.Inst.Index, 8714 safety_check: bool, 8715 ) CompileError!Air.Inst.Ref { 8716 const tracy = trace(@src()); 8717 defer tracy.end(); 8718 8719 const mod = sema.mod; 8720 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8721 const src = inst_data.src(); 8722 const operand = try sema.resolveInst(inst_data.operand); 8723 const operand_ty = sema.typeOf(operand); 8724 const result_ty = switch (operand_ty.zigTypeTag(mod)) { 8725 .Optional => operand_ty.optionalChild(mod), 8726 .Pointer => t: { 8727 if (operand_ty.ptrSize(mod) != .C) { 8728 return sema.failWithExpectedOptionalType(block, src, operand_ty); 8729 } 8730 // TODO https://github.com/ziglang/zig/issues/6597 8731 if (true) break :t operand_ty; 8732 const ptr_info = operand_ty.ptrInfo(mod); 8733 break :t try sema.ptrType(.{ 8734 .child = ptr_info.child, 8735 .flags = .{ 8736 .alignment = ptr_info.flags.alignment, 8737 .is_const = ptr_info.flags.is_const, 8738 .is_volatile = ptr_info.flags.is_volatile, 8739 .is_allowzero = ptr_info.flags.is_allowzero, 8740 .address_space = ptr_info.flags.address_space, 8741 }, 8742 }); 8743 }, 8744 else => return sema.failWithExpectedOptionalType(block, src, operand_ty), 8745 }; 8746 8747 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 8748 return if (val.optionalValue(mod)) |payload| 8749 Air.internedToRef(payload.toIntern()) 8750 else 8751 sema.fail(block, src, "unable to unwrap null", .{}); 8752 } 8753 8754 try sema.requireRuntimeBlock(block, src, null); 8755 if (safety_check and block.wantSafety()) { 8756 const is_non_null = try block.addUnOp(.is_non_null, operand); 8757 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 8758 } 8759 return block.addTyOp(.optional_payload, result_ty, operand); 8760 } 8761 8762 /// Value in, value out 8763 fn zirErrUnionPayload( 8764 sema: *Sema, 8765 block: *Block, 8766 inst: Zir.Inst.Index, 8767 ) CompileError!Air.Inst.Ref { 8768 const tracy = trace(@src()); 8769 defer tracy.end(); 8770 8771 const mod = sema.mod; 8772 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8773 const src = inst_data.src(); 8774 const operand = try sema.resolveInst(inst_data.operand); 8775 const operand_src = src; 8776 const err_union_ty = sema.typeOf(operand); 8777 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 8778 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ 8779 err_union_ty.fmt(mod), 8780 }); 8781 } 8782 return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); 8783 } 8784 8785 fn analyzeErrUnionPayload( 8786 sema: *Sema, 8787 block: *Block, 8788 src: LazySrcLoc, 8789 err_union_ty: Type, 8790 operand: Air.Inst.Ref, 8791 operand_src: LazySrcLoc, 8792 safety_check: bool, 8793 ) CompileError!Air.Inst.Ref { 8794 const mod = sema.mod; 8795 const payload_ty = err_union_ty.errorUnionPayload(mod); 8796 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 8797 if (val.getErrorName(mod).unwrap()) |name| { 8798 return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)}); 8799 } 8800 return Air.internedToRef(mod.intern_pool.indexToKey(val.toIntern()).error_union.val.payload); 8801 } 8802 8803 try sema.requireRuntimeBlock(block, src, null); 8804 8805 // If the error set has no fields then no safety check is needed. 8806 if (safety_check and block.wantSafety() and 8807 !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) 8808 { 8809 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); 8810 } 8811 8812 return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); 8813 } 8814 8815 /// Pointer in, pointer out. 8816 fn zirErrUnionPayloadPtr( 8817 sema: *Sema, 8818 block: *Block, 8819 inst: Zir.Inst.Index, 8820 ) CompileError!Air.Inst.Ref { 8821 const tracy = trace(@src()); 8822 defer tracy.end(); 8823 8824 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8825 const operand = try sema.resolveInst(inst_data.operand); 8826 const src = inst_data.src(); 8827 8828 return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); 8829 } 8830 8831 fn analyzeErrUnionPayloadPtr( 8832 sema: *Sema, 8833 block: *Block, 8834 src: LazySrcLoc, 8835 operand: Air.Inst.Ref, 8836 safety_check: bool, 8837 initializing: bool, 8838 ) CompileError!Air.Inst.Ref { 8839 const mod = sema.mod; 8840 const operand_ty = sema.typeOf(operand); 8841 assert(operand_ty.zigTypeTag(mod) == .Pointer); 8842 8843 if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) { 8844 return sema.fail(block, src, "expected error union type, found '{}'", .{ 8845 operand_ty.childType(mod).fmt(mod), 8846 }); 8847 } 8848 8849 const err_union_ty = operand_ty.childType(mod); 8850 const payload_ty = err_union_ty.errorUnionPayload(mod); 8851 const operand_pointer_ty = try sema.ptrType(.{ 8852 .child = payload_ty.toIntern(), 8853 .flags = .{ 8854 .is_const = operand_ty.isConstPtr(mod), 8855 .address_space = operand_ty.ptrAddressSpace(mod), 8856 }, 8857 }); 8858 8859 if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| { 8860 if (initializing) { 8861 if (!ptr_val.isComptimeMutablePtr(mod)) { 8862 // If the pointer resulting from this function was stored at comptime, 8863 // the error union error code would be set that way. But in this case, 8864 // we need to emit a runtime instruction to do it. 8865 try sema.requireRuntimeBlock(block, src, null); 8866 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); 8867 try sema.checkKnownAllocPtr(operand, eu_payload_ptr); 8868 } 8869 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 8870 .ty = operand_pointer_ty.toIntern(), 8871 .addr = .{ .eu_payload = ptr_val.toIntern() }, 8872 } }))); 8873 } 8874 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| { 8875 if (val.getErrorName(mod).unwrap()) |name| { 8876 return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)}); 8877 } 8878 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 8879 .ty = operand_pointer_ty.toIntern(), 8880 .addr = .{ .eu_payload = ptr_val.toIntern() }, 8881 } }))); 8882 } 8883 } 8884 8885 try sema.requireRuntimeBlock(block, src, null); 8886 8887 // If the error set has no fields then no safety check is needed. 8888 if (safety_check and block.wantSafety() and 8889 !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) 8890 { 8891 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); 8892 } 8893 8894 if (initializing) { 8895 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); 8896 try sema.checkKnownAllocPtr(operand, eu_payload_ptr); 8897 return eu_payload_ptr; 8898 } else { 8899 return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand); 8900 } 8901 } 8902 8903 /// Value in, value out 8904 fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8905 const tracy = trace(@src()); 8906 defer tracy.end(); 8907 8908 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8909 const src = inst_data.src(); 8910 const operand = try sema.resolveInst(inst_data.operand); 8911 return sema.analyzeErrUnionCode(block, src, operand); 8912 } 8913 8914 fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { 8915 const mod = sema.mod; 8916 const operand_ty = sema.typeOf(operand); 8917 if (operand_ty.zigTypeTag(mod) != .ErrorUnion) { 8918 return sema.fail(block, src, "expected error union type, found '{}'", .{ 8919 operand_ty.fmt(mod), 8920 }); 8921 } 8922 8923 const result_ty = operand_ty.errorUnionSet(mod); 8924 8925 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 8926 return Air.internedToRef((try mod.intern(.{ .err = .{ 8927 .ty = result_ty.toIntern(), 8928 .name = mod.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, 8929 } }))); 8930 } 8931 8932 try sema.requireRuntimeBlock(block, src, null); 8933 return block.addTyOp(.unwrap_errunion_err, result_ty, operand); 8934 } 8935 8936 /// Pointer in, value out 8937 fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8938 const tracy = trace(@src()); 8939 defer tracy.end(); 8940 8941 const mod = sema.mod; 8942 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 8943 const src = inst_data.src(); 8944 const operand = try sema.resolveInst(inst_data.operand); 8945 const operand_ty = sema.typeOf(operand); 8946 assert(operand_ty.zigTypeTag(mod) == .Pointer); 8947 8948 if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) { 8949 return sema.fail(block, src, "expected error union type, found '{}'", .{ 8950 operand_ty.childType(mod).fmt(mod), 8951 }); 8952 } 8953 8954 const result_ty = operand_ty.childType(mod).errorUnionSet(mod); 8955 8956 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { 8957 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { 8958 assert(val.getErrorName(mod) != .none); 8959 return Air.internedToRef(val.toIntern()); 8960 } 8961 } 8962 8963 try sema.requireRuntimeBlock(block, src, null); 8964 return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); 8965 } 8966 8967 fn zirFunc( 8968 sema: *Sema, 8969 block: *Block, 8970 inst: Zir.Inst.Index, 8971 inferred_error_set: bool, 8972 ) CompileError!Air.Inst.Ref { 8973 const mod = sema.mod; 8974 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 8975 const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); 8976 const target = sema.mod.getTarget(); 8977 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node }; 8978 8979 var extra_index = extra.end; 8980 8981 const ret_ty: Type = switch (extra.data.ret_body_len) { 8982 0 => Type.void, 8983 1 => blk: { 8984 const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 8985 extra_index += 1; 8986 if (sema.resolveType(block, ret_ty_src, ret_ty_ref)) |ret_ty| { 8987 break :blk ret_ty; 8988 } else |err| switch (err) { 8989 error.GenericPoison => { 8990 break :blk Type.generic_poison; 8991 }, 8992 else => |e| return e, 8993 } 8994 }, 8995 else => blk: { 8996 const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_body_len); 8997 extra_index += ret_ty_body.len; 8998 8999 const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type, .{ 9000 .needed_comptime_reason = "return type must be comptime-known", 9001 }); 9002 break :blk ret_ty_val.toType(); 9003 }, 9004 }; 9005 9006 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 9007 const has_body = extra.data.body_len != 0; 9008 if (has_body) { 9009 extra_index += extra.data.body_len; 9010 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 9011 } 9012 9013 // If this instruction has a body it means it's the type of the `owner_decl` 9014 // otherwise it's a function type without a `callconv` attribute and should 9015 // never be `.C`. 9016 const cc: std.builtin.CallingConvention = if (has_body and mod.declPtr(block.src_decl).is_exported) 9017 .C 9018 else 9019 .Unspecified; 9020 9021 return sema.funcCommon( 9022 block, 9023 inst_data.src_node, 9024 inst, 9025 .none, 9026 target_util.defaultAddressSpace(target, .function), 9027 .default, 9028 cc, 9029 ret_ty, 9030 false, 9031 inferred_error_set, 9032 false, 9033 has_body, 9034 src_locs, 9035 null, 9036 0, 9037 false, 9038 ); 9039 } 9040 9041 fn resolveGenericBody( 9042 sema: *Sema, 9043 block: *Block, 9044 src: LazySrcLoc, 9045 body: []const Zir.Inst.Index, 9046 func_inst: Zir.Inst.Index, 9047 dest_ty: Type, 9048 reason: NeededComptimeReason, 9049 ) !Value { 9050 assert(body.len != 0); 9051 9052 const err = err: { 9053 // Make sure any nested param instructions don't clobber our work. 9054 const prev_params = block.params; 9055 const prev_no_partial_func_type = sema.no_partial_func_ty; 9056 const prev_generic_owner = sema.generic_owner; 9057 const prev_generic_call_src = sema.generic_call_src; 9058 const prev_generic_call_decl = sema.generic_call_decl; 9059 block.params = .{}; 9060 sema.no_partial_func_ty = true; 9061 sema.generic_owner = .none; 9062 sema.generic_call_src = .unneeded; 9063 sema.generic_call_decl = .none; 9064 defer { 9065 block.params = prev_params; 9066 sema.no_partial_func_ty = prev_no_partial_func_type; 9067 sema.generic_owner = prev_generic_owner; 9068 sema.generic_call_src = prev_generic_call_src; 9069 sema.generic_call_decl = prev_generic_call_decl; 9070 } 9071 9072 const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err; 9073 const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err; 9074 const val = sema.resolveConstDefinedValue(block, src, result, reason) catch |err| break :err err; 9075 return val; 9076 }; 9077 switch (err) { 9078 error.GenericPoison => { 9079 if (dest_ty.toIntern() == .type_type) { 9080 return Value.generic_poison_type; 9081 } else { 9082 return Value.generic_poison; 9083 } 9084 }, 9085 else => |e| return e, 9086 } 9087 } 9088 9089 /// Given a library name, examines if the library name should end up in 9090 /// `link.File.Options.system_libs` table (for example, libc is always 9091 /// specified via dedicated flag `link_libc` instead), 9092 /// and puts it there if it doesn't exist. 9093 /// It also dupes the library name which can then be saved as part of the 9094 /// respective `Decl` (either `ExternFn` or `Var`). 9095 /// The liveness of the duped library name is tied to liveness of `Module`. 9096 /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). 9097 fn handleExternLibName( 9098 sema: *Sema, 9099 block: *Block, 9100 src_loc: LazySrcLoc, 9101 lib_name: []const u8, 9102 ) CompileError!void { 9103 blk: { 9104 const mod = sema.mod; 9105 const comp = mod.comp; 9106 const target = mod.getTarget(); 9107 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); 9108 if (target.is_libc_lib_name(lib_name)) { 9109 if (!comp.config.link_libc) { 9110 return sema.fail( 9111 block, 9112 src_loc, 9113 "dependency on libc must be explicitly specified in the build command", 9114 .{}, 9115 ); 9116 } 9117 break :blk; 9118 } 9119 if (target.is_libcpp_lib_name(lib_name)) { 9120 if (!comp.config.link_libcpp) return sema.fail( 9121 block, 9122 src_loc, 9123 "dependency on libc++ must be explicitly specified in the build command", 9124 .{}, 9125 ); 9126 break :blk; 9127 } 9128 if (mem.eql(u8, lib_name, "unwind")) { 9129 if (!comp.config.link_libunwind) return sema.fail( 9130 block, 9131 src_loc, 9132 "dependency on libunwind must be explicitly specified in the build command", 9133 .{}, 9134 ); 9135 break :blk; 9136 } 9137 if (!target.isWasm() and !block.ownerModule().pic) { 9138 return sema.fail( 9139 block, 9140 src_loc, 9141 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.", 9142 .{ lib_name, lib_name }, 9143 ); 9144 } 9145 comp.addLinkLib(lib_name) catch |err| { 9146 return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ 9147 lib_name, @errorName(err), 9148 }); 9149 }; 9150 } 9151 } 9152 9153 /// These are calling conventions that are confirmed to work with variadic functions. 9154 /// Any calling conventions not included here are either not yet verified to work with variadic 9155 /// functions or there are no more other calling conventions that support variadic functions. 9156 const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention{ 9157 .C, 9158 }; 9159 fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention) bool { 9160 return for (calling_conventions_supporting_var_args) |supported_cc| { 9161 if (cc == supported_cc) return true; 9162 } else false; 9163 } 9164 fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention) CompileError!void { 9165 const CallingConventionsSupportingVarArgsList = struct { 9166 pub fn format(_: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { 9167 _ = fmt; 9168 _ = options; 9169 for (calling_conventions_supporting_var_args, 0..) |cc_inner, i| { 9170 if (i != 0) 9171 try writer.writeAll(", "); 9172 try writer.print("'.{s}'", .{@tagName(cc_inner)}); 9173 } 9174 } 9175 }; 9176 9177 if (!callConvSupportsVarArgs(cc)) { 9178 const msg = msg: { 9179 const msg = try sema.errMsg(block, src, "variadic function does not support '.{s}' calling convention", .{@tagName(cc)}); 9180 errdefer msg.destroy(sema.gpa); 9181 try sema.errNote(block, src, msg, "supported calling conventions: {}", .{CallingConventionsSupportingVarArgsList{}}); 9182 break :msg msg; 9183 }; 9184 return sema.failWithOwnedErrorMsg(block, msg); 9185 } 9186 } 9187 9188 const Section = union(enum) { 9189 generic, 9190 default, 9191 explicit: InternPool.NullTerminatedString, 9192 }; 9193 9194 fn funcCommon( 9195 sema: *Sema, 9196 block: *Block, 9197 src_node_offset: i32, 9198 func_inst: Zir.Inst.Index, 9199 /// null means generic poison 9200 alignment: ?Alignment, 9201 /// null means generic poison 9202 address_space: ?std.builtin.AddressSpace, 9203 section: Section, 9204 /// null means generic poison 9205 cc: ?std.builtin.CallingConvention, 9206 /// this might be Type.generic_poison 9207 bare_return_type: Type, 9208 var_args: bool, 9209 inferred_error_set: bool, 9210 is_extern: bool, 9211 has_body: bool, 9212 src_locs: Zir.Inst.Func.SrcLocs, 9213 opt_lib_name: ?[]const u8, 9214 noalias_bits: u32, 9215 is_noinline: bool, 9216 ) CompileError!Air.Inst.Ref { 9217 const mod = sema.mod; 9218 const gpa = sema.gpa; 9219 const target = mod.getTarget(); 9220 const ip = &mod.intern_pool; 9221 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; 9222 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; 9223 const func_src = LazySrcLoc.nodeOffset(src_node_offset); 9224 9225 var is_generic = bare_return_type.isGenericPoison() or 9226 alignment == null or 9227 address_space == null or 9228 section == .generic or 9229 cc == null; 9230 9231 if (var_args) { 9232 if (is_generic) { 9233 return sema.fail(block, func_src, "generic function cannot be variadic", .{}); 9234 } 9235 try sema.checkCallConvSupportsVarArgs(block, cc_src, cc.?); 9236 } 9237 9238 const is_source_decl = sema.generic_owner == .none; 9239 9240 // In the case of generic calling convention, or generic alignment, we use 9241 // default values which are only meaningful for the generic function, *not* 9242 // the instantiation, which can depend on comptime parameters. 9243 // Related proposal: https://github.com/ziglang/zig/issues/11834 9244 const cc_resolved = cc orelse .Unspecified; 9245 var comptime_bits: u32 = 0; 9246 for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| { 9247 const param_ty = Type.fromInterned(param_ty_ip); 9248 const is_noalias = blk: { 9249 const index = std.math.cast(u5, i) orelse break :blk false; 9250 break :blk @as(u1, @truncate(noalias_bits >> index)) != 0; 9251 }; 9252 const param_src: LazySrcLoc = .{ .fn_proto_param = .{ 9253 .decl = block.src_decl, 9254 .fn_proto_node_offset = src_node_offset, 9255 .param_index = @intCast(i), 9256 } }; 9257 const requires_comptime = try sema.typeRequiresComptime(param_ty); 9258 if (param_is_comptime or requires_comptime) { 9259 comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error 9260 } 9261 const this_generic = param_ty.isGenericPoison(); 9262 is_generic = is_generic or this_generic; 9263 if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved)) { 9264 return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc_resolved)}); 9265 } 9266 if (this_generic and !sema.no_partial_func_ty and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved)) { 9267 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc_resolved)}); 9268 } 9269 if (!param_ty.isValidParamType(mod)) { 9270 const opaque_str = if (param_ty.zigTypeTag(mod) == .Opaque) "opaque " else ""; 9271 const msg = msg: { 9272 const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{ 9273 opaque_str, param_ty.fmt(mod), 9274 }); 9275 errdefer msg.destroy(sema.gpa); 9276 9277 try sema.addDeclaredHereNote(msg, param_ty); 9278 break :msg msg; 9279 }; 9280 return sema.failWithOwnedErrorMsg(block, msg); 9281 } 9282 if (!this_generic and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and !try sema.validateExternType(param_ty, .param_ty)) { 9283 const msg = msg: { 9284 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ 9285 param_ty.fmt(mod), @tagName(cc_resolved), 9286 }); 9287 errdefer msg.destroy(sema.gpa); 9288 9289 const src_decl = mod.declPtr(block.src_decl); 9290 try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl, mod), param_ty, .param_ty); 9291 9292 try sema.addDeclaredHereNote(msg, param_ty); 9293 break :msg msg; 9294 }; 9295 return sema.failWithOwnedErrorMsg(block, msg); 9296 } 9297 if (is_source_decl and requires_comptime and !param_is_comptime and has_body) { 9298 const msg = msg: { 9299 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' must be declared comptime", .{ 9300 param_ty.fmt(mod), 9301 }); 9302 errdefer msg.destroy(sema.gpa); 9303 9304 const src_decl = mod.declPtr(block.src_decl); 9305 try sema.explainWhyTypeIsComptime(msg, param_src.toSrcLoc(src_decl, mod), param_ty); 9306 9307 try sema.addDeclaredHereNote(msg, param_ty); 9308 break :msg msg; 9309 }; 9310 return sema.failWithOwnedErrorMsg(block, msg); 9311 } 9312 if (is_source_decl and !this_generic and is_noalias and 9313 !(param_ty.zigTypeTag(mod) == .Pointer or param_ty.isPtrLikeOptional(mod))) 9314 { 9315 return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); 9316 } 9317 } 9318 9319 var ret_ty_requires_comptime = false; 9320 const ret_poison = if (sema.typeRequiresComptime(bare_return_type)) |ret_comptime| rp: { 9321 ret_ty_requires_comptime = ret_comptime; 9322 break :rp bare_return_type.isGenericPoison(); 9323 } else |err| switch (err) { 9324 error.GenericPoison => rp: { 9325 is_generic = true; 9326 break :rp true; 9327 }, 9328 else => |e| return e, 9329 }; 9330 const final_is_generic = is_generic or comptime_bits != 0 or ret_ty_requires_comptime; 9331 9332 const param_types = block.params.items(.ty); 9333 9334 if (!is_source_decl) { 9335 assert(has_body); 9336 assert(!is_generic); 9337 assert(comptime_bits == 0); 9338 assert(cc != null); 9339 assert(section != .generic); 9340 assert(address_space != null); 9341 assert(!var_args); 9342 if (inferred_error_set) { 9343 try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); 9344 } 9345 const func_index = try ip.getFuncInstance(gpa, .{ 9346 .param_types = param_types, 9347 .noalias_bits = noalias_bits, 9348 .bare_return_type = bare_return_type.toIntern(), 9349 .cc = cc_resolved, 9350 .alignment = alignment.?, 9351 .section = switch (section) { 9352 .generic => unreachable, 9353 .default => .none, 9354 .explicit => |x| x.toOptional(), 9355 }, 9356 .is_noinline = is_noinline, 9357 .inferred_error_set = inferred_error_set, 9358 .generic_owner = sema.generic_owner, 9359 .comptime_args = sema.comptime_args, 9360 .generation = mod.generation, 9361 }); 9362 return finishFunc( 9363 sema, 9364 block, 9365 func_index, 9366 .none, 9367 ret_poison, 9368 bare_return_type, 9369 ret_ty_src, 9370 cc_resolved, 9371 is_source_decl, 9372 ret_ty_requires_comptime, 9373 func_inst, 9374 cc_src, 9375 is_noinline, 9376 is_generic, 9377 final_is_generic, 9378 ); 9379 } 9380 9381 // extern_func and func_decl functions take ownership of `sema.owner_decl`. 9382 sema.owner_decl.@"linksection" = switch (section) { 9383 .generic => .none, 9384 .default => .none, 9385 .explicit => |section_name| section_name.toOptional(), 9386 }; 9387 sema.owner_decl.alignment = alignment orelse .none; 9388 sema.owner_decl.@"addrspace" = address_space orelse .generic; 9389 9390 if (inferred_error_set) { 9391 assert(!is_extern); 9392 assert(has_body); 9393 if (!ret_poison) 9394 try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); 9395 const func_index = try ip.getFuncDeclIes(gpa, .{ 9396 .owner_decl = sema.owner_decl_index, 9397 9398 .param_types = param_types, 9399 .noalias_bits = noalias_bits, 9400 .comptime_bits = comptime_bits, 9401 .bare_return_type = bare_return_type.toIntern(), 9402 .cc = cc, 9403 .alignment = alignment, 9404 .section_is_generic = section == .generic, 9405 .addrspace_is_generic = address_space == null, 9406 .is_var_args = var_args, 9407 .is_generic = final_is_generic, 9408 .is_noinline = is_noinline, 9409 9410 .zir_body_inst = func_inst, 9411 .lbrace_line = src_locs.lbrace_line, 9412 .rbrace_line = src_locs.rbrace_line, 9413 .lbrace_column = @as(u16, @truncate(src_locs.columns)), 9414 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), 9415 }); 9416 return finishFunc( 9417 sema, 9418 block, 9419 func_index, 9420 .none, 9421 ret_poison, 9422 bare_return_type, 9423 ret_ty_src, 9424 cc_resolved, 9425 is_source_decl, 9426 ret_ty_requires_comptime, 9427 func_inst, 9428 cc_src, 9429 is_noinline, 9430 is_generic, 9431 final_is_generic, 9432 ); 9433 } 9434 9435 const func_ty = try ip.getFuncType(gpa, .{ 9436 .param_types = param_types, 9437 .noalias_bits = noalias_bits, 9438 .comptime_bits = comptime_bits, 9439 .return_type = bare_return_type.toIntern(), 9440 .cc = cc, 9441 .alignment = alignment, 9442 .section_is_generic = section == .generic, 9443 .addrspace_is_generic = address_space == null, 9444 .is_var_args = var_args, 9445 .is_generic = final_is_generic, 9446 .is_noinline = is_noinline, 9447 }); 9448 9449 if (is_extern) { 9450 assert(comptime_bits == 0); 9451 assert(cc != null); 9452 assert(section != .generic); 9453 assert(address_space != null); 9454 assert(!is_generic); 9455 if (opt_lib_name) |lib_name| try sema.handleExternLibName(block, .{ 9456 .node_offset_lib_name = src_node_offset, 9457 }, lib_name); 9458 const func_index = try ip.getExternFunc(gpa, .{ 9459 .ty = func_ty, 9460 .decl = sema.owner_decl_index, 9461 .lib_name = try mod.intern_pool.getOrPutStringOpt(gpa, opt_lib_name), 9462 }); 9463 return finishFunc( 9464 sema, 9465 block, 9466 func_index, 9467 func_ty, 9468 ret_poison, 9469 bare_return_type, 9470 ret_ty_src, 9471 cc_resolved, 9472 is_source_decl, 9473 ret_ty_requires_comptime, 9474 func_inst, 9475 cc_src, 9476 is_noinline, 9477 is_generic, 9478 final_is_generic, 9479 ); 9480 } 9481 9482 if (has_body) { 9483 const func_index = try ip.getFuncDecl(gpa, .{ 9484 .owner_decl = sema.owner_decl_index, 9485 .ty = func_ty, 9486 .cc = cc, 9487 .is_noinline = is_noinline, 9488 .zir_body_inst = func_inst, 9489 .lbrace_line = src_locs.lbrace_line, 9490 .rbrace_line = src_locs.rbrace_line, 9491 .lbrace_column = @as(u16, @truncate(src_locs.columns)), 9492 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), 9493 }); 9494 return finishFunc( 9495 sema, 9496 block, 9497 func_index, 9498 func_ty, 9499 ret_poison, 9500 bare_return_type, 9501 ret_ty_src, 9502 cc_resolved, 9503 is_source_decl, 9504 ret_ty_requires_comptime, 9505 func_inst, 9506 cc_src, 9507 is_noinline, 9508 is_generic, 9509 final_is_generic, 9510 ); 9511 } 9512 9513 return finishFunc( 9514 sema, 9515 block, 9516 .none, 9517 func_ty, 9518 ret_poison, 9519 bare_return_type, 9520 ret_ty_src, 9521 cc_resolved, 9522 is_source_decl, 9523 ret_ty_requires_comptime, 9524 func_inst, 9525 cc_src, 9526 is_noinline, 9527 is_generic, 9528 final_is_generic, 9529 ); 9530 } 9531 9532 fn finishFunc( 9533 sema: *Sema, 9534 block: *Block, 9535 opt_func_index: InternPool.Index, 9536 func_ty: InternPool.Index, 9537 ret_poison: bool, 9538 bare_return_type: Type, 9539 ret_ty_src: LazySrcLoc, 9540 cc_resolved: std.builtin.CallingConvention, 9541 is_source_decl: bool, 9542 ret_ty_requires_comptime: bool, 9543 func_inst: Zir.Inst.Index, 9544 cc_src: LazySrcLoc, 9545 is_noinline: bool, 9546 is_generic: bool, 9547 final_is_generic: bool, 9548 ) CompileError!Air.Inst.Ref { 9549 const mod = sema.mod; 9550 const ip = &mod.intern_pool; 9551 const gpa = sema.gpa; 9552 const target = mod.getTarget(); 9553 9554 const return_type: Type = if (opt_func_index == .none or ret_poison) 9555 bare_return_type 9556 else 9557 Type.fromInterned(ip.funcTypeReturnType(ip.typeOf(opt_func_index))); 9558 9559 if (!return_type.isValidReturnType(mod)) { 9560 const opaque_str = if (return_type.zigTypeTag(mod) == .Opaque) "opaque " else ""; 9561 const msg = msg: { 9562 const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ 9563 opaque_str, return_type.fmt(mod), 9564 }); 9565 errdefer msg.destroy(gpa); 9566 9567 try sema.addDeclaredHereNote(msg, return_type); 9568 break :msg msg; 9569 }; 9570 return sema.failWithOwnedErrorMsg(block, msg); 9571 } 9572 if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and 9573 !try sema.validateExternType(return_type, .ret_ty)) 9574 { 9575 const msg = msg: { 9576 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ 9577 return_type.fmt(mod), @tagName(cc_resolved), 9578 }); 9579 errdefer msg.destroy(gpa); 9580 9581 const src_decl = mod.declPtr(block.src_decl); 9582 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl, mod), return_type, .ret_ty); 9583 9584 try sema.addDeclaredHereNote(msg, return_type); 9585 break :msg msg; 9586 }; 9587 return sema.failWithOwnedErrorMsg(block, msg); 9588 } 9589 9590 // If the return type is comptime-only but not dependent on parameters then 9591 // all parameter types also need to be comptime. 9592 if (is_source_decl and opt_func_index != .none and ret_ty_requires_comptime) comptime_check: { 9593 for (block.params.items(.is_comptime)) |is_comptime| { 9594 if (!is_comptime) break; 9595 } else break :comptime_check; 9596 9597 const msg = try sema.errMsg( 9598 block, 9599 ret_ty_src, 9600 "function with comptime-only return type '{}' requires all parameters to be comptime", 9601 .{return_type.fmt(mod)}, 9602 ); 9603 try sema.explainWhyTypeIsComptime(msg, ret_ty_src.toSrcLoc(sema.owner_decl, mod), return_type); 9604 9605 const tags = sema.code.instructions.items(.tag); 9606 const data = sema.code.instructions.items(.data); 9607 const param_body = sema.code.getParamBody(func_inst); 9608 for ( 9609 block.params.items(.is_comptime), 9610 block.params.items(.name), 9611 param_body[0..block.params.len], 9612 ) |is_comptime, name_nts, param_index| { 9613 if (!is_comptime) { 9614 const param_src = switch (tags[@intFromEnum(param_index)]) { 9615 .param => data[@intFromEnum(param_index)].pl_tok.src(), 9616 .param_anytype => data[@intFromEnum(param_index)].str_tok.src(), 9617 else => unreachable, 9618 }; 9619 const name = sema.code.nullTerminatedString2(name_nts); 9620 if (name.len != 0) { 9621 try sema.errNote(block, param_src, msg, "param '{s}' is required to be comptime", .{name}); 9622 } else { 9623 try sema.errNote(block, param_src, msg, "param is required to be comptime", .{}); 9624 } 9625 } 9626 } 9627 return sema.failWithOwnedErrorMsg(block, msg); 9628 } 9629 9630 const arch = target.cpu.arch; 9631 if (@as(?[]const u8, switch (cc_resolved) { 9632 .Unspecified, .C, .Naked, .Async, .Inline => null, 9633 .Interrupt => switch (arch) { 9634 .x86, .x86_64, .avr, .msp430 => null, 9635 else => "x86, x86_64, AVR, and MSP430", 9636 }, 9637 .Signal => switch (arch) { 9638 .avr => null, 9639 else => "AVR", 9640 }, 9641 .Stdcall, .Fastcall, .Thiscall => switch (arch) { 9642 .x86 => null, 9643 else => "x86", 9644 }, 9645 .Vectorcall => switch (arch) { 9646 .x86, .aarch64, .aarch64_be, .aarch64_32 => null, 9647 else => "x86 and AArch64", 9648 }, 9649 .APCS, .AAPCS, .AAPCSVFP => switch (arch) { 9650 .arm, .armeb, .aarch64, .aarch64_be, .aarch64_32, .thumb, .thumbeb => null, 9651 else => "ARM", 9652 }, 9653 .SysV, .Win64 => switch (arch) { 9654 .x86_64 => null, 9655 else => "x86_64", 9656 }, 9657 .Kernel => switch (arch) { 9658 .nvptx, .nvptx64, .amdgcn, .spirv32, .spirv64 => null, 9659 else => "nvptx, amdgcn and SPIR-V", 9660 }, 9661 })) |allowed_platform| { 9662 return sema.fail(block, cc_src, "callconv '{s}' is only available on {s}, not {s}", .{ 9663 @tagName(cc_resolved), 9664 allowed_platform, 9665 @tagName(arch), 9666 }); 9667 } 9668 9669 if (cc_resolved == .Inline and is_noinline) { 9670 return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{}); 9671 } 9672 if (is_generic and sema.no_partial_func_ty) return error.GenericPoison; 9673 9674 if (!final_is_generic and sema.wantErrorReturnTracing(return_type)) { 9675 // Make sure that StackTrace's fields are resolved so that the backend can 9676 // lower this fn type. 9677 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 9678 try sema.resolveTypeFields(unresolved_stack_trace_ty); 9679 } 9680 9681 return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty); 9682 } 9683 9684 fn zirParam( 9685 sema: *Sema, 9686 block: *Block, 9687 inst: Zir.Inst.Index, 9688 comptime_syntax: bool, 9689 ) CompileError!void { 9690 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; 9691 const src = inst_data.src(); 9692 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); 9693 const param_name: Zir.NullTerminatedString = @enumFromInt(extra.data.name); 9694 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 9695 9696 const param_ty = param_ty: { 9697 const err = err: { 9698 // Make sure any nested param instructions don't clobber our work. 9699 const prev_params = block.params; 9700 const prev_no_partial_func_type = sema.no_partial_func_ty; 9701 const prev_generic_owner = sema.generic_owner; 9702 const prev_generic_call_src = sema.generic_call_src; 9703 const prev_generic_call_decl = sema.generic_call_decl; 9704 block.params = .{}; 9705 sema.no_partial_func_ty = true; 9706 sema.generic_owner = .none; 9707 sema.generic_call_src = .unneeded; 9708 sema.generic_call_decl = .none; 9709 defer { 9710 block.params = prev_params; 9711 sema.no_partial_func_ty = prev_no_partial_func_type; 9712 sema.generic_owner = prev_generic_owner; 9713 sema.generic_call_src = prev_generic_call_src; 9714 sema.generic_call_decl = prev_generic_call_decl; 9715 } 9716 9717 if (sema.resolveBody(block, body, inst)) |param_ty_inst| { 9718 if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| { 9719 break :param_ty param_ty; 9720 } else |err| break :err err; 9721 } else |err| break :err err; 9722 }; 9723 switch (err) { 9724 error.GenericPoison => { 9725 // The type is not available until the generic instantiation. 9726 // We result the param instruction with a poison value and 9727 // insert an anytype parameter. 9728 try block.params.append(sema.arena, .{ 9729 .ty = .generic_poison_type, 9730 .is_comptime = comptime_syntax, 9731 .name = param_name, 9732 }); 9733 sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison); 9734 return; 9735 }, 9736 else => |e| return e, 9737 } 9738 }; 9739 9740 const is_comptime = sema.typeRequiresComptime(param_ty) catch |err| switch (err) { 9741 error.GenericPoison => { 9742 // The type is not available until the generic instantiation. 9743 // We result the param instruction with a poison value and 9744 // insert an anytype parameter. 9745 try block.params.append(sema.arena, .{ 9746 .ty = .generic_poison_type, 9747 .is_comptime = comptime_syntax, 9748 .name = param_name, 9749 }); 9750 sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison); 9751 return; 9752 }, 9753 else => |e| return e, 9754 } or comptime_syntax; 9755 9756 try block.params.append(sema.arena, .{ 9757 .ty = param_ty.toIntern(), 9758 .is_comptime = comptime_syntax, 9759 .name = param_name, 9760 }); 9761 9762 if (is_comptime) { 9763 // If this is a comptime parameter we can add a constant generic_poison 9764 // since this is also a generic parameter. 9765 sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison); 9766 } else { 9767 // Otherwise we need a dummy runtime instruction. 9768 const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 9769 try sema.air_instructions.append(sema.gpa, .{ 9770 .tag = .alloc, 9771 .data = .{ .ty = param_ty }, 9772 }); 9773 sema.inst_map.putAssumeCapacityNoClobber(inst, result_index.toRef()); 9774 } 9775 } 9776 9777 fn zirParamAnytype( 9778 sema: *Sema, 9779 block: *Block, 9780 inst: Zir.Inst.Index, 9781 comptime_syntax: bool, 9782 ) CompileError!void { 9783 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 9784 const param_name: Zir.NullTerminatedString = @enumFromInt(inst_data.start); 9785 9786 // We are evaluating a generic function without any comptime args provided. 9787 9788 try block.params.append(sema.arena, .{ 9789 .ty = .generic_poison_type, 9790 .is_comptime = comptime_syntax, 9791 .name = param_name, 9792 }); 9793 sema.inst_map.putAssumeCapacity(inst, .generic_poison); 9794 } 9795 9796 fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9797 const tracy = trace(@src()); 9798 defer tracy.end(); 9799 9800 const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin; 9801 return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false); 9802 } 9803 9804 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9805 const tracy = trace(@src()); 9806 defer tracy.end(); 9807 9808 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9809 const src = inst_data.src(); 9810 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; 9811 sema.src = src; 9812 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false); 9813 } 9814 9815 fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9816 const tracy = trace(@src()); 9817 defer tracy.end(); 9818 9819 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9820 const src = inst_data.src(); 9821 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; 9822 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true); 9823 } 9824 9825 fn analyzeAs( 9826 sema: *Sema, 9827 block: *Block, 9828 src: LazySrcLoc, 9829 zir_dest_type: Zir.Inst.Ref, 9830 zir_operand: Zir.Inst.Ref, 9831 no_cast_to_comptime_int: bool, 9832 ) CompileError!Air.Inst.Ref { 9833 const mod = sema.mod; 9834 const operand = try sema.resolveInst(zir_operand); 9835 if (zir_dest_type == .var_args_param_type) return operand; 9836 const operand_air_inst = sema.resolveInst(zir_dest_type) catch |err| switch (err) { 9837 error.GenericPoison => return operand, 9838 else => |e| return e, 9839 }; 9840 if (operand_air_inst == .var_args_param_type) return operand; 9841 const dest_ty = sema.analyzeAsType(block, src, operand_air_inst) catch |err| switch (err) { 9842 error.GenericPoison => return operand, 9843 else => |e| return e, 9844 }; 9845 const dest_ty_tag = dest_ty.zigTypeTagOrPoison(mod) catch |err| switch (err) { 9846 error.GenericPoison => return operand, 9847 }; 9848 if (dest_ty_tag == .NoReturn) { 9849 return sema.fail(block, src, "cannot cast to noreturn", .{}); 9850 } 9851 const is_ret = if (zir_dest_type.toIndex()) |ptr_index| 9852 sema.code.instructions.items(.tag)[@intFromEnum(ptr_index)] == .ret_type 9853 else 9854 false; 9855 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) { 9856 error.NotCoercible => unreachable, 9857 else => |e| return e, 9858 }; 9859 } 9860 9861 fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9862 const tracy = trace(@src()); 9863 defer tracy.end(); 9864 9865 const mod = sema.mod; 9866 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 9867 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 9868 const operand = try sema.resolveInst(inst_data.operand); 9869 const operand_ty = sema.typeOf(operand); 9870 const ptr_ty = operand_ty.scalarType(mod); 9871 const is_vector = operand_ty.zigTypeTag(mod) == .Vector; 9872 if (!ptr_ty.isPtrAtRuntime(mod)) { 9873 return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(mod)}); 9874 } 9875 const pointee_ty = ptr_ty.childType(mod); 9876 if (try sema.typeRequiresComptime(ptr_ty)) { 9877 const msg = msg: { 9878 const msg = try sema.errMsg(block, ptr_src, "comptime-only type '{}' has no pointer address", .{pointee_ty.fmt(mod)}); 9879 errdefer msg.destroy(sema.gpa); 9880 const src_decl = mod.declPtr(block.src_decl); 9881 try sema.explainWhyTypeIsComptime(msg, ptr_src.toSrcLoc(src_decl, mod), pointee_ty); 9882 break :msg msg; 9883 }; 9884 return sema.failWithOwnedErrorMsg(block, msg); 9885 } 9886 if (try sema.resolveValueIntable(operand)) |operand_val| ct: { 9887 if (!is_vector) { 9888 return Air.internedToRef((try mod.intValue( 9889 Type.usize, 9890 (try operand_val.getUnsignedIntAdvanced(mod, sema)).?, 9891 )).toIntern()); 9892 } 9893 const len = operand_ty.vectorLen(mod); 9894 const dest_ty = try mod.vectorType(.{ .child = .usize_type, .len = len }); 9895 const new_elems = try sema.arena.alloc(InternPool.Index, len); 9896 for (new_elems, 0..) |*new_elem, i| { 9897 const ptr_val = try operand_val.elemValue(mod, i); 9898 const addr = try ptr_val.getUnsignedIntAdvanced(mod, sema) orelse { 9899 // A vector element wasn't an integer pointer. This is a runtime operation. 9900 break :ct; 9901 }; 9902 new_elem.* = (try mod.intValue( 9903 Type.usize, 9904 addr, 9905 )).toIntern(); 9906 } 9907 return Air.internedToRef(try mod.intern(.{ .aggregate = .{ 9908 .ty = dest_ty.toIntern(), 9909 .storage = .{ .elems = new_elems }, 9910 } })); 9911 } 9912 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); 9913 if (!is_vector) { 9914 return block.addUnOp(.int_from_ptr, operand); 9915 } 9916 const len = operand_ty.vectorLen(mod); 9917 const dest_ty = try mod.vectorType(.{ .child = .usize_type, .len = len }); 9918 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len); 9919 for (new_elems, 0..) |*new_elem, i| { 9920 const idx_ref = try mod.intRef(Type.usize, i); 9921 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref); 9922 new_elem.* = try block.addUnOp(.int_from_ptr, old_elem); 9923 } 9924 return block.addAggregateInit(dest_ty, new_elems); 9925 } 9926 9927 fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9928 const tracy = trace(@src()); 9929 defer tracy.end(); 9930 9931 const mod = sema.mod; 9932 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9933 const src = inst_data.src(); 9934 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; 9935 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9936 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); 9937 const object = try sema.resolveInst(extra.lhs); 9938 return sema.fieldVal(block, src, object, field_name, field_name_src); 9939 } 9940 9941 fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9942 const tracy = trace(@src()); 9943 defer tracy.end(); 9944 9945 const mod = sema.mod; 9946 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9947 const src = inst_data.src(); 9948 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; 9949 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9950 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); 9951 const object_ptr = try sema.resolveInst(extra.lhs); 9952 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); 9953 } 9954 9955 fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9956 const tracy = trace(@src()); 9957 defer tracy.end(); 9958 9959 const mod = sema.mod; 9960 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9961 const src = inst_data.src(); 9962 const field_name_src: LazySrcLoc = .{ .node_offset_field_name_init = inst_data.src_node }; 9963 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9964 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); 9965 const object_ptr = try sema.resolveInst(extra.lhs); 9966 const struct_ty = sema.typeOf(object_ptr).childType(mod); 9967 switch (struct_ty.zigTypeTag(mod)) { 9968 .Struct, .Union => { 9969 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true); 9970 }, 9971 else => { 9972 return sema.failWithStructInitNotSupported(block, src, struct_ty); 9973 }, 9974 } 9975 } 9976 9977 fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9978 const tracy = trace(@src()); 9979 defer tracy.end(); 9980 9981 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9982 const src = inst_data.src(); 9983 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 9984 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; 9985 const object = try sema.resolveInst(extra.lhs); 9986 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ 9987 .needed_comptime_reason = "field name must be comptime-known", 9988 }); 9989 return sema.fieldVal(block, src, object, field_name, field_name_src); 9990 } 9991 9992 fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9993 const tracy = trace(@src()); 9994 defer tracy.end(); 9995 9996 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 9997 const src = inst_data.src(); 9998 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 9999 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; 10000 const object_ptr = try sema.resolveInst(extra.lhs); 10001 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ 10002 .needed_comptime_reason = "field name must be comptime-known", 10003 }); 10004 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); 10005 } 10006 10007 fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10008 const tracy = trace(@src()); 10009 defer tracy.end(); 10010 10011 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10012 const src = inst_data.src(); 10013 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 10014 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10015 10016 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast"); 10017 const operand = try sema.resolveInst(extra.rhs); 10018 10019 return sema.intCast(block, inst_data.src(), dest_ty, src, operand, operand_src, true); 10020 } 10021 10022 fn intCast( 10023 sema: *Sema, 10024 block: *Block, 10025 src: LazySrcLoc, 10026 dest_ty: Type, 10027 dest_ty_src: LazySrcLoc, 10028 operand: Air.Inst.Ref, 10029 operand_src: LazySrcLoc, 10030 runtime_safety: bool, 10031 ) CompileError!Air.Inst.Ref { 10032 const mod = sema.mod; 10033 const operand_ty = sema.typeOf(operand); 10034 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src); 10035 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 10036 10037 if (try sema.isComptimeKnown(operand)) { 10038 return sema.coerce(block, dest_ty, operand, operand_src); 10039 } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 10040 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); 10041 } 10042 10043 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); 10044 const is_vector = dest_ty.zigTypeTag(mod) == .Vector; 10045 10046 if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| { 10047 // requirement: intCast(u0, input) iff input == 0 10048 if (runtime_safety and block.wantSafety()) { 10049 try sema.requireRuntimeBlock(block, src, operand_src); 10050 const wanted_info = dest_scalar_ty.intInfo(mod); 10051 const wanted_bits = wanted_info.bits; 10052 10053 if (wanted_bits == 0) { 10054 const ok = if (is_vector) ok: { 10055 const zeros = try sema.splat(operand_ty, try mod.intValue(operand_scalar_ty, 0)); 10056 const zero_inst = Air.internedToRef(zeros.toIntern()); 10057 const is_in_range = try block.addCmpVector(operand, zero_inst, .eq); 10058 const all_in_range = try block.addInst(.{ 10059 .tag = .reduce, 10060 .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } }, 10061 }); 10062 break :ok all_in_range; 10063 } else ok: { 10064 const zero_inst = Air.internedToRef((try mod.intValue(operand_ty, 0)).toIntern()); 10065 const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst); 10066 break :ok is_in_range; 10067 }; 10068 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data); 10069 } 10070 } 10071 10072 return Air.internedToRef(opv.toIntern()); 10073 } 10074 10075 try sema.requireRuntimeBlock(block, src, operand_src); 10076 if (runtime_safety and block.wantSafety()) { 10077 const actual_info = operand_scalar_ty.intInfo(mod); 10078 const wanted_info = dest_scalar_ty.intInfo(mod); 10079 const actual_bits = actual_info.bits; 10080 const wanted_bits = wanted_info.bits; 10081 const actual_value_bits = actual_bits - @intFromBool(actual_info.signedness == .signed); 10082 const wanted_value_bits = wanted_bits - @intFromBool(wanted_info.signedness == .signed); 10083 10084 // range shrinkage 10085 // requirement: int value fits into target type 10086 if (wanted_value_bits < actual_value_bits) { 10087 const dest_max_val_scalar = try dest_scalar_ty.maxIntScalar(mod, operand_scalar_ty); 10088 const dest_max_val = try sema.splat(operand_ty, dest_max_val_scalar); 10089 const dest_max = Air.internedToRef(dest_max_val.toIntern()); 10090 const diff = try block.addBinOp(.sub_wrap, dest_max, operand); 10091 10092 if (actual_info.signedness == .signed) { 10093 // Reinterpret the sign-bit as part of the value. This will make 10094 // negative differences (`operand` > `dest_max`) appear too big. 10095 const unsigned_scalar_operand_ty = try mod.intType(.unsigned, actual_bits); 10096 const unsigned_operand_ty = if (is_vector) try mod.vectorType(.{ 10097 .len = dest_ty.vectorLen(mod), 10098 .child = unsigned_scalar_operand_ty.toIntern(), 10099 }) else unsigned_scalar_operand_ty; 10100 const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff); 10101 10102 // If the destination type is signed, then we need to double its 10103 // range to account for negative values. 10104 const dest_range_val = if (wanted_info.signedness == .signed) range_val: { 10105 const one_scalar = try mod.intValue(unsigned_scalar_operand_ty, 1); 10106 const one = if (is_vector) Value.fromInterned((try mod.intern(.{ .aggregate = .{ 10107 .ty = unsigned_operand_ty.toIntern(), 10108 .storage = .{ .repeated_elem = one_scalar.toIntern() }, 10109 } }))) else one_scalar; 10110 const range_minus_one = try dest_max_val.shl(one, unsigned_operand_ty, sema.arena, mod); 10111 break :range_val try sema.intAdd(range_minus_one, one, unsigned_operand_ty, undefined); 10112 } else try mod.getCoerced(dest_max_val, unsigned_operand_ty); 10113 const dest_range = Air.internedToRef(dest_range_val.toIntern()); 10114 10115 const ok = if (is_vector) ok: { 10116 const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte); 10117 const all_in_range = try block.addInst(.{ 10118 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 10119 .data = .{ .reduce = .{ 10120 .operand = is_in_range, 10121 .operation = .And, 10122 } }, 10123 }); 10124 break :ok all_in_range; 10125 } else ok: { 10126 const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range); 10127 break :ok is_in_range; 10128 }; 10129 // TODO negative_to_unsigned? 10130 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data); 10131 } else { 10132 const ok = if (is_vector) ok: { 10133 const is_in_range = try block.addCmpVector(diff, dest_max, .lte); 10134 const all_in_range = try block.addInst(.{ 10135 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 10136 .data = .{ .reduce = .{ 10137 .operand = is_in_range, 10138 .operation = .And, 10139 } }, 10140 }); 10141 break :ok all_in_range; 10142 } else ok: { 10143 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max); 10144 break :ok is_in_range; 10145 }; 10146 try sema.addSafetyCheck(block, src, ok, .cast_truncated_data); 10147 } 10148 } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) { 10149 // no shrinkage, yes sign loss 10150 // requirement: signed to unsigned >= 0 10151 const ok = if (is_vector) ok: { 10152 const scalar_zero = try mod.intValue(operand_scalar_ty, 0); 10153 const zero_val = try sema.splat(operand_ty, scalar_zero); 10154 const zero_inst = Air.internedToRef(zero_val.toIntern()); 10155 const is_in_range = try block.addCmpVector(operand, zero_inst, .gte); 10156 const all_in_range = try block.addInst(.{ 10157 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 10158 .data = .{ .reduce = .{ 10159 .operand = is_in_range, 10160 .operation = .And, 10161 } }, 10162 }); 10163 break :ok all_in_range; 10164 } else ok: { 10165 const zero_inst = Air.internedToRef((try mod.intValue(operand_ty, 0)).toIntern()); 10166 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); 10167 break :ok is_in_range; 10168 }; 10169 try sema.addSafetyCheck(block, src, ok, .negative_to_unsigned); 10170 } 10171 } 10172 return block.addTyOp(.intcast, dest_ty, operand); 10173 } 10174 10175 fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10176 const tracy = trace(@src()); 10177 defer tracy.end(); 10178 10179 const mod = sema.mod; 10180 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10181 const src = inst_data.src(); 10182 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 10183 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10184 10185 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast"); 10186 const operand = try sema.resolveInst(extra.rhs); 10187 const operand_ty = sema.typeOf(operand); 10188 switch (dest_ty.zigTypeTag(mod)) { 10189 .AnyFrame, 10190 .ComptimeFloat, 10191 .ComptimeInt, 10192 .EnumLiteral, 10193 .ErrorSet, 10194 .ErrorUnion, 10195 .Fn, 10196 .Frame, 10197 .NoReturn, 10198 .Null, 10199 .Opaque, 10200 .Optional, 10201 .Type, 10202 .Undefined, 10203 .Void, 10204 => return sema.fail(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}), 10205 10206 .Enum => { 10207 const msg = msg: { 10208 const msg = try sema.errMsg(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}); 10209 errdefer msg.destroy(sema.gpa); 10210 switch (operand_ty.zigTypeTag(mod)) { 10211 .Int, .ComptimeInt => try sema.errNote(block, src, msg, "use @enumFromInt to cast from '{}'", .{operand_ty.fmt(mod)}), 10212 else => {}, 10213 } 10214 10215 break :msg msg; 10216 }; 10217 return sema.failWithOwnedErrorMsg(block, msg); 10218 }, 10219 10220 .Pointer => { 10221 const msg = msg: { 10222 const msg = try sema.errMsg(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}); 10223 errdefer msg.destroy(sema.gpa); 10224 switch (operand_ty.zigTypeTag(mod)) { 10225 .Int, .ComptimeInt => try sema.errNote(block, src, msg, "use @ptrFromInt to cast from '{}'", .{operand_ty.fmt(mod)}), 10226 .Pointer => try sema.errNote(block, src, msg, "use @ptrCast to cast from '{}'", .{operand_ty.fmt(mod)}), 10227 else => {}, 10228 } 10229 10230 break :msg msg; 10231 }; 10232 return sema.failWithOwnedErrorMsg(block, msg); 10233 }, 10234 .Struct, .Union => if (dest_ty.containerLayout(mod) == .Auto) { 10235 const container = switch (dest_ty.zigTypeTag(mod)) { 10236 .Struct => "struct", 10237 .Union => "union", 10238 else => unreachable, 10239 }; 10240 return sema.fail(block, src, "cannot @bitCast to '{}'; {s} does not have a guaranteed in-memory layout", .{ 10241 dest_ty.fmt(mod), container, 10242 }); 10243 }, 10244 10245 .Array, 10246 .Bool, 10247 .Float, 10248 .Int, 10249 .Vector, 10250 => {}, 10251 } 10252 switch (operand_ty.zigTypeTag(mod)) { 10253 .AnyFrame, 10254 .ComptimeFloat, 10255 .ComptimeInt, 10256 .EnumLiteral, 10257 .ErrorSet, 10258 .ErrorUnion, 10259 .Fn, 10260 .Frame, 10261 .NoReturn, 10262 .Null, 10263 .Opaque, 10264 .Optional, 10265 .Type, 10266 .Undefined, 10267 .Void, 10268 => return sema.fail(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}), 10269 10270 .Enum => { 10271 const msg = msg: { 10272 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}); 10273 errdefer msg.destroy(sema.gpa); 10274 switch (dest_ty.zigTypeTag(mod)) { 10275 .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @intFromEnum to cast to '{}'", .{dest_ty.fmt(mod)}), 10276 else => {}, 10277 } 10278 10279 break :msg msg; 10280 }; 10281 return sema.failWithOwnedErrorMsg(block, msg); 10282 }, 10283 .Pointer => { 10284 const msg = msg: { 10285 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}); 10286 errdefer msg.destroy(sema.gpa); 10287 switch (dest_ty.zigTypeTag(mod)) { 10288 .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @intFromPtr to cast to '{}'", .{dest_ty.fmt(mod)}), 10289 .Pointer => try sema.errNote(block, operand_src, msg, "use @ptrCast to cast to '{}'", .{dest_ty.fmt(mod)}), 10290 else => {}, 10291 } 10292 10293 break :msg msg; 10294 }; 10295 return sema.failWithOwnedErrorMsg(block, msg); 10296 }, 10297 .Struct, .Union => if (operand_ty.containerLayout(mod) == .Auto) { 10298 const container = switch (operand_ty.zigTypeTag(mod)) { 10299 .Struct => "struct", 10300 .Union => "union", 10301 else => unreachable, 10302 }; 10303 return sema.fail(block, operand_src, "cannot @bitCast from '{}'; {s} does not have a guaranteed in-memory layout", .{ 10304 operand_ty.fmt(mod), container, 10305 }); 10306 }, 10307 10308 .Array, 10309 .Bool, 10310 .Float, 10311 .Int, 10312 .Vector, 10313 => {}, 10314 } 10315 return sema.bitCast(block, dest_ty, operand, inst_data.src(), operand_src); 10316 } 10317 10318 fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10319 const tracy = trace(@src()); 10320 defer tracy.end(); 10321 10322 const mod = sema.mod; 10323 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10324 const src = inst_data.src(); 10325 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 10326 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10327 10328 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast"); 10329 const dest_scalar_ty = dest_ty.scalarType(mod); 10330 10331 const operand = try sema.resolveInst(extra.rhs); 10332 const operand_ty = sema.typeOf(operand); 10333 const operand_scalar_ty = operand_ty.scalarType(mod); 10334 10335 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 10336 const is_vector = dest_ty.zigTypeTag(mod) == .Vector; 10337 10338 const target = mod.getTarget(); 10339 const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(mod)) { 10340 .ComptimeFloat => true, 10341 .Float => false, 10342 else => return sema.fail( 10343 block, 10344 src, 10345 "expected float or vector type, found '{}'", 10346 .{dest_ty.fmt(mod)}, 10347 ), 10348 }; 10349 10350 switch (operand_scalar_ty.zigTypeTag(mod)) { 10351 .ComptimeFloat, .Float, .ComptimeInt => {}, 10352 else => return sema.fail( 10353 block, 10354 operand_src, 10355 "expected float or vector type, found '{}'", 10356 .{operand_ty.fmt(mod)}, 10357 ), 10358 } 10359 10360 if (try sema.resolveValue(operand)) |operand_val| { 10361 if (!is_vector) { 10362 return Air.internedToRef((try operand_val.floatCast(dest_ty, mod)).toIntern()); 10363 } 10364 const vec_len = operand_ty.vectorLen(mod); 10365 const new_elems = try sema.arena.alloc(InternPool.Index, vec_len); 10366 for (new_elems, 0..) |*new_elem, i| { 10367 const old_elem = try operand_val.elemValue(mod, i); 10368 new_elem.* = (try old_elem.floatCast(dest_scalar_ty, mod)).toIntern(); 10369 } 10370 return Air.internedToRef(try mod.intern(.{ .aggregate = .{ 10371 .ty = dest_ty.toIntern(), 10372 .storage = .{ .elems = new_elems }, 10373 } })); 10374 } 10375 if (dest_is_comptime_float) { 10376 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{}); 10377 } 10378 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 10379 10380 const src_bits = operand_scalar_ty.floatBits(target); 10381 const dst_bits = dest_scalar_ty.floatBits(target); 10382 if (dst_bits >= src_bits) { 10383 return sema.coerce(block, dest_ty, operand, operand_src); 10384 } 10385 if (!is_vector) { 10386 return block.addTyOp(.fptrunc, dest_ty, operand); 10387 } 10388 const vec_len = operand_ty.vectorLen(mod); 10389 const new_elems = try sema.arena.alloc(Air.Inst.Ref, vec_len); 10390 for (new_elems, 0..) |*new_elem, i| { 10391 const idx_ref = try mod.intRef(Type.usize, i); 10392 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref); 10393 new_elem.* = try block.addTyOp(.fptrunc, dest_scalar_ty, old_elem); 10394 } 10395 return block.addAggregateInit(dest_ty, new_elems); 10396 } 10397 10398 fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10399 const tracy = trace(@src()); 10400 defer tracy.end(); 10401 10402 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10403 const src = inst_data.src(); 10404 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10405 const array = try sema.resolveInst(extra.lhs); 10406 const elem_index = try sema.resolveInst(extra.rhs); 10407 return sema.elemVal(block, src, array, elem_index, src, false); 10408 } 10409 10410 fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10411 const tracy = trace(@src()); 10412 defer tracy.end(); 10413 10414 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10415 const src = inst_data.src(); 10416 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; 10417 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10418 const array = try sema.resolveInst(extra.lhs); 10419 const elem_index = try sema.resolveInst(extra.rhs); 10420 return sema.elemVal(block, src, array, elem_index, elem_index_src, true); 10421 } 10422 10423 fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10424 const tracy = trace(@src()); 10425 defer tracy.end(); 10426 10427 const mod = sema.mod; 10428 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm; 10429 const array = try sema.resolveInst(inst_data.operand); 10430 const elem_index = try mod.intRef(Type.usize, inst_data.idx); 10431 return sema.elemVal(block, .unneeded, array, elem_index, .unneeded, false); 10432 } 10433 10434 fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10435 const tracy = trace(@src()); 10436 defer tracy.end(); 10437 10438 const mod = sema.mod; 10439 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10440 const src = inst_data.src(); 10441 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10442 const array_ptr = try sema.resolveInst(extra.lhs); 10443 const elem_index = try sema.resolveInst(extra.rhs); 10444 const indexable_ty = sema.typeOf(array_ptr); 10445 if (indexable_ty.zigTypeTag(mod) != .Pointer) { 10446 const capture_src: LazySrcLoc = .{ .for_capture_from_input = inst_data.src_node }; 10447 const msg = msg: { 10448 const msg = try sema.errMsg(block, capture_src, "pointer capture of non pointer type '{}'", .{ 10449 indexable_ty.fmt(mod), 10450 }); 10451 errdefer msg.destroy(sema.gpa); 10452 if (indexable_ty.isIndexable(mod)) { 10453 try sema.errNote(block, src, msg, "consider using '&' here", .{}); 10454 } 10455 break :msg msg; 10456 }; 10457 return sema.failWithOwnedErrorMsg(block, msg); 10458 } 10459 return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false); 10460 } 10461 10462 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10463 const tracy = trace(@src()); 10464 defer tracy.end(); 10465 10466 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10467 const src = inst_data.src(); 10468 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; 10469 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10470 const array_ptr = try sema.resolveInst(extra.lhs); 10471 const elem_index = try sema.resolveInst(extra.rhs); 10472 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true); 10473 } 10474 10475 fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10476 const tracy = trace(@src()); 10477 defer tracy.end(); 10478 10479 const mod = sema.mod; 10480 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10481 const src = inst_data.src(); 10482 const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; 10483 const array_ptr = try sema.resolveInst(extra.ptr); 10484 const elem_index = try sema.mod.intRef(Type.usize, extra.index); 10485 const array_ty = sema.typeOf(array_ptr).childType(mod); 10486 switch (array_ty.zigTypeTag(mod)) { 10487 .Array, .Vector => {}, 10488 else => if (!array_ty.isTuple(mod)) { 10489 return sema.failWithArrayInitNotSupported(block, src, array_ty); 10490 }, 10491 } 10492 return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true); 10493 } 10494 10495 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10496 const tracy = trace(@src()); 10497 defer tracy.end(); 10498 10499 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10500 const src = inst_data.src(); 10501 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; 10502 const array_ptr = try sema.resolveInst(extra.lhs); 10503 const start = try sema.resolveInst(extra.start); 10504 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; 10505 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; 10506 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; 10507 10508 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false); 10509 } 10510 10511 fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10512 const tracy = trace(@src()); 10513 defer tracy.end(); 10514 10515 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10516 const src = inst_data.src(); 10517 const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data; 10518 const array_ptr = try sema.resolveInst(extra.lhs); 10519 const start = try sema.resolveInst(extra.start); 10520 const end = try sema.resolveInst(extra.end); 10521 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; 10522 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; 10523 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; 10524 10525 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false); 10526 } 10527 10528 fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10529 const tracy = trace(@src()); 10530 defer tracy.end(); 10531 10532 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10533 const src = inst_data.src(); 10534 const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node }; 10535 const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; 10536 const array_ptr = try sema.resolveInst(extra.lhs); 10537 const start = try sema.resolveInst(extra.start); 10538 const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end); 10539 const sentinel = try sema.resolveInst(extra.sentinel); 10540 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; 10541 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; 10542 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; 10543 10544 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false); 10545 } 10546 10547 fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10548 const tracy = trace(@src()); 10549 defer tracy.end(); 10550 10551 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 10552 const src = inst_data.src(); 10553 const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; 10554 const array_ptr = try sema.resolveInst(extra.lhs); 10555 const start = try sema.resolveInst(extra.start); 10556 const len = try sema.resolveInst(extra.len); 10557 const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel); 10558 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; 10559 const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset }; 10560 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; 10561 const sentinel_src: LazySrcLoc = if (sentinel == .none) 10562 .unneeded 10563 else 10564 .{ .node_offset_slice_sentinel = inst_data.src_node }; 10565 10566 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); 10567 } 10568 10569 /// Holds common data used when analyzing or resolving switch prong bodies, 10570 /// including setting up captures. 10571 const SwitchProngAnalysis = struct { 10572 sema: *Sema, 10573 /// The block containing the `switch_block` itself. 10574 parent_block: *Block, 10575 /// The raw switch operand value (*not* the condition). Always defined. 10576 operand: Air.Inst.Ref, 10577 /// May be `undefined` if no prong has a by-ref capture. 10578 operand_ptr: Air.Inst.Ref, 10579 /// The switch condition value. For unions, `operand` is the union and `cond` is its tag. 10580 cond: Air.Inst.Ref, 10581 /// If this switch is on an error set, this is the type to assign to the 10582 /// `else` prong. If `null`, the prong should be unreachable. 10583 else_error_ty: ?Type, 10584 /// The index of the `switch_block` instruction itself. 10585 switch_block_inst: Zir.Inst.Index, 10586 /// The dummy index into which inline tag captures should be placed. May be 10587 /// undefined if no prong has a tag capture. 10588 tag_capture_inst: Zir.Inst.Index, 10589 10590 /// Resolve a switch prong which is determined at comptime to have no peers. 10591 /// Uses `resolveBlockBody`. Sets up captures as needed. 10592 fn resolveProngComptime( 10593 spa: SwitchProngAnalysis, 10594 child_block: *Block, 10595 prong_type: enum { normal, special }, 10596 prong_body: []const Zir.Inst.Index, 10597 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10598 /// Must use the `scalar_capture`, `special_capture`, or `multi_capture` union field. 10599 raw_capture_src: Module.SwitchProngSrc, 10600 /// The set of all values which can reach this prong. May be undefined 10601 /// if the prong is special or contains ranges. 10602 case_vals: []const Air.Inst.Ref, 10603 /// The inline capture of this prong. If this is not an inline prong, 10604 /// this is `.none`. 10605 inline_case_capture: Air.Inst.Ref, 10606 /// Whether this prong has an inline tag capture. If `true`, then 10607 /// `inline_case_capture` cannot be `.none`. 10608 has_tag_capture: bool, 10609 merges: *Block.Merges, 10610 ) CompileError!Air.Inst.Ref { 10611 const sema = spa.sema; 10612 const src = sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src(); 10613 10614 if (has_tag_capture) { 10615 const tag_ref = try spa.analyzeTagCapture(child_block, raw_capture_src, inline_case_capture); 10616 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); 10617 } 10618 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); 10619 10620 switch (capture) { 10621 .none => { 10622 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); 10623 }, 10624 10625 .by_val, .by_ref => { 10626 const capture_ref = try spa.analyzeCapture( 10627 child_block, 10628 capture == .by_ref, 10629 prong_type == .special, 10630 raw_capture_src, 10631 case_vals, 10632 inline_case_capture, 10633 ); 10634 10635 if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) { 10636 // This prong should be unreachable! 10637 return .unreachable_value; 10638 } 10639 10640 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); 10641 defer assert(sema.inst_map.remove(spa.switch_block_inst)); 10642 10643 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); 10644 }, 10645 } 10646 } 10647 10648 /// Analyze a switch prong which may have peers at runtime. 10649 /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed. 10650 fn analyzeProngRuntime( 10651 spa: SwitchProngAnalysis, 10652 case_block: *Block, 10653 prong_type: enum { normal, special }, 10654 prong_body: []const Zir.Inst.Index, 10655 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10656 /// Must use the `scalar`, `special`, or `multi_capture` union field. 10657 raw_capture_src: Module.SwitchProngSrc, 10658 /// The set of all values which can reach this prong. May be undefined 10659 /// if the prong is special or contains ranges. 10660 case_vals: []const Air.Inst.Ref, 10661 /// The inline capture of this prong. If this is not an inline prong, 10662 /// this is `.none`. 10663 inline_case_capture: Air.Inst.Ref, 10664 /// Whether this prong has an inline tag capture. If `true`, then 10665 /// `inline_case_capture` cannot be `.none`. 10666 has_tag_capture: bool, 10667 ) CompileError!void { 10668 const sema = spa.sema; 10669 10670 if (has_tag_capture) { 10671 const tag_ref = try spa.analyzeTagCapture(case_block, raw_capture_src, inline_case_capture); 10672 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); 10673 } 10674 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); 10675 10676 switch (capture) { 10677 .none => { 10678 return sema.analyzeBodyRuntimeBreak(case_block, prong_body); 10679 }, 10680 10681 .by_val, .by_ref => { 10682 const capture_ref = try spa.analyzeCapture( 10683 case_block, 10684 capture == .by_ref, 10685 prong_type == .special, 10686 raw_capture_src, 10687 case_vals, 10688 inline_case_capture, 10689 ); 10690 10691 if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) { 10692 // No need to analyze any further, the prong is unreachable 10693 return; 10694 } 10695 10696 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); 10697 defer assert(sema.inst_map.remove(spa.switch_block_inst)); 10698 10699 return sema.analyzeBodyRuntimeBreak(case_block, prong_body); 10700 }, 10701 } 10702 } 10703 10704 fn analyzeTagCapture( 10705 spa: SwitchProngAnalysis, 10706 block: *Block, 10707 raw_capture_src: Module.SwitchProngSrc, 10708 inline_case_capture: Air.Inst.Ref, 10709 ) CompileError!Air.Inst.Ref { 10710 const sema = spa.sema; 10711 const mod = sema.mod; 10712 const operand_ty = sema.typeOf(spa.operand); 10713 if (operand_ty.zigTypeTag(mod) != .Union) { 10714 const zir_datas = sema.code.instructions.items(.data); 10715 const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node; 10716 const raw_tag_capture_src: Module.SwitchProngSrc = switch (raw_capture_src) { 10717 .scalar_capture => |i| .{ .scalar_tag_capture = i }, 10718 .multi_capture => |i| .{ .multi_tag_capture = i }, 10719 .special_capture => .special_tag_capture, 10720 else => unreachable, 10721 }; 10722 const capture_src = raw_tag_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none); 10723 const msg = msg: { 10724 const msg = try sema.errMsg(block, capture_src, "cannot capture tag of non-union type '{}'", .{ 10725 operand_ty.fmt(mod), 10726 }); 10727 errdefer msg.destroy(sema.gpa); 10728 try sema.addDeclaredHereNote(msg, operand_ty); 10729 break :msg msg; 10730 }; 10731 return sema.failWithOwnedErrorMsg(block, msg); 10732 } 10733 assert(inline_case_capture != .none); 10734 return inline_case_capture; 10735 } 10736 10737 fn analyzeCapture( 10738 spa: SwitchProngAnalysis, 10739 block: *Block, 10740 capture_byref: bool, 10741 is_special_prong: bool, 10742 raw_capture_src: Module.SwitchProngSrc, 10743 case_vals: []const Air.Inst.Ref, 10744 inline_case_capture: Air.Inst.Ref, 10745 ) CompileError!Air.Inst.Ref { 10746 const sema = spa.sema; 10747 const mod = sema.mod; 10748 const ip = &mod.intern_pool; 10749 10750 const zir_datas = sema.code.instructions.items(.data); 10751 const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node; 10752 10753 const operand_ty = sema.typeOf(spa.operand); 10754 const operand_ptr_ty = if (capture_byref) sema.typeOf(spa.operand_ptr) else undefined; 10755 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_node_offset }; 10756 10757 if (inline_case_capture != .none) { 10758 const item_val = sema.resolveConstDefinedValue(block, .unneeded, inline_case_capture, undefined) catch unreachable; 10759 if (operand_ty.zigTypeTag(mod) == .Union) { 10760 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, mod).?); 10761 const union_obj = mod.typeToUnion(operand_ty).?; 10762 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 10763 if (capture_byref) { 10764 const ptr_field_ty = try sema.ptrType(.{ 10765 .child = field_ty.toIntern(), 10766 .flags = .{ 10767 .is_const = !operand_ptr_ty.ptrIsMutable(mod), 10768 .is_volatile = operand_ptr_ty.isVolatilePtr(mod), 10769 .address_space = operand_ptr_ty.ptrAddressSpace(mod), 10770 }, 10771 }); 10772 if (try sema.resolveDefinedValue(block, sema.src, spa.operand_ptr)) |union_ptr| { 10773 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 10774 .ty = ptr_field_ty.toIntern(), 10775 .addr = .{ .field = .{ 10776 .base = union_ptr.toIntern(), 10777 .index = field_index, 10778 } }, 10779 } }))); 10780 } 10781 return block.addStructFieldPtr(spa.operand_ptr, field_index, ptr_field_ty); 10782 } else { 10783 if (try sema.resolveDefinedValue(block, sema.src, spa.operand)) |union_val| { 10784 const tag_and_val = ip.indexToKey(union_val.toIntern()).un; 10785 return Air.internedToRef(tag_and_val.val); 10786 } 10787 return block.addStructFieldVal(spa.operand, field_index, field_ty); 10788 } 10789 } else if (capture_byref) { 10790 return anonDeclRef(sema, item_val.toIntern()); 10791 } else { 10792 return inline_case_capture; 10793 } 10794 } 10795 10796 if (is_special_prong) { 10797 if (capture_byref) { 10798 return spa.operand_ptr; 10799 } 10800 10801 switch (operand_ty.zigTypeTag(mod)) { 10802 .ErrorSet => if (spa.else_error_ty) |ty| { 10803 return sema.bitCast(block, ty, spa.operand, operand_src, null); 10804 } else { 10805 try block.addUnreachable(operand_src, false); 10806 return .unreachable_value; 10807 }, 10808 else => return spa.operand, 10809 } 10810 } 10811 10812 switch (operand_ty.zigTypeTag(mod)) { 10813 .Union => { 10814 const union_obj = mod.typeToUnion(operand_ty).?; 10815 const first_item_val = sema.resolveConstDefinedValue(block, .unneeded, case_vals[0], undefined) catch unreachable; 10816 10817 const first_field_index: u32 = mod.unionTagFieldIndex(union_obj, first_item_val).?; 10818 const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[first_field_index]); 10819 10820 const field_indices = try sema.arena.alloc(u32, case_vals.len); 10821 for (case_vals, field_indices) |item, *field_idx| { 10822 const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable; 10823 field_idx.* = mod.unionTagFieldIndex(union_obj, item_val).?; 10824 } 10825 10826 // Fast path: if all the operands are the same type already, we don't need to hit 10827 // PTR! This will also allow us to emit simpler code. 10828 const same_types = for (field_indices[1..]) |field_idx| { 10829 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]); 10830 if (!field_ty.eql(first_field_ty, sema.mod)) break false; 10831 } else true; 10832 10833 const capture_ty = if (same_types) first_field_ty else capture_ty: { 10834 // We need values to run PTR on, so make a bunch of undef constants. 10835 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); 10836 for (dummy_captures, field_indices) |*dummy, field_idx| { 10837 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]); 10838 dummy.* = try mod.undefRef(field_ty); 10839 } 10840 10841 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); 10842 @memset(case_srcs, .unneeded); 10843 10844 break :capture_ty sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { 10845 error.NeededSourceLocation => { 10846 // This must be a multi-prong so this must be a `multi_capture` src 10847 const multi_idx = raw_capture_src.multi_capture; 10848 const src_decl_ptr = sema.mod.declPtr(block.src_decl); 10849 for (case_srcs, 0..) |*case_src, i| { 10850 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } }; 10851 case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10852 } 10853 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10854 _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) { 10855 error.AnalysisFail => { 10856 const msg = sema.err orelse return error.AnalysisFail; 10857 try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{}); 10858 return error.AnalysisFail; 10859 }, 10860 else => |e| return e, 10861 }; 10862 unreachable; 10863 }, 10864 else => |e| return e, 10865 }; 10866 }; 10867 10868 // By-reference captures have some further restrictions which make them easier to emit 10869 if (capture_byref) { 10870 const operand_ptr_info = operand_ptr_ty.ptrInfo(mod); 10871 const capture_ptr_ty = try sema.ptrType(.{ 10872 .child = capture_ty.toIntern(), 10873 .flags = .{ 10874 // TODO: alignment! 10875 .is_const = operand_ptr_info.flags.is_const, 10876 .is_volatile = operand_ptr_info.flags.is_volatile, 10877 .address_space = operand_ptr_info.flags.address_space, 10878 }, 10879 }); 10880 10881 // By-ref captures of hetereogeneous types are only allowed if each field 10882 // pointer type is in-memory coercible to the capture pointer type. 10883 if (!same_types) { 10884 for (field_indices, 0..) |field_idx, i| { 10885 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]); 10886 const field_ptr_ty = try sema.ptrType(.{ 10887 .child = field_ty.toIntern(), 10888 .flags = .{ 10889 // TODO: alignment! 10890 .is_const = operand_ptr_info.flags.is_const, 10891 .is_volatile = operand_ptr_info.flags.is_volatile, 10892 .address_space = operand_ptr_info.flags.address_space, 10893 }, 10894 }); 10895 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { 10896 const multi_idx = raw_capture_src.multi_capture; 10897 const src_decl_ptr = sema.mod.declPtr(block.src_decl); 10898 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10899 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(i) } }; 10900 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10901 const msg = msg: { 10902 const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{}); 10903 errdefer msg.destroy(sema.gpa); 10904 try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{ 10905 field_ty.fmt(sema.mod), 10906 capture_ty.fmt(sema.mod), 10907 }); 10908 try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{}); 10909 break :msg msg; 10910 }; 10911 return sema.failWithOwnedErrorMsg(block, msg); 10912 } 10913 } 10914 } 10915 10916 if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| { 10917 if (op_ptr_val.isUndef(mod)) return mod.undefRef(capture_ptr_ty); 10918 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 10919 .ty = capture_ptr_ty.toIntern(), 10920 .addr = .{ .field = .{ 10921 .base = op_ptr_val.toIntern(), 10922 .index = first_field_index, 10923 } }, 10924 } }))); 10925 } 10926 10927 try sema.requireRuntimeBlock(block, operand_src, null); 10928 return block.addStructFieldPtr(spa.operand_ptr, first_field_index, capture_ptr_ty); 10929 } 10930 10931 if (try sema.resolveDefinedValue(block, operand_src, spa.operand)) |operand_val| { 10932 if (operand_val.isUndef(mod)) return mod.undefRef(capture_ty); 10933 const union_val = ip.indexToKey(operand_val.toIntern()).un; 10934 if (Value.fromInterned(union_val.tag).isUndef(mod)) return mod.undefRef(capture_ty); 10935 const uncoerced = Air.internedToRef(union_val.val); 10936 return sema.coerce(block, capture_ty, uncoerced, operand_src); 10937 } 10938 10939 try sema.requireRuntimeBlock(block, operand_src, null); 10940 10941 if (same_types) { 10942 return block.addStructFieldVal(spa.operand, first_field_index, capture_ty); 10943 } 10944 10945 // We may have to emit a switch block which coerces the operand to the capture type. 10946 // If we can, try to avoid that using in-memory coercions. 10947 const first_non_imc = in_mem: { 10948 for (field_indices, 0..) |field_idx, i| { 10949 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]); 10950 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { 10951 break :in_mem i; 10952 } 10953 } 10954 // All fields are in-memory coercible to the resolved type! 10955 // Just take the first field and bitcast the result. 10956 const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field_ty); 10957 return block.addBitCast(capture_ty, uncoerced); 10958 }; 10959 10960 // By-val capture with heterogeneous types which are not all in-memory coercible to 10961 // the resolved capture type. We finally have to fall back to the ugly method. 10962 10963 // However, let's first track which operands are in-memory coercible. There may well 10964 // be several, and we can squash all of these cases into the same switch prong using 10965 // a simple bitcast. We'll make this the 'else' prong. 10966 10967 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len); 10968 in_mem_coercible.unset(first_non_imc); 10969 { 10970 const next = first_non_imc + 1; 10971 for (field_indices[next..], next..) |field_idx, i| { 10972 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]); 10973 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { 10974 in_mem_coercible.unset(i); 10975 } 10976 } 10977 } 10978 10979 const capture_block_inst = try block.addInstAsIndex(.{ 10980 .tag = .block, 10981 .data = .{ 10982 .ty_pl = .{ 10983 .ty = Air.internedToRef(capture_ty.toIntern()), 10984 .payload = undefined, // updated below 10985 }, 10986 }, 10987 }); 10988 10989 const prong_count = field_indices.len - in_mem_coercible.count(); 10990 10991 const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts 10992 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra); 10993 defer cases_extra.deinit(); 10994 10995 { 10996 // Non-bitcast cases 10997 var it = in_mem_coercible.iterator(.{ .kind = .unset }); 10998 while (it.next()) |idx| { 10999 var coerce_block = block.makeSubBlock(); 11000 defer coerce_block.instructions.deinit(sema.gpa); 11001 11002 const field_idx = field_indices[idx]; 11003 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_idx]); 11004 const uncoerced = try coerce_block.addStructFieldVal(spa.operand, field_idx, field_ty); 11005 const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) { 11006 error.NeededSourceLocation => { 11007 const multi_idx = raw_capture_src.multi_capture; 11008 const src_decl_ptr = sema.mod.declPtr(block.src_decl); 11009 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(idx) } }; 11010 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 11011 _ = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); 11012 unreachable; 11013 }, 11014 else => |e| return e, 11015 }; 11016 _ = try coerce_block.addBr(capture_block_inst, coerced); 11017 11018 try cases_extra.ensureUnusedCapacity(3 + coerce_block.instructions.items.len); 11019 cases_extra.appendAssumeCapacity(1); // items_len 11020 cases_extra.appendAssumeCapacity(@intCast(coerce_block.instructions.items.len)); // body_len 11021 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item 11022 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body 11023 } 11024 } 11025 const else_body_len = len: { 11026 // 'else' prong uses a bitcast 11027 var coerce_block = block.makeSubBlock(); 11028 defer coerce_block.instructions.deinit(sema.gpa); 11029 11030 const first_imc_item_idx = in_mem_coercible.findFirstSet().?; 11031 const first_imc_field_idx = field_indices[first_imc_item_idx]; 11032 const first_imc_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]); 11033 const uncoerced = try coerce_block.addStructFieldVal(spa.operand, first_imc_field_idx, first_imc_field_ty); 11034 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); 11035 _ = try coerce_block.addBr(capture_block_inst, coerced); 11036 11037 try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items)); 11038 break :len coerce_block.instructions.items.len; 11039 }; 11040 11041 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + 11042 cases_extra.items.len + 11043 @typeInfo(Air.Block).Struct.fields.len + 11044 1); 11045 11046 const switch_br_inst: u32 = @intCast(sema.air_instructions.len); 11047 try sema.air_instructions.append(sema.gpa, .{ 11048 .tag = .switch_br, 11049 .data = .{ .pl_op = .{ 11050 .operand = spa.cond, 11051 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ 11052 .cases_len = @intCast(prong_count), 11053 .else_body_len = @intCast(else_body_len), 11054 }), 11055 } }, 11056 }); 11057 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); 11058 11059 // Set up block body 11060 sema.air_instructions.items(.data)[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ 11061 .body_len = 1, 11062 }); 11063 sema.air_extra.appendAssumeCapacity(switch_br_inst); 11064 11065 return capture_block_inst.toRef(); 11066 }, 11067 .ErrorSet => { 11068 if (capture_byref) { 11069 const capture_src = raw_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none); 11070 return sema.fail( 11071 block, 11072 capture_src, 11073 "error set cannot be captured by reference", 11074 .{}, 11075 ); 11076 } 11077 11078 if (case_vals.len == 1) { 11079 const item_val = sema.resolveConstDefinedValue(block, .unneeded, case_vals[0], undefined) catch unreachable; 11080 const item_ty = try mod.singleErrorSetType(item_val.getErrorName(mod).unwrap().?); 11081 return sema.bitCast(block, item_ty, spa.operand, operand_src, null); 11082 } 11083 11084 var names: InferredErrorSet.NameMap = .{}; 11085 try names.ensureUnusedCapacity(sema.arena, case_vals.len); 11086 for (case_vals) |err| { 11087 const err_val = sema.resolveConstDefinedValue(block, .unneeded, err, undefined) catch unreachable; 11088 names.putAssumeCapacityNoClobber(err_val.getErrorName(mod).unwrap().?, {}); 11089 } 11090 const error_ty = try mod.errorSetFromUnsortedNames(names.keys()); 11091 return sema.bitCast(block, error_ty, spa.operand, operand_src, null); 11092 }, 11093 else => { 11094 // In this case the capture value is just the passed-through value 11095 // of the switch condition. 11096 if (capture_byref) { 11097 return spa.operand_ptr; 11098 } else { 11099 return spa.operand; 11100 } 11101 }, 11102 } 11103 } 11104 }; 11105 11106 fn switchCond( 11107 sema: *Sema, 11108 block: *Block, 11109 src: LazySrcLoc, 11110 operand: Air.Inst.Ref, 11111 ) CompileError!Air.Inst.Ref { 11112 const mod = sema.mod; 11113 const operand_ty = sema.typeOf(operand); 11114 switch (operand_ty.zigTypeTag(mod)) { 11115 .Type, 11116 .Void, 11117 .Bool, 11118 .Int, 11119 .Float, 11120 .ComptimeFloat, 11121 .ComptimeInt, 11122 .EnumLiteral, 11123 .Pointer, 11124 .Fn, 11125 .ErrorSet, 11126 .Enum, 11127 => { 11128 if (operand_ty.isSlice(mod)) { 11129 return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)}); 11130 } 11131 if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| { 11132 return Air.internedToRef(opv.toIntern()); 11133 } 11134 return operand; 11135 }, 11136 11137 .Union => { 11138 try sema.resolveTypeFields(operand_ty); 11139 const enum_ty = operand_ty.unionTagType(mod) orelse { 11140 const msg = msg: { 11141 const msg = try sema.errMsg(block, src, "switch on union with no attached enum", .{}); 11142 errdefer msg.destroy(sema.gpa); 11143 if (operand_ty.declSrcLocOrNull(mod)) |union_src| { 11144 try mod.errNoteNonLazy(union_src, msg, "consider 'union(enum)' here", .{}); 11145 } 11146 break :msg msg; 11147 }; 11148 return sema.failWithOwnedErrorMsg(block, msg); 11149 }; 11150 return sema.unionToTag(block, enum_ty, operand, src); 11151 }, 11152 11153 .ErrorUnion, 11154 .NoReturn, 11155 .Array, 11156 .Struct, 11157 .Undefined, 11158 .Null, 11159 .Optional, 11160 .Opaque, 11161 .Vector, 11162 .Frame, 11163 .AnyFrame, 11164 => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)}), 11165 } 11166 } 11167 11168 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc); 11169 11170 fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { 11171 const tracy = trace(@src()); 11172 defer tracy.end(); 11173 11174 const mod = sema.mod; 11175 const gpa = sema.gpa; 11176 const ip = &mod.intern_pool; 11177 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 11178 const src = inst_data.src(); 11179 const src_node_offset = inst_data.src_node; 11180 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; 11181 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset }; 11182 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); 11183 11184 const raw_operand_val: Air.Inst.Ref, const raw_operand_ptr: Air.Inst.Ref = blk: { 11185 const maybe_ptr = try sema.resolveInst(extra.data.operand); 11186 if (operand_is_ref) { 11187 const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src); 11188 break :blk .{ val, maybe_ptr }; 11189 } else { 11190 break :blk .{ maybe_ptr, undefined }; 11191 } 11192 }; 11193 11194 const operand = try sema.switchCond(block, operand_src, raw_operand_val); 11195 11196 // AstGen guarantees that the instruction immediately preceding 11197 // switch_block(_ref) is a dbg_stmt 11198 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1); 11199 11200 var header_extra_index: usize = extra.end; 11201 11202 const scalar_cases_len = extra.data.bits.scalar_cases_len; 11203 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: { 11204 const multi_cases_len = sema.code.extra[header_extra_index]; 11205 header_extra_index += 1; 11206 break :blk multi_cases_len; 11207 } else 0; 11208 11209 const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: { 11210 const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]); 11211 header_extra_index += 1; 11212 // SwitchProngAnalysis wants inst_map to have space for the tag capture. 11213 // Note that the normal capture is referred to via the switch block 11214 // index, which there is already necessarily space for. 11215 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst}); 11216 break :blk tag_capture_inst; 11217 } else undefined; 11218 11219 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); 11220 defer case_vals.deinit(gpa); 11221 11222 const Special = struct { 11223 body: []const Zir.Inst.Index, 11224 end: usize, 11225 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 11226 is_inline: bool, 11227 has_tag_capture: bool, 11228 }; 11229 11230 const special_prong = extra.data.bits.specialProng(); 11231 const special: Special = switch (special_prong) { 11232 .none => .{ 11233 .body = &.{}, 11234 .end = header_extra_index, 11235 .capture = .none, 11236 .is_inline = false, 11237 .has_tag_capture = false, 11238 }, 11239 .under, .@"else" => blk: { 11240 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]); 11241 const extra_body_start = header_extra_index + 1; 11242 break :blk .{ 11243 .body = sema.code.bodySlice(extra_body_start, info.body_len), 11244 .end = extra_body_start + info.body_len, 11245 .capture = info.capture, 11246 .is_inline = info.is_inline, 11247 .has_tag_capture = info.has_tag_capture, 11248 }; 11249 }, 11250 }; 11251 11252 const maybe_union_ty = sema.typeOf(raw_operand_val); 11253 const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union; 11254 11255 // Duplicate checking variables later also used for `inline else`. 11256 var seen_enum_fields: []?Module.SwitchProngSrc = &.{}; 11257 var seen_errors = SwitchErrorSet.init(gpa); 11258 var range_set = RangeSet.init(gpa, mod); 11259 var true_count: u8 = 0; 11260 var false_count: u8 = 0; 11261 11262 defer { 11263 range_set.deinit(); 11264 gpa.free(seen_enum_fields); 11265 seen_errors.deinit(); 11266 } 11267 11268 var empty_enum = false; 11269 11270 const operand_ty = sema.typeOf(operand); 11271 const err_set = operand_ty.zigTypeTag(mod) == .ErrorSet; 11272 11273 var else_error_ty: ?Type = null; 11274 11275 // Validate usage of '_' prongs. 11276 if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) { 11277 const msg = msg: { 11278 const msg = try sema.errMsg( 11279 block, 11280 src, 11281 "'_' prong only allowed when switching on non-exhaustive enums", 11282 .{}, 11283 ); 11284 errdefer msg.destroy(gpa); 11285 try sema.errNote( 11286 block, 11287 special_prong_src, 11288 msg, 11289 "'_' prong here", 11290 .{}, 11291 ); 11292 try sema.errNote( 11293 block, 11294 src, 11295 msg, 11296 "consider using 'else'", 11297 .{}, 11298 ); 11299 break :msg msg; 11300 }; 11301 return sema.failWithOwnedErrorMsg(block, msg); 11302 } 11303 11304 // Validate for duplicate items, missing else prong, and invalid range. 11305 switch (operand_ty.zigTypeTag(mod)) { 11306 .Union => unreachable, // handled in zirSwitchCond 11307 .Enum => { 11308 seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount(mod)); 11309 empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(mod); 11310 @memset(seen_enum_fields, null); 11311 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. 11312 11313 var extra_index: usize = special.end; 11314 { 11315 var scalar_i: u32 = 0; 11316 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11317 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11318 extra_index += 1; 11319 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11320 extra_index += 1 + info.body_len; 11321 11322 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( 11323 block, 11324 seen_enum_fields, 11325 &range_set, 11326 item_ref, 11327 operand_ty, 11328 src_node_offset, 11329 .{ .scalar = scalar_i }, 11330 )); 11331 } 11332 } 11333 { 11334 var multi_i: u32 = 0; 11335 while (multi_i < multi_cases_len) : (multi_i += 1) { 11336 const items_len = sema.code.extra[extra_index]; 11337 extra_index += 1; 11338 const ranges_len = sema.code.extra[extra_index]; 11339 extra_index += 1; 11340 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11341 extra_index += 1; 11342 const items = sema.code.refSlice(extra_index, items_len); 11343 extra_index += items_len + info.body_len; 11344 11345 try case_vals.ensureUnusedCapacity(gpa, items.len); 11346 for (items, 0..) |item_ref, item_i| { 11347 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( 11348 block, 11349 seen_enum_fields, 11350 &range_set, 11351 item_ref, 11352 operand_ty, 11353 src_node_offset, 11354 .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } }, 11355 )); 11356 } 11357 11358 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 11359 } 11360 } 11361 const all_tags_handled = for (seen_enum_fields) |seen_src| { 11362 if (seen_src == null) break false; 11363 } else true; 11364 11365 if (special_prong == .@"else") { 11366 if (all_tags_handled and !operand_ty.isNonexhaustiveEnum(mod)) return sema.fail( 11367 block, 11368 special_prong_src, 11369 "unreachable else prong; all cases already handled", 11370 .{}, 11371 ); 11372 } else if (!all_tags_handled) { 11373 const msg = msg: { 11374 const msg = try sema.errMsg( 11375 block, 11376 src, 11377 "switch must handle all possibilities", 11378 .{}, 11379 ); 11380 errdefer msg.destroy(sema.gpa); 11381 for (seen_enum_fields, 0..) |seen_src, i| { 11382 if (seen_src != null) continue; 11383 11384 const field_name = operand_ty.enumFieldName(i, mod); 11385 try sema.addFieldErrNote( 11386 operand_ty, 11387 i, 11388 msg, 11389 "unhandled enumeration value: '{}'", 11390 .{field_name.fmt(&mod.intern_pool)}, 11391 ); 11392 } 11393 try mod.errNoteNonLazy( 11394 operand_ty.declSrcLoc(mod), 11395 msg, 11396 "enum '{}' declared here", 11397 .{operand_ty.fmt(mod)}, 11398 ); 11399 break :msg msg; 11400 }; 11401 return sema.failWithOwnedErrorMsg(block, msg); 11402 } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum(mod) and !union_originally) { 11403 return sema.fail( 11404 block, 11405 src, 11406 "switch on non-exhaustive enum must include 'else' or '_' prong", 11407 .{}, 11408 ); 11409 } 11410 }, 11411 .ErrorSet => { 11412 var extra_index: usize = special.end; 11413 { 11414 var scalar_i: u32 = 0; 11415 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11416 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11417 extra_index += 1; 11418 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11419 extra_index += 1 + info.body_len; 11420 11421 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( 11422 block, 11423 &seen_errors, 11424 item_ref, 11425 operand_ty, 11426 src_node_offset, 11427 .{ .scalar = scalar_i }, 11428 )); 11429 } 11430 } 11431 { 11432 var multi_i: u32 = 0; 11433 while (multi_i < multi_cases_len) : (multi_i += 1) { 11434 const items_len = sema.code.extra[extra_index]; 11435 extra_index += 1; 11436 const ranges_len = sema.code.extra[extra_index]; 11437 extra_index += 1; 11438 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11439 extra_index += 1; 11440 const items = sema.code.refSlice(extra_index, items_len); 11441 extra_index += items_len + info.body_len; 11442 11443 try case_vals.ensureUnusedCapacity(gpa, items.len); 11444 for (items, 0..) |item_ref, item_i| { 11445 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( 11446 block, 11447 &seen_errors, 11448 item_ref, 11449 operand_ty, 11450 src_node_offset, 11451 .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } }, 11452 )); 11453 } 11454 11455 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 11456 } 11457 } 11458 11459 switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) { 11460 .anyerror_type => { 11461 if (special_prong != .@"else") { 11462 return sema.fail( 11463 block, 11464 src, 11465 "else prong required when switching on type 'anyerror'", 11466 .{}, 11467 ); 11468 } 11469 else_error_ty = Type.anyerror; 11470 }, 11471 else => |err_set_ty_index| else_validation: { 11472 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names; 11473 var maybe_msg: ?*Module.ErrorMsg = null; 11474 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); 11475 11476 for (error_names.get(ip)) |error_name| { 11477 if (!seen_errors.contains(error_name) and special_prong != .@"else") { 11478 const msg = maybe_msg orelse blk: { 11479 maybe_msg = try sema.errMsg( 11480 block, 11481 src, 11482 "switch must handle all possibilities", 11483 .{}, 11484 ); 11485 break :blk maybe_msg.?; 11486 }; 11487 11488 try sema.errNote( 11489 block, 11490 src, 11491 msg, 11492 "unhandled error value: 'error.{}'", 11493 .{error_name.fmt(ip)}, 11494 ); 11495 } 11496 } 11497 11498 if (maybe_msg) |msg| { 11499 maybe_msg = null; 11500 try sema.addDeclaredHereNote(msg, operand_ty); 11501 return sema.failWithOwnedErrorMsg(block, msg); 11502 } 11503 11504 if (special_prong == .@"else" and 11505 seen_errors.count() == error_names.len) 11506 { 11507 // In order to enable common patterns for generic code allow simple else bodies 11508 // else => unreachable, 11509 // else => return, 11510 // else => |e| return e, 11511 // even if all the possible errors were already handled. 11512 const tags = sema.code.instructions.items(.tag); 11513 for (special.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) { 11514 .dbg_block_begin, 11515 .dbg_block_end, 11516 .dbg_stmt, 11517 .dbg_var_val, 11518 .ret_type, 11519 .as_node, 11520 .ret_node, 11521 .@"unreachable", 11522 .@"defer", 11523 .defer_err_code, 11524 .err_union_code, 11525 .ret_err_value_code, 11526 .restore_err_ret_index, 11527 .is_non_err, 11528 .ret_is_non_err, 11529 .condbr, 11530 => {}, 11531 else => break, 11532 } else break :else_validation; 11533 11534 return sema.fail( 11535 block, 11536 special_prong_src, 11537 "unreachable else prong; all cases already handled", 11538 .{}, 11539 ); 11540 } 11541 11542 var names: InferredErrorSet.NameMap = .{}; 11543 try names.ensureUnusedCapacity(sema.arena, error_names.len); 11544 for (error_names.get(ip)) |error_name| { 11545 if (seen_errors.contains(error_name)) continue; 11546 11547 names.putAssumeCapacityNoClobber(error_name, {}); 11548 } 11549 // No need to keep the hash map metadata correct; here we 11550 // extract the (sorted) keys only. 11551 else_error_ty = try mod.errorSetFromUnsortedNames(names.keys()); 11552 }, 11553 } 11554 }, 11555 .Int, .ComptimeInt => { 11556 var extra_index: usize = special.end; 11557 { 11558 var scalar_i: u32 = 0; 11559 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11560 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11561 extra_index += 1; 11562 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11563 extra_index += 1 + info.body_len; 11564 11565 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( 11566 block, 11567 &range_set, 11568 item_ref, 11569 operand_ty, 11570 src_node_offset, 11571 .{ .scalar = scalar_i }, 11572 )); 11573 } 11574 } 11575 { 11576 var multi_i: u32 = 0; 11577 while (multi_i < multi_cases_len) : (multi_i += 1) { 11578 const items_len = sema.code.extra[extra_index]; 11579 extra_index += 1; 11580 const ranges_len = sema.code.extra[extra_index]; 11581 extra_index += 1; 11582 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11583 extra_index += 1; 11584 const items = sema.code.refSlice(extra_index, items_len); 11585 extra_index += items_len; 11586 11587 try case_vals.ensureUnusedCapacity(gpa, items.len); 11588 for (items, 0..) |item_ref, item_i| { 11589 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( 11590 block, 11591 &range_set, 11592 item_ref, 11593 operand_ty, 11594 src_node_offset, 11595 .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } }, 11596 )); 11597 } 11598 11599 try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len); 11600 var range_i: u32 = 0; 11601 while (range_i < ranges_len) : (range_i += 1) { 11602 const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11603 extra_index += 1; 11604 const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11605 extra_index += 1; 11606 11607 const vals = try sema.validateSwitchRange( 11608 block, 11609 &range_set, 11610 item_first, 11611 item_last, 11612 operand_ty, 11613 src_node_offset, 11614 .{ .range = .{ .prong = multi_i, .item = range_i } }, 11615 ); 11616 case_vals.appendAssumeCapacity(vals[0]); 11617 case_vals.appendAssumeCapacity(vals[1]); 11618 } 11619 11620 extra_index += info.body_len; 11621 } 11622 } 11623 11624 check_range: { 11625 if (operand_ty.zigTypeTag(mod) == .Int) { 11626 const min_int = try operand_ty.minInt(mod, operand_ty); 11627 const max_int = try operand_ty.maxInt(mod, operand_ty); 11628 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) { 11629 if (special_prong == .@"else") { 11630 return sema.fail( 11631 block, 11632 special_prong_src, 11633 "unreachable else prong; all cases already handled", 11634 .{}, 11635 ); 11636 } 11637 break :check_range; 11638 } 11639 } 11640 if (special_prong != .@"else") { 11641 return sema.fail( 11642 block, 11643 src, 11644 "switch must handle all possibilities", 11645 .{}, 11646 ); 11647 } 11648 } 11649 }, 11650 .Bool => { 11651 var extra_index: usize = special.end; 11652 { 11653 var scalar_i: u32 = 0; 11654 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11655 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11656 extra_index += 1; 11657 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11658 extra_index += 1 + info.body_len; 11659 11660 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( 11661 block, 11662 &true_count, 11663 &false_count, 11664 item_ref, 11665 src_node_offset, 11666 .{ .scalar = scalar_i }, 11667 )); 11668 } 11669 } 11670 { 11671 var multi_i: u32 = 0; 11672 while (multi_i < multi_cases_len) : (multi_i += 1) { 11673 const items_len = sema.code.extra[extra_index]; 11674 extra_index += 1; 11675 const ranges_len = sema.code.extra[extra_index]; 11676 extra_index += 1; 11677 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11678 extra_index += 1; 11679 const items = sema.code.refSlice(extra_index, items_len); 11680 extra_index += items_len + info.body_len; 11681 11682 try case_vals.ensureUnusedCapacity(gpa, items.len); 11683 for (items, 0..) |item_ref, item_i| { 11684 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( 11685 block, 11686 &true_count, 11687 &false_count, 11688 item_ref, 11689 src_node_offset, 11690 .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } }, 11691 )); 11692 } 11693 11694 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 11695 } 11696 } 11697 switch (special_prong) { 11698 .@"else" => { 11699 if (true_count + false_count == 2) { 11700 return sema.fail( 11701 block, 11702 special_prong_src, 11703 "unreachable else prong; all cases already handled", 11704 .{}, 11705 ); 11706 } 11707 }, 11708 .under, .none => { 11709 if (true_count + false_count < 2) { 11710 return sema.fail( 11711 block, 11712 src, 11713 "switch must handle all possibilities", 11714 .{}, 11715 ); 11716 } 11717 }, 11718 } 11719 }, 11720 .EnumLiteral, .Void, .Fn, .Pointer, .Type => { 11721 if (special_prong != .@"else") { 11722 return sema.fail( 11723 block, 11724 src, 11725 "else prong required when switching on type '{}'", 11726 .{operand_ty.fmt(mod)}, 11727 ); 11728 } 11729 11730 var seen_values = ValueSrcMap{}; 11731 defer seen_values.deinit(gpa); 11732 11733 var extra_index: usize = special.end; 11734 { 11735 var scalar_i: u32 = 0; 11736 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11737 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 11738 extra_index += 1; 11739 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11740 extra_index += 1; 11741 extra_index += info.body_len; 11742 11743 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( 11744 block, 11745 &seen_values, 11746 item_ref, 11747 operand_ty, 11748 src_node_offset, 11749 .{ .scalar = scalar_i }, 11750 )); 11751 } 11752 } 11753 { 11754 var multi_i: u32 = 0; 11755 while (multi_i < multi_cases_len) : (multi_i += 1) { 11756 const items_len = sema.code.extra[extra_index]; 11757 extra_index += 1; 11758 const ranges_len = sema.code.extra[extra_index]; 11759 extra_index += 1; 11760 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11761 extra_index += 1; 11762 const items = sema.code.refSlice(extra_index, items_len); 11763 extra_index += items_len + info.body_len; 11764 11765 try case_vals.ensureUnusedCapacity(gpa, items.len); 11766 for (items, 0..) |item_ref, item_i| { 11767 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( 11768 block, 11769 &seen_values, 11770 item_ref, 11771 operand_ty, 11772 src_node_offset, 11773 .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } }, 11774 )); 11775 } 11776 11777 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 11778 } 11779 } 11780 }, 11781 11782 .ErrorUnion, 11783 .NoReturn, 11784 .Array, 11785 .Struct, 11786 .Undefined, 11787 .Null, 11788 .Optional, 11789 .Opaque, 11790 .Vector, 11791 .Frame, 11792 .AnyFrame, 11793 .ComptimeFloat, 11794 .Float, 11795 => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{ 11796 operand_ty.fmt(mod), 11797 }), 11798 } 11799 11800 const spa: SwitchProngAnalysis = .{ 11801 .sema = sema, 11802 .parent_block = block, 11803 .operand = raw_operand_val, 11804 .operand_ptr = raw_operand_ptr, 11805 .cond = operand, 11806 .else_error_ty = else_error_ty, 11807 .switch_block_inst = inst, 11808 .tag_capture_inst = tag_capture_inst, 11809 }; 11810 11811 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 11812 try sema.air_instructions.append(gpa, .{ 11813 .tag = .block, 11814 .data = undefined, 11815 }); 11816 var label: Block.Label = .{ 11817 .zir_block = inst, 11818 .merges = .{ 11819 .src_locs = .{}, 11820 .results = .{}, 11821 .br_list = .{}, 11822 .block_inst = block_inst, 11823 }, 11824 }; 11825 11826 var child_block: Block = .{ 11827 .parent = block, 11828 .sema = sema, 11829 .src_decl = block.src_decl, 11830 .namespace = block.namespace, 11831 .wip_capture_scope = block.wip_capture_scope, 11832 .instructions = .{}, 11833 .label = &label, 11834 .inlining = block.inlining, 11835 .is_comptime = block.is_comptime, 11836 .comptime_reason = block.comptime_reason, 11837 .is_typeof = block.is_typeof, 11838 .c_import_buf = block.c_import_buf, 11839 .runtime_cond = block.runtime_cond, 11840 .runtime_loop = block.runtime_loop, 11841 .runtime_index = block.runtime_index, 11842 .want_safety = block.want_safety, 11843 .error_return_trace_index = block.error_return_trace_index, 11844 }; 11845 const merges = &child_block.label.?.merges; 11846 defer child_block.instructions.deinit(gpa); 11847 defer merges.deinit(gpa); 11848 11849 if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| { 11850 const resolved_operand_val = try sema.resolveLazyValue(operand_val); 11851 var extra_index: usize = special.end; 11852 { 11853 var scalar_i: usize = 0; 11854 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11855 extra_index += 1; 11856 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11857 extra_index += 1; 11858 const body = sema.code.bodySlice(extra_index, info.body_len); 11859 extra_index += info.body_len; 11860 11861 const item = case_vals.items[scalar_i]; 11862 const item_val = sema.resolveConstDefinedValue(&child_block, .unneeded, item, undefined) catch unreachable; 11863 if (operand_val.eql(item_val, operand_ty, sema.mod)) { 11864 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); 11865 return spa.resolveProngComptime( 11866 &child_block, 11867 .normal, 11868 body, 11869 info.capture, 11870 .{ .scalar_capture = @intCast(scalar_i) }, 11871 &.{item}, 11872 if (info.is_inline) operand else .none, 11873 info.has_tag_capture, 11874 merges, 11875 ); 11876 } 11877 } 11878 } 11879 { 11880 var multi_i: usize = 0; 11881 var case_val_idx: usize = scalar_cases_len; 11882 while (multi_i < multi_cases_len) : (multi_i += 1) { 11883 const items_len = sema.code.extra[extra_index]; 11884 extra_index += 1; 11885 const ranges_len = sema.code.extra[extra_index]; 11886 extra_index += 1; 11887 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 11888 extra_index += 1 + items_len; 11889 const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len); 11890 11891 const items = case_vals.items[case_val_idx..][0..items_len]; 11892 case_val_idx += items_len; 11893 11894 for (items) |item| { 11895 // Validation above ensured these will succeed. 11896 const item_val = sema.resolveConstDefinedValue(&child_block, .unneeded, item, undefined) catch unreachable; 11897 if (operand_val.eql(item_val, operand_ty, sema.mod)) { 11898 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); 11899 return spa.resolveProngComptime( 11900 &child_block, 11901 .normal, 11902 body, 11903 info.capture, 11904 .{ .multi_capture = @intCast(multi_i) }, 11905 items, 11906 if (info.is_inline) operand else .none, 11907 info.has_tag_capture, 11908 merges, 11909 ); 11910 } 11911 } 11912 11913 var range_i: usize = 0; 11914 while (range_i < ranges_len) : (range_i += 1) { 11915 const range_items = case_vals.items[case_val_idx..][0..2]; 11916 extra_index += 2; 11917 case_val_idx += 2; 11918 11919 // Validation above ensured these will succeed. 11920 const first_val = sema.resolveConstDefinedValue(&child_block, .unneeded, range_items[0], undefined) catch unreachable; 11921 const last_val = sema.resolveConstDefinedValue(&child_block, .unneeded, range_items[1], undefined) catch unreachable; 11922 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and 11923 (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty))) 11924 { 11925 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); 11926 return spa.resolveProngComptime( 11927 &child_block, 11928 .normal, 11929 body, 11930 info.capture, 11931 .{ .multi_capture = @intCast(multi_i) }, 11932 undefined, // case_vals may be undefined for ranges 11933 if (info.is_inline) operand else .none, 11934 info.has_tag_capture, 11935 merges, 11936 ); 11937 } 11938 } 11939 11940 extra_index += info.body_len; 11941 } 11942 } 11943 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand); 11944 if (empty_enum) { 11945 return .void_value; 11946 } 11947 11948 return spa.resolveProngComptime( 11949 &child_block, 11950 .special, 11951 special.body, 11952 special.capture, 11953 .special_capture, 11954 undefined, // case_vals may be undefined for special prongs 11955 if (special.is_inline) operand else .none, 11956 special.has_tag_capture, 11957 merges, 11958 ); 11959 } 11960 11961 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) { 11962 if (empty_enum) { 11963 return .void_value; 11964 } 11965 if (special_prong == .none) { 11966 return sema.fail(block, src, "switch must handle all possibilities", .{}); 11967 } 11968 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand, operand_src)) { 11969 return .unreachable_value; 11970 } 11971 if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and 11972 (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) 11973 { 11974 try sema.zirDbgStmt(block, cond_dbg_node_index); 11975 const ok = try block.addUnOp(.is_named_enum_value, operand); 11976 try sema.addSafetyCheck(block, src, ok, .corrupt_switch); 11977 } 11978 11979 return spa.resolveProngComptime( 11980 &child_block, 11981 .special, 11982 special.body, 11983 special.capture, 11984 .special_capture, 11985 undefined, // case_vals may be undefined for special prongs 11986 .none, 11987 false, 11988 merges, 11989 ); 11990 } 11991 11992 if (child_block.is_comptime) { 11993 _ = try sema.resolveConstDefinedValue(&child_block, operand_src, operand, .{ 11994 .needed_comptime_reason = "condition in comptime switch must be comptime-known", 11995 .block_comptime_reason = child_block.comptime_reason, 11996 }); 11997 unreachable; 11998 } 11999 12000 const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * 12001 @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2; 12002 var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra); 12003 defer cases_extra.deinit(gpa); 12004 12005 var case_block = child_block.makeSubBlock(); 12006 case_block.runtime_loop = null; 12007 case_block.runtime_cond = operand_src; 12008 case_block.runtime_index.increment(); 12009 defer case_block.instructions.deinit(gpa); 12010 12011 var extra_index: usize = special.end; 12012 12013 var scalar_i: usize = 0; 12014 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 12015 extra_index += 1; 12016 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12017 extra_index += 1; 12018 const body = sema.code.bodySlice(extra_index, info.body_len); 12019 extra_index += info.body_len; 12020 12021 case_block.instructions.shrinkRetainingCapacity(0); 12022 case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope); 12023 12024 const item = case_vals.items[scalar_i]; 12025 // `item` is already guaranteed to be constant known. 12026 12027 const analyze_body = if (union_originally) blk: { 12028 const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable; 12029 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable; 12030 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; 12031 break :blk field_ty.zigTypeTag(mod) != .NoReturn; 12032 } else true; 12033 12034 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) { 12035 // nothing to do here 12036 } else if (analyze_body) { 12037 try spa.analyzeProngRuntime( 12038 &case_block, 12039 .normal, 12040 body, 12041 info.capture, 12042 .{ .scalar_capture = @intCast(scalar_i) }, 12043 &.{item}, 12044 if (info.is_inline) item else .none, 12045 info.has_tag_capture, 12046 ); 12047 } else { 12048 _ = try case_block.addNoOp(.unreach); 12049 } 12050 12051 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12052 cases_extra.appendAssumeCapacity(1); // items_len 12053 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12054 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12055 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12056 } 12057 12058 var is_first = true; 12059 var prev_cond_br: Air.Inst.Index = undefined; 12060 var first_else_body: []const Air.Inst.Index = &.{}; 12061 defer gpa.free(first_else_body); 12062 var prev_then_body: []const Air.Inst.Index = &.{}; 12063 defer gpa.free(prev_then_body); 12064 12065 var cases_len = scalar_cases_len; 12066 var case_val_idx: usize = scalar_cases_len; 12067 var multi_i: u32 = 0; 12068 while (multi_i < multi_cases_len) : (multi_i += 1) { 12069 const items_len = sema.code.extra[extra_index]; 12070 extra_index += 1; 12071 const ranges_len = sema.code.extra[extra_index]; 12072 extra_index += 1; 12073 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); 12074 extra_index += 1 + items_len; 12075 12076 const items = case_vals.items[case_val_idx..][0..items_len]; 12077 case_val_idx += items_len; 12078 12079 case_block.instructions.shrinkRetainingCapacity(0); 12080 case_block.wip_capture_scope = child_block.wip_capture_scope; 12081 12082 // Generate all possible cases as scalar prongs. 12083 if (info.is_inline) { 12084 const body_start = extra_index + 2 * ranges_len; 12085 const body = sema.code.bodySlice(body_start, info.body_len); 12086 var emit_bb = false; 12087 12088 var range_i: u32 = 0; 12089 while (range_i < ranges_len) : (range_i += 1) { 12090 const range_items = case_vals.items[case_val_idx..][0..2]; 12091 extra_index += 2; 12092 case_val_idx += 2; 12093 12094 const item_first_ref = range_items[0]; 12095 const item_last_ref = range_items[1]; 12096 12097 var item = sema.resolveConstDefinedValue(block, .unneeded, item_first_ref, undefined) catch unreachable; 12098 const item_last = sema.resolveConstDefinedValue(block, .unneeded, item_last_ref, undefined) catch unreachable; 12099 12100 while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({ 12101 // Previous validation has resolved any possible lazy values. 12102 item = sema.intAddScalar(item, try mod.intValue(operand_ty, 1), operand_ty) catch |err| switch (err) { 12103 error.Overflow => unreachable, 12104 else => |e| return e, 12105 }; 12106 }) { 12107 cases_len += 1; 12108 12109 const item_ref = Air.internedToRef(item.toIntern()); 12110 12111 case_block.instructions.shrinkRetainingCapacity(0); 12112 case_block.wip_capture_scope = child_block.wip_capture_scope; 12113 12114 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { 12115 error.NeededSourceLocation => { 12116 const case_src = Module.SwitchProngSrc{ 12117 .range = .{ .prong = multi_i, .item = range_i }, 12118 }; 12119 const decl = mod.declPtr(case_block.src_decl); 12120 try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none)); 12121 unreachable; 12122 }, 12123 else => return err, 12124 }; 12125 emit_bb = true; 12126 12127 try spa.analyzeProngRuntime( 12128 &case_block, 12129 .normal, 12130 body, 12131 info.capture, 12132 .{ .multi_capture = multi_i }, 12133 undefined, // case_vals may be undefined for ranges 12134 item_ref, 12135 info.has_tag_capture, 12136 ); 12137 12138 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12139 cases_extra.appendAssumeCapacity(1); // items_len 12140 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12141 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12142 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12143 12144 if (item.compareScalar(.eq, item_last, operand_ty, mod)) break; 12145 } 12146 } 12147 12148 for (items, 0..) |item, item_i| { 12149 cases_len += 1; 12150 12151 case_block.instructions.shrinkRetainingCapacity(0); 12152 case_block.wip_capture_scope = child_block.wip_capture_scope; 12153 12154 const analyze_body = if (union_originally) blk: { 12155 const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable; 12156 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; 12157 break :blk field_ty.zigTypeTag(mod) != .NoReturn; 12158 } else true; 12159 12160 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { 12161 error.NeededSourceLocation => { 12162 const case_src = Module.SwitchProngSrc{ 12163 .multi = .{ .prong = multi_i, .item = @intCast(item_i) }, 12164 }; 12165 const decl = mod.declPtr(case_block.src_decl); 12166 try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none)); 12167 unreachable; 12168 }, 12169 else => return err, 12170 }; 12171 emit_bb = true; 12172 12173 if (analyze_body) { 12174 try spa.analyzeProngRuntime( 12175 &case_block, 12176 .normal, 12177 body, 12178 info.capture, 12179 .{ .multi_capture = multi_i }, 12180 &.{item}, 12181 item, 12182 info.has_tag_capture, 12183 ); 12184 } else { 12185 _ = try case_block.addNoOp(.unreach); 12186 } 12187 12188 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12189 cases_extra.appendAssumeCapacity(1); // items_len 12190 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12191 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12192 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12193 } 12194 12195 extra_index += info.body_len; 12196 continue; 12197 } 12198 12199 var any_ok: Air.Inst.Ref = .none; 12200 12201 // If there are any ranges, we have to put all the items into the 12202 // else prong. Otherwise, we can take advantage of multiple items 12203 // mapping to the same body. 12204 if (ranges_len == 0) { 12205 cases_len += 1; 12206 12207 const analyze_body = if (union_originally) 12208 for (items) |item| { 12209 const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable; 12210 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; 12211 if (field_ty.zigTypeTag(mod) != .NoReturn) break true; 12212 } else false 12213 else 12214 true; 12215 12216 const body = sema.code.bodySlice(extra_index, info.body_len); 12217 extra_index += info.body_len; 12218 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) { 12219 // nothing to do here 12220 } else if (analyze_body) { 12221 try spa.analyzeProngRuntime( 12222 &case_block, 12223 .normal, 12224 body, 12225 info.capture, 12226 .{ .multi_capture = multi_i }, 12227 items, 12228 .none, 12229 false, 12230 ); 12231 } else { 12232 _ = try case_block.addNoOp(.unreach); 12233 } 12234 12235 try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len + 12236 case_block.instructions.items.len); 12237 12238 cases_extra.appendAssumeCapacity(@intCast(items.len)); 12239 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12240 12241 for (items) |item| { 12242 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 12243 } 12244 12245 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12246 } else { 12247 for (items) |item| { 12248 const cmp_ok = try case_block.addBinOp(if (case_block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, item); 12249 if (any_ok != .none) { 12250 any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok); 12251 } else { 12252 any_ok = cmp_ok; 12253 } 12254 } 12255 12256 var range_i: usize = 0; 12257 while (range_i < ranges_len) : (range_i += 1) { 12258 const range_items = case_vals.items[case_val_idx..][0..2]; 12259 extra_index += 2; 12260 case_val_idx += 2; 12261 12262 const item_first = range_items[0]; 12263 const item_last = range_items[1]; 12264 12265 // operand >= first and operand <= last 12266 const range_first_ok = try case_block.addBinOp( 12267 if (case_block.float_mode == .Optimized) .cmp_gte_optimized else .cmp_gte, 12268 operand, 12269 item_first, 12270 ); 12271 const range_last_ok = try case_block.addBinOp( 12272 if (case_block.float_mode == .Optimized) .cmp_lte_optimized else .cmp_lte, 12273 operand, 12274 item_last, 12275 ); 12276 const range_ok = try case_block.addBinOp( 12277 .bool_and, 12278 range_first_ok, 12279 range_last_ok, 12280 ); 12281 if (any_ok != .none) { 12282 any_ok = try case_block.addBinOp(.bool_or, any_ok, range_ok); 12283 } else { 12284 any_ok = range_ok; 12285 } 12286 } 12287 12288 const new_cond_br = try case_block.addInstAsIndex(.{ .tag = .cond_br, .data = .{ 12289 .pl_op = .{ 12290 .operand = any_ok, 12291 .payload = undefined, 12292 }, 12293 } }); 12294 var cond_body = try case_block.instructions.toOwnedSlice(gpa); 12295 defer gpa.free(cond_body); 12296 12297 case_block.instructions.shrinkRetainingCapacity(0); 12298 case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope); 12299 12300 const body = sema.code.bodySlice(extra_index, info.body_len); 12301 extra_index += info.body_len; 12302 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) { 12303 // nothing to do here 12304 } else { 12305 try spa.analyzeProngRuntime( 12306 &case_block, 12307 .normal, 12308 body, 12309 info.capture, 12310 .{ .multi_capture = multi_i }, 12311 items, 12312 .none, 12313 false, 12314 ); 12315 } 12316 12317 if (is_first) { 12318 is_first = false; 12319 first_else_body = cond_body; 12320 cond_body = &.{}; 12321 } else { 12322 try sema.air_extra.ensureUnusedCapacity( 12323 gpa, 12324 @typeInfo(Air.CondBr).Struct.fields.len + prev_then_body.len + cond_body.len, 12325 ); 12326 12327 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = 12328 sema.addExtraAssumeCapacity(Air.CondBr{ 12329 .then_body_len = @intCast(prev_then_body.len), 12330 .else_body_len = @intCast(cond_body.len), 12331 }); 12332 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body)); 12333 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cond_body)); 12334 } 12335 gpa.free(prev_then_body); 12336 prev_then_body = try case_block.instructions.toOwnedSlice(gpa); 12337 prev_cond_br = new_cond_br; 12338 } 12339 } 12340 12341 var final_else_body: []const Air.Inst.Index = &.{}; 12342 if (special.body.len != 0 or !is_first or case_block.wantSafety()) { 12343 var emit_bb = false; 12344 if (special.is_inline) switch (operand_ty.zigTypeTag(mod)) { 12345 .Enum => { 12346 if (operand_ty.isNonexhaustiveEnum(mod) and !union_originally) { 12347 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ 12348 operand_ty.fmt(mod), 12349 }); 12350 } 12351 for (seen_enum_fields, 0..) |f, i| { 12352 if (f != null) continue; 12353 cases_len += 1; 12354 12355 const item_val = try mod.enumValueFieldIndex(operand_ty, @intCast(i)); 12356 const item_ref = Air.internedToRef(item_val.toIntern()); 12357 12358 case_block.instructions.shrinkRetainingCapacity(0); 12359 case_block.wip_capture_scope = child_block.wip_capture_scope; 12360 12361 const analyze_body = if (union_originally) blk: { 12362 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; 12363 break :blk field_ty.zigTypeTag(mod) != .NoReturn; 12364 } else true; 12365 12366 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12367 emit_bb = true; 12368 12369 if (analyze_body) { 12370 try spa.analyzeProngRuntime( 12371 &case_block, 12372 .special, 12373 special.body, 12374 special.capture, 12375 .special_capture, 12376 &.{item_ref}, 12377 item_ref, 12378 special.has_tag_capture, 12379 ); 12380 } else { 12381 _ = try case_block.addNoOp(.unreach); 12382 } 12383 12384 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12385 cases_extra.appendAssumeCapacity(1); // items_len 12386 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12387 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12388 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12389 } 12390 }, 12391 .ErrorSet => { 12392 if (operand_ty.isAnyError(mod)) { 12393 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ 12394 operand_ty.fmt(mod), 12395 }); 12396 } 12397 for (0..operand_ty.errorSetNames(mod).len) |i| { 12398 const error_name = operand_ty.errorSetNames(mod)[i]; 12399 if (seen_errors.contains(error_name)) continue; 12400 cases_len += 1; 12401 12402 const item_val = try mod.intern(.{ .err = .{ 12403 .ty = operand_ty.toIntern(), 12404 .name = error_name, 12405 } }); 12406 const item_ref = Air.internedToRef(item_val); 12407 12408 case_block.instructions.shrinkRetainingCapacity(0); 12409 case_block.wip_capture_scope = child_block.wip_capture_scope; 12410 12411 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12412 emit_bb = true; 12413 12414 try spa.analyzeProngRuntime( 12415 &case_block, 12416 .special, 12417 special.body, 12418 special.capture, 12419 .special_capture, 12420 &.{item_ref}, 12421 item_ref, 12422 special.has_tag_capture, 12423 ); 12424 12425 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12426 cases_extra.appendAssumeCapacity(1); // items_len 12427 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12428 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12429 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12430 } 12431 }, 12432 .Int => { 12433 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set); 12434 while (try it.next()) |cur| { 12435 cases_len += 1; 12436 12437 const item_ref = Air.internedToRef(cur); 12438 12439 case_block.instructions.shrinkRetainingCapacity(0); 12440 case_block.wip_capture_scope = child_block.wip_capture_scope; 12441 12442 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12443 emit_bb = true; 12444 12445 try spa.analyzeProngRuntime( 12446 &case_block, 12447 .special, 12448 special.body, 12449 special.capture, 12450 .special_capture, 12451 &.{item_ref}, 12452 item_ref, 12453 special.has_tag_capture, 12454 ); 12455 12456 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12457 cases_extra.appendAssumeCapacity(1); // items_len 12458 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12459 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12460 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12461 } 12462 }, 12463 .Bool => { 12464 if (true_count == 0) { 12465 cases_len += 1; 12466 12467 case_block.instructions.shrinkRetainingCapacity(0); 12468 case_block.wip_capture_scope = child_block.wip_capture_scope; 12469 12470 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12471 emit_bb = true; 12472 12473 try spa.analyzeProngRuntime( 12474 &case_block, 12475 .special, 12476 special.body, 12477 special.capture, 12478 .special_capture, 12479 &.{.bool_true}, 12480 .bool_true, 12481 special.has_tag_capture, 12482 ); 12483 12484 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12485 cases_extra.appendAssumeCapacity(1); // items_len 12486 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12487 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true)); 12488 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12489 } 12490 if (false_count == 0) { 12491 cases_len += 1; 12492 12493 case_block.instructions.shrinkRetainingCapacity(0); 12494 case_block.wip_capture_scope = child_block.wip_capture_scope; 12495 12496 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12497 emit_bb = true; 12498 12499 try spa.analyzeProngRuntime( 12500 &case_block, 12501 .special, 12502 special.body, 12503 special.capture, 12504 .special_capture, 12505 &.{.bool_false}, 12506 .bool_false, 12507 special.has_tag_capture, 12508 ); 12509 12510 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12511 cases_extra.appendAssumeCapacity(1); // items_len 12512 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len)); 12513 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false)); 12514 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12515 } 12516 }, 12517 else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ 12518 operand_ty.fmt(mod), 12519 }), 12520 }; 12521 12522 case_block.instructions.shrinkRetainingCapacity(0); 12523 case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope); 12524 12525 if (mod.backendSupportsFeature(.is_named_enum_value) and 12526 special.body.len != 0 and block.wantSafety() and 12527 operand_ty.zigTypeTag(mod) == .Enum and 12528 (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) 12529 { 12530 try sema.zirDbgStmt(&case_block, cond_dbg_node_index); 12531 const ok = try case_block.addUnOp(.is_named_enum_value, operand); 12532 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); 12533 } 12534 12535 const analyze_body = if (union_originally and !special.is_inline) 12536 for (seen_enum_fields, 0..) |seen_field, index| { 12537 if (seen_field != null) continue; 12538 const union_obj = mod.typeToUnion(maybe_union_ty).?; 12539 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[index]); 12540 if (field_ty.zigTypeTag(mod) != .NoReturn) break true; 12541 } else false 12542 else 12543 true; 12544 if (special.body.len != 0 and err_set and 12545 try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src)) 12546 { 12547 // nothing to do here 12548 } else if (special.body.len != 0 and analyze_body and !special.is_inline) { 12549 try spa.analyzeProngRuntime( 12550 &case_block, 12551 .special, 12552 special.body, 12553 special.capture, 12554 .special_capture, 12555 undefined, // case_vals may be undefined for special prongs 12556 .none, 12557 false, 12558 ); 12559 } else { 12560 // We still need a terminator in this block, but we have proven 12561 // that it is unreachable. 12562 if (case_block.wantSafety()) { 12563 try sema.zirDbgStmt(&case_block, cond_dbg_node_index); 12564 try sema.safetyPanic(&case_block, src, .corrupt_switch); 12565 } else { 12566 _ = try case_block.addNoOp(.unreach); 12567 } 12568 } 12569 12570 if (is_first) { 12571 final_else_body = case_block.instructions.items; 12572 } else { 12573 try sema.air_extra.ensureUnusedCapacity(gpa, prev_then_body.len + 12574 @typeInfo(Air.CondBr).Struct.fields.len + case_block.instructions.items.len); 12575 12576 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = 12577 sema.addExtraAssumeCapacity(Air.CondBr{ 12578 .then_body_len = @intCast(prev_then_body.len), 12579 .else_body_len = @intCast(case_block.instructions.items.len), 12580 }); 12581 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body)); 12582 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); 12583 final_else_body = first_else_body; 12584 } 12585 } 12586 12587 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + 12588 cases_extra.items.len + final_else_body.len); 12589 12590 _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{ 12591 .operand = operand, 12592 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ 12593 .cases_len = @intCast(cases_len), 12594 .else_body_len = @intCast(final_else_body.len), 12595 }), 12596 } } }); 12597 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items)); 12598 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body)); 12599 12600 return sema.analyzeBlockBody(block, src, &child_block, merges); 12601 } 12602 12603 const RangeSetUnhandledIterator = struct { 12604 mod: *Module, 12605 cur: ?InternPool.Index, 12606 max: InternPool.Index, 12607 range_i: usize, 12608 ranges: []const RangeSet.Range, 12609 limbs: []math.big.Limb, 12610 12611 const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128); 12612 12613 fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator { 12614 const mod = sema.mod; 12615 const int_type = mod.intern_pool.indexToKey(ty.toIntern()).int_type; 12616 const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits); 12617 return .{ 12618 .mod = mod, 12619 .cur = (try ty.minInt(mod, ty)).toIntern(), 12620 .max = (try ty.maxInt(mod, ty)).toIntern(), 12621 .range_i = 0, 12622 .ranges = range_set.ranges.items, 12623 .limbs = if (needed_limbs > preallocated_limbs) 12624 try sema.arena.alloc(math.big.Limb, needed_limbs) 12625 else 12626 &.{}, 12627 }; 12628 } 12629 12630 fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index { 12631 if (val == it.max) return null; 12632 const int = it.mod.intern_pool.indexToKey(val).int; 12633 12634 switch (int.storage) { 12635 inline .u64, .i64 => |val_int| { 12636 const next_int = @addWithOverflow(val_int, 1); 12637 if (next_int[1] == 0) 12638 return (try it.mod.intValue(Type.fromInterned(int.ty), next_int[0])).toIntern(); 12639 }, 12640 .big_int => {}, 12641 .lazy_align, .lazy_size => unreachable, 12642 } 12643 12644 var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined; 12645 const val_bigint = int.storage.toBigInt(&val_space); 12646 12647 var result_limbs: [preallocated_limbs]math.big.Limb = undefined; 12648 var result_bigint = math.big.int.Mutable.init( 12649 if (it.limbs.len > 0) it.limbs else &result_limbs, 12650 0, 12651 ); 12652 12653 result_bigint.addScalar(val_bigint, 1); 12654 return (try it.mod.intValue_big(Type.fromInterned(int.ty), result_bigint.toConst())).toIntern(); 12655 } 12656 12657 fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index { 12658 var cur = it.cur orelse return null; 12659 while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) { 12660 defer it.range_i += 1; 12661 cur = (try it.addOne(it.ranges[it.range_i].last)) orelse { 12662 it.cur = null; 12663 return null; 12664 }; 12665 } 12666 it.cur = try it.addOne(cur); 12667 return cur; 12668 } 12669 }; 12670 12671 const ResolvedSwitchItem = struct { 12672 ref: Air.Inst.Ref, 12673 val: InternPool.Index, 12674 }; 12675 fn resolveSwitchItemVal( 12676 sema: *Sema, 12677 block: *Block, 12678 item_ref: Zir.Inst.Ref, 12679 /// Coerce `item_ref` to this type. 12680 coerce_ty: Type, 12681 switch_node_offset: i32, 12682 switch_prong_src: Module.SwitchProngSrc, 12683 range_expand: Module.SwitchProngSrc.RangeExpand, 12684 ) CompileError!ResolvedSwitchItem { 12685 const mod = sema.mod; 12686 const uncoerced_item = try sema.resolveInst(item_ref); 12687 12688 // Constructing a LazySrcLoc is costly because we only have the switch AST node. 12689 // Only if we know for sure we need to report a compile error do we resolve the 12690 // full source locations. 12691 12692 const item = sema.coerce(block, coerce_ty, uncoerced_item, .unneeded) catch |err| switch (err) { 12693 error.NeededSourceLocation => { 12694 const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand); 12695 _ = try sema.coerce(block, coerce_ty, uncoerced_item, src); 12696 unreachable; 12697 }, 12698 else => |e| return e, 12699 }; 12700 12701 const maybe_lazy = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch |err| switch (err) { 12702 error.NeededSourceLocation => { 12703 const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand); 12704 _ = try sema.resolveConstDefinedValue(block, src, item, .{ 12705 .needed_comptime_reason = "switch prong values must be comptime-known", 12706 }); 12707 unreachable; 12708 }, 12709 else => |e| return e, 12710 }; 12711 12712 const val = try sema.resolveLazyValue(maybe_lazy); 12713 const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: { 12714 break :blk Air.internedToRef(val.toIntern()); 12715 } else item; 12716 12717 return .{ .ref = new_item, .val = val.toIntern() }; 12718 } 12719 12720 fn validateSwitchRange( 12721 sema: *Sema, 12722 block: *Block, 12723 range_set: *RangeSet, 12724 first_ref: Zir.Inst.Ref, 12725 last_ref: Zir.Inst.Ref, 12726 operand_ty: Type, 12727 src_node_offset: i32, 12728 switch_prong_src: Module.SwitchProngSrc, 12729 ) CompileError![2]Air.Inst.Ref { 12730 const mod = sema.mod; 12731 const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, src_node_offset, switch_prong_src, .first); 12732 const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, src_node_offset, switch_prong_src, .last); 12733 if (try Value.fromInterned(first.val).compareAll(.gt, Value.fromInterned(last.val), operand_ty, mod)) { 12734 const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), src_node_offset, .first); 12735 return sema.fail(block, src, "range start value is greater than the end value", .{}); 12736 } 12737 const maybe_prev_src = try range_set.add(first.val, last.val, switch_prong_src); 12738 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12739 return .{ first.ref, last.ref }; 12740 } 12741 12742 fn validateSwitchItemInt( 12743 sema: *Sema, 12744 block: *Block, 12745 range_set: *RangeSet, 12746 item_ref: Zir.Inst.Ref, 12747 operand_ty: Type, 12748 src_node_offset: i32, 12749 switch_prong_src: Module.SwitchProngSrc, 12750 ) CompileError!Air.Inst.Ref { 12751 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); 12752 const maybe_prev_src = try range_set.add(item.val, item.val, switch_prong_src); 12753 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12754 return item.ref; 12755 } 12756 12757 fn validateSwitchItemEnum( 12758 sema: *Sema, 12759 block: *Block, 12760 seen_fields: []?Module.SwitchProngSrc, 12761 range_set: *RangeSet, 12762 item_ref: Zir.Inst.Ref, 12763 operand_ty: Type, 12764 src_node_offset: i32, 12765 switch_prong_src: Module.SwitchProngSrc, 12766 ) CompileError!Air.Inst.Ref { 12767 const ip = &sema.mod.intern_pool; 12768 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); 12769 const int = ip.indexToKey(item.val).enum_tag.int; 12770 const field_index = ip.indexToKey(ip.typeOf(item.val)).enum_type.tagValueIndex(ip, int) orelse { 12771 const maybe_prev_src = try range_set.add(int, int, switch_prong_src); 12772 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12773 return item.ref; 12774 }; 12775 const maybe_prev_src = seen_fields[field_index]; 12776 seen_fields[field_index] = switch_prong_src; 12777 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12778 return item.ref; 12779 } 12780 12781 fn validateSwitchItemError( 12782 sema: *Sema, 12783 block: *Block, 12784 seen_errors: *SwitchErrorSet, 12785 item_ref: Zir.Inst.Ref, 12786 operand_ty: Type, 12787 src_node_offset: i32, 12788 switch_prong_src: Module.SwitchProngSrc, 12789 ) CompileError!Air.Inst.Ref { 12790 const ip = &sema.mod.intern_pool; 12791 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); 12792 const error_name = ip.indexToKey(item.val).err.name; 12793 const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev| 12794 prev.value 12795 else 12796 null; 12797 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12798 return item.ref; 12799 } 12800 12801 fn validateSwitchDupe( 12802 sema: *Sema, 12803 block: *Block, 12804 maybe_prev_src: ?Module.SwitchProngSrc, 12805 switch_prong_src: Module.SwitchProngSrc, 12806 src_node_offset: i32, 12807 ) CompileError!void { 12808 const prev_prong_src = maybe_prev_src orelse return; 12809 const mod = sema.mod; 12810 const block_src_decl = mod.declPtr(block.src_decl); 12811 const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); 12812 const prev_src = prev_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); 12813 const msg = msg: { 12814 const msg = try sema.errMsg( 12815 block, 12816 src, 12817 "duplicate switch value", 12818 .{}, 12819 ); 12820 errdefer msg.destroy(sema.gpa); 12821 try sema.errNote( 12822 block, 12823 prev_src, 12824 msg, 12825 "previous value here", 12826 .{}, 12827 ); 12828 break :msg msg; 12829 }; 12830 return sema.failWithOwnedErrorMsg(block, msg); 12831 } 12832 12833 fn validateSwitchItemBool( 12834 sema: *Sema, 12835 block: *Block, 12836 true_count: *u8, 12837 false_count: *u8, 12838 item_ref: Zir.Inst.Ref, 12839 src_node_offset: i32, 12840 switch_prong_src: Module.SwitchProngSrc, 12841 ) CompileError!Air.Inst.Ref { 12842 const mod = sema.mod; 12843 const item = try sema.resolveSwitchItemVal(block, item_ref, Type.bool, src_node_offset, switch_prong_src, .none); 12844 if (Value.fromInterned(item.val).toBool()) { 12845 true_count.* += 1; 12846 } else { 12847 false_count.* += 1; 12848 } 12849 if (true_count.* > 1 or false_count.* > 1) { 12850 const block_src_decl = sema.mod.declPtr(block.src_decl); 12851 const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); 12852 return sema.fail(block, src, "duplicate switch value", .{}); 12853 } 12854 return item.ref; 12855 } 12856 12857 const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, Module.SwitchProngSrc); 12858 12859 fn validateSwitchItemSparse( 12860 sema: *Sema, 12861 block: *Block, 12862 seen_values: *ValueSrcMap, 12863 item_ref: Zir.Inst.Ref, 12864 operand_ty: Type, 12865 src_node_offset: i32, 12866 switch_prong_src: Module.SwitchProngSrc, 12867 ) CompileError!Air.Inst.Ref { 12868 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); 12869 const kv = (try seen_values.fetchPut(sema.gpa, item.val, switch_prong_src)) orelse return item.ref; 12870 try sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset); 12871 unreachable; 12872 } 12873 12874 fn validateSwitchNoRange( 12875 sema: *Sema, 12876 block: *Block, 12877 ranges_len: u32, 12878 operand_ty: Type, 12879 src_node_offset: i32, 12880 ) CompileError!void { 12881 if (ranges_len == 0) 12882 return; 12883 12884 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; 12885 const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset }; 12886 12887 const msg = msg: { 12888 const msg = try sema.errMsg( 12889 block, 12890 operand_src, 12891 "ranges not allowed when switching on type '{}'", 12892 .{operand_ty.fmt(sema.mod)}, 12893 ); 12894 errdefer msg.destroy(sema.gpa); 12895 try sema.errNote( 12896 block, 12897 range_src, 12898 msg, 12899 "range here", 12900 .{}, 12901 ); 12902 break :msg msg; 12903 }; 12904 return sema.failWithOwnedErrorMsg(block, msg); 12905 } 12906 12907 fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref, operand_src: LazySrcLoc) !bool { 12908 const mod = sema.mod; 12909 if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false; 12910 12911 const tags = sema.code.instructions.items(.tag); 12912 for (body) |inst| { 12913 switch (tags[@intFromEnum(inst)]) { 12914 .@"unreachable" => if (!block.wantSafety()) return false, 12915 .save_err_ret_index, 12916 .dbg_block_begin, 12917 .dbg_block_end, 12918 .dbg_stmt, 12919 .str, 12920 .as_node, 12921 .panic, 12922 .field_val, 12923 => {}, 12924 else => return false, 12925 } 12926 } 12927 12928 for (body) |inst| { 12929 const air_inst = switch (tags[@intFromEnum(inst)]) { 12930 .dbg_block_begin, 12931 .dbg_block_end, 12932 => continue, 12933 .dbg_stmt => { 12934 try sema.zirDbgStmt(block, inst); 12935 continue; 12936 }, 12937 .save_err_ret_index => { 12938 try sema.zirSaveErrRetIndex(block, inst); 12939 continue; 12940 }, 12941 .str => try sema.zirStr(inst), 12942 .as_node => try sema.zirAsNode(block, inst), 12943 .field_val => try sema.zirFieldVal(block, inst), 12944 .@"unreachable" => { 12945 if (!mod.comp.formatted_panics) { 12946 try sema.safetyPanic(block, operand_src, .unwrap_error); 12947 return true; 12948 } 12949 12950 const panic_fn = try sema.getBuiltin("panicUnwrapError"); 12951 const err_return_trace = try sema.getErrorReturnTrace(block); 12952 const args: [2]Air.Inst.Ref = .{ err_return_trace, operand }; 12953 try sema.callBuiltin(block, operand_src, panic_fn, .auto, &args, .@"safety check"); 12954 return true; 12955 }, 12956 .panic => { 12957 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 12958 const msg_inst = try sema.resolveInst(inst_data.operand); 12959 12960 const panic_fn = try sema.getBuiltin("panic"); 12961 const err_return_trace = try sema.getErrorReturnTrace(block); 12962 const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value }; 12963 try sema.callBuiltin(block, operand_src, panic_fn, .auto, &args, .@"safety check"); 12964 return true; 12965 }, 12966 else => unreachable, 12967 }; 12968 if (sema.typeOf(air_inst).isNoReturn(mod)) 12969 return true; 12970 sema.inst_map.putAssumeCapacity(inst, air_inst); 12971 } 12972 unreachable; 12973 } 12974 12975 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void { 12976 const mod = sema.mod; 12977 const index = cond.toIndex() orelse return; 12978 if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) return; 12979 12980 const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node; 12981 const err_operand = try sema.resolveInst(err_inst_data.operand); 12982 const operand_ty = sema.typeOf(err_operand); 12983 if (operand_ty.zigTypeTag(mod) == .ErrorSet) { 12984 try sema.maybeErrorUnwrapComptime(block, body, err_operand); 12985 return; 12986 } 12987 if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| { 12988 if (!operand_ty.isError(mod)) return; 12989 if (val.getErrorName(mod) == .none) return; 12990 try sema.maybeErrorUnwrapComptime(block, body, err_operand); 12991 } 12992 } 12993 12994 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void { 12995 const tags = sema.code.instructions.items(.tag); 12996 const inst = for (body) |inst| { 12997 switch (tags[@intFromEnum(inst)]) { 12998 .dbg_block_begin, 12999 .dbg_block_end, 13000 .dbg_stmt, 13001 .save_err_ret_index, 13002 => {}, 13003 .@"unreachable" => break inst, 13004 else => return, 13005 } 13006 } else return; 13007 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable"; 13008 const src = inst_data.src(); 13009 13010 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 13011 if (val.getErrorName(sema.mod).unwrap()) |name| { 13012 return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&sema.mod.intern_pool)}); 13013 } 13014 } 13015 } 13016 13017 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13018 const mod = sema.mod; 13019 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13020 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13021 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 13022 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 13023 const ty = try sema.resolveType(block, ty_src, extra.lhs); 13024 const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ 13025 .needed_comptime_reason = "field name must be comptime-known", 13026 }); 13027 try sema.resolveTypeFields(ty); 13028 const ip = &mod.intern_pool; 13029 13030 const has_field = hf: { 13031 switch (ip.indexToKey(ty.toIntern())) { 13032 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 13033 .Slice => { 13034 if (ip.stringEqlSlice(field_name, "ptr")) break :hf true; 13035 if (ip.stringEqlSlice(field_name, "len")) break :hf true; 13036 break :hf false; 13037 }, 13038 else => {}, 13039 }, 13040 .anon_struct_type => |anon_struct| { 13041 if (anon_struct.names.len != 0) { 13042 break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names.get(ip), field_name) != null; 13043 } else { 13044 const field_index = field_name.toUnsigned(ip) orelse break :hf false; 13045 break :hf field_index < ty.structFieldCount(mod); 13046 } 13047 }, 13048 .struct_type => |struct_type| { 13049 break :hf struct_type.nameIndex(ip, field_name) != null; 13050 }, 13051 .union_type => |union_type| { 13052 const union_obj = ip.loadUnionType(union_type); 13053 break :hf union_obj.nameIndex(ip, field_name) != null; 13054 }, 13055 .enum_type => |enum_type| { 13056 break :hf enum_type.nameIndex(ip, field_name) != null; 13057 }, 13058 .array_type => break :hf ip.stringEqlSlice(field_name, "len"), 13059 else => {}, 13060 } 13061 return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ 13062 ty.fmt(mod), 13063 }); 13064 }; 13065 return if (has_field) .bool_true else .bool_false; 13066 } 13067 13068 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13069 const mod = sema.mod; 13070 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13071 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13072 const src = inst_data.src(); 13073 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 13074 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 13075 const container_type = try sema.resolveType(block, lhs_src, extra.lhs); 13076 const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{ 13077 .needed_comptime_reason = "decl name must be comptime-known", 13078 }); 13079 13080 try sema.checkNamespaceType(block, lhs_src, container_type); 13081 13082 const namespace = container_type.getNamespaceIndex(mod).unwrap() orelse 13083 return .bool_false; 13084 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { 13085 const decl = mod.declPtr(decl_index); 13086 if (decl.is_pub or decl.getFileScope(mod) == block.getFileScope(mod)) { 13087 return .bool_true; 13088 } 13089 } 13090 return .bool_false; 13091 } 13092 13093 fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13094 const tracy = trace(@src()); 13095 defer tracy.end(); 13096 13097 const mod = sema.mod; 13098 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 13099 const operand_src = inst_data.src(); 13100 const operand = inst_data.get(sema.code); 13101 13102 const result = mod.importFile(block.getFileScope(mod), operand) catch |err| switch (err) { 13103 error.ImportOutsideModulePath => { 13104 return sema.fail(block, operand_src, "import of file outside module path: '{s}'", .{operand}); 13105 }, 13106 error.ModuleNotFound => { 13107 return sema.fail(block, operand_src, "no module named '{s}' available within module {s}", .{ 13108 operand, block.getFileScope(mod).mod.fully_qualified_name, 13109 }); 13110 }, 13111 else => { 13112 // TODO: these errors are file system errors; make sure an update() will 13113 // retry this and not cache the file system error, which may be transient. 13114 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); 13115 }, 13116 }; 13117 try mod.semaFile(result.file); 13118 const file_root_decl_index = result.file.root_decl.unwrap().?; 13119 return sema.analyzeDeclVal(block, operand_src, file_root_decl_index); 13120 } 13121 13122 fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13123 const tracy = trace(@src()); 13124 defer tracy.end(); 13125 13126 const mod = sema.mod; 13127 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 13128 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 13129 const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ 13130 .needed_comptime_reason = "file path name must be comptime-known", 13131 }); 13132 13133 if (name.len == 0) { 13134 return sema.fail(block, operand_src, "file path name cannot be empty", .{}); 13135 } 13136 13137 const src_loc = operand_src.toSrcLoc(mod.declPtr(block.src_decl), mod); 13138 const val = mod.embedFile(block.getFileScope(mod), name, src_loc) catch |err| switch (err) { 13139 error.ImportOutsideModulePath => { 13140 return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name}); 13141 }, 13142 else => { 13143 // TODO: these errors are file system errors; make sure an update() will 13144 // retry this and not cache the file system error, which may be transient. 13145 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(err) }); 13146 }, 13147 }; 13148 13149 return Air.internedToRef(val); 13150 } 13151 13152 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13153 const mod = sema.mod; 13154 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 13155 const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 13156 _ = try mod.getErrorValue(name); 13157 const error_set_type = try mod.singleErrorSetType(name); 13158 return Air.internedToRef((try mod.intern(.{ .err = .{ 13159 .ty = error_set_type.toIntern(), 13160 .name = name, 13161 } }))); 13162 } 13163 13164 fn zirShl( 13165 sema: *Sema, 13166 block: *Block, 13167 inst: Zir.Inst.Index, 13168 air_tag: Air.Inst.Tag, 13169 ) CompileError!Air.Inst.Ref { 13170 const tracy = trace(@src()); 13171 defer tracy.end(); 13172 13173 const mod = sema.mod; 13174 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13175 const src = inst_data.src(); 13176 sema.src = src; 13177 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13178 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13179 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13180 const lhs = try sema.resolveInst(extra.lhs); 13181 const rhs = try sema.resolveInst(extra.rhs); 13182 const lhs_ty = sema.typeOf(lhs); 13183 const rhs_ty = sema.typeOf(rhs); 13184 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13185 13186 const scalar_ty = lhs_ty.scalarType(mod); 13187 const scalar_rhs_ty = rhs_ty.scalarType(mod); 13188 13189 // TODO coerce rhs if air_tag is not shl_sat 13190 const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); 13191 13192 const maybe_lhs_val = try sema.resolveValueIntable(lhs); 13193 const maybe_rhs_val = try sema.resolveValueIntable(rhs); 13194 13195 if (maybe_rhs_val) |rhs_val| { 13196 if (rhs_val.isUndef(mod)) { 13197 return mod.undefRef(sema.typeOf(lhs)); 13198 } 13199 // If rhs is 0, return lhs without doing any calculations. 13200 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 13201 return lhs; 13202 } 13203 if (scalar_ty.zigTypeTag(mod) != .ComptimeInt and air_tag != .shl_sat) { 13204 const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits); 13205 if (rhs_ty.zigTypeTag(mod) == .Vector) { 13206 var i: usize = 0; 13207 while (i < rhs_ty.vectorLen(mod)) : (i += 1) { 13208 const rhs_elem = try rhs_val.elemValue(mod, i); 13209 if (rhs_elem.compareHetero(.gte, bit_value, mod)) { 13210 return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{ 13211 rhs_elem.fmtValue(scalar_ty, mod), 13212 i, 13213 scalar_ty.fmt(mod), 13214 }); 13215 } 13216 } 13217 } else if (rhs_val.compareHetero(.gte, bit_value, mod)) { 13218 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{ 13219 rhs_val.fmtValue(scalar_ty, mod), 13220 scalar_ty.fmt(mod), 13221 }); 13222 } 13223 } 13224 if (rhs_ty.zigTypeTag(mod) == .Vector) { 13225 var i: usize = 0; 13226 while (i < rhs_ty.vectorLen(mod)) : (i += 1) { 13227 const rhs_elem = try rhs_val.elemValue(mod, i); 13228 if (rhs_elem.compareHetero(.lt, try mod.intValue(scalar_rhs_ty, 0), mod)) { 13229 return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{ 13230 rhs_elem.fmtValue(scalar_ty, mod), 13231 i, 13232 }); 13233 } 13234 } 13235 } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) { 13236 return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{ 13237 rhs_val.fmtValue(scalar_ty, mod), 13238 }); 13239 } 13240 } 13241 13242 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { 13243 if (lhs_val.isUndef(mod)) return mod.undefRef(lhs_ty); 13244 const rhs_val = maybe_rhs_val orelse { 13245 if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 13246 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 13247 } 13248 break :rs rhs_src; 13249 }; 13250 13251 const val = switch (air_tag) { 13252 .shl_exact => val: { 13253 const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, mod); 13254 if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 13255 break :val shifted.wrapped_result; 13256 } 13257 if (shifted.overflow_bit.compareAllWithZero(.eq, mod)) { 13258 break :val shifted.wrapped_result; 13259 } 13260 return sema.fail(block, src, "operation caused overflow", .{}); 13261 }, 13262 13263 .shl_sat => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) 13264 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod) 13265 else 13266 try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, mod), 13267 13268 .shl => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) 13269 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod) 13270 else 13271 try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, mod), 13272 13273 else => unreachable, 13274 }; 13275 13276 return Air.internedToRef(val.toIntern()); 13277 } else lhs_src; 13278 13279 const new_rhs = if (air_tag == .shl_sat) rhs: { 13280 // Limit the RHS type for saturating shl to be an integer as small as the LHS. 13281 if (rhs_is_comptime_int or 13282 scalar_rhs_ty.intInfo(mod).bits > scalar_ty.intInfo(mod).bits) 13283 { 13284 const max_int = Air.internedToRef((try lhs_ty.maxInt(mod, lhs_ty)).toIntern()); 13285 const rhs_limited = try sema.analyzeMinMax(block, rhs_src, .min, &.{ rhs, max_int }, &.{ rhs_src, rhs_src }); 13286 break :rhs try sema.intCast(block, src, lhs_ty, rhs_src, rhs_limited, rhs_src, false); 13287 } else { 13288 break :rhs rhs; 13289 } 13290 } else rhs; 13291 13292 try sema.requireRuntimeBlock(block, src, runtime_src); 13293 if (block.wantSafety()) { 13294 const bit_count = scalar_ty.intInfo(mod).bits; 13295 if (!std.math.isPowerOfTwo(bit_count)) { 13296 const bit_count_val = try mod.intValue(scalar_rhs_ty, bit_count); 13297 const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { 13298 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern()); 13299 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); 13300 break :ok try block.addInst(.{ 13301 .tag = .reduce, 13302 .data = .{ .reduce = .{ 13303 .operand = lt, 13304 .operation = .And, 13305 } }, 13306 }); 13307 } else ok: { 13308 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern()); 13309 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); 13310 }; 13311 try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big); 13312 } 13313 13314 if (air_tag == .shl_exact) { 13315 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty); 13316 const op_ov = try block.addInst(.{ 13317 .tag = .shl_with_overflow, 13318 .data = .{ .ty_pl = .{ 13319 .ty = Air.internedToRef(op_ov_tuple_ty.toIntern()), 13320 .payload = try sema.addExtra(Air.Bin{ 13321 .lhs = lhs, 13322 .rhs = rhs, 13323 }), 13324 } }, 13325 }); 13326 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); 13327 const any_ov_bit = if (lhs_ty.zigTypeTag(mod) == .Vector) 13328 try block.addInst(.{ 13329 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 13330 .data = .{ .reduce = .{ 13331 .operand = ov_bit, 13332 .operation = .Or, 13333 } }, 13334 }) 13335 else 13336 ov_bit; 13337 const zero_ov = Air.internedToRef((try mod.intValue(Type.u1, 0)).toIntern()); 13338 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); 13339 13340 try sema.addSafetyCheck(block, src, no_ov, .shl_overflow); 13341 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); 13342 } 13343 } 13344 return block.addBinOp(air_tag, lhs, new_rhs); 13345 } 13346 13347 fn zirShr( 13348 sema: *Sema, 13349 block: *Block, 13350 inst: Zir.Inst.Index, 13351 air_tag: Air.Inst.Tag, 13352 ) CompileError!Air.Inst.Ref { 13353 const tracy = trace(@src()); 13354 defer tracy.end(); 13355 13356 const mod = sema.mod; 13357 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13358 const src = inst_data.src(); 13359 sema.src = src; 13360 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13361 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13362 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13363 const lhs = try sema.resolveInst(extra.lhs); 13364 const rhs = try sema.resolveInst(extra.rhs); 13365 const lhs_ty = sema.typeOf(lhs); 13366 const rhs_ty = sema.typeOf(rhs); 13367 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13368 const scalar_ty = lhs_ty.scalarType(mod); 13369 13370 const maybe_lhs_val = try sema.resolveValueIntable(lhs); 13371 const maybe_rhs_val = try sema.resolveValueIntable(rhs); 13372 13373 const runtime_src = if (maybe_rhs_val) |rhs_val| rs: { 13374 if (rhs_val.isUndef(mod)) { 13375 return mod.undefRef(lhs_ty); 13376 } 13377 // If rhs is 0, return lhs without doing any calculations. 13378 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 13379 return lhs; 13380 } 13381 if (scalar_ty.zigTypeTag(mod) != .ComptimeInt) { 13382 const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits); 13383 if (rhs_ty.zigTypeTag(mod) == .Vector) { 13384 var i: usize = 0; 13385 while (i < rhs_ty.vectorLen(mod)) : (i += 1) { 13386 const rhs_elem = try rhs_val.elemValue(mod, i); 13387 if (rhs_elem.compareHetero(.gte, bit_value, mod)) { 13388 return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{ 13389 rhs_elem.fmtValue(scalar_ty, mod), 13390 i, 13391 scalar_ty.fmt(mod), 13392 }); 13393 } 13394 } 13395 } else if (rhs_val.compareHetero(.gte, bit_value, mod)) { 13396 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{ 13397 rhs_val.fmtValue(scalar_ty, mod), 13398 scalar_ty.fmt(mod), 13399 }); 13400 } 13401 } 13402 if (rhs_ty.zigTypeTag(mod) == .Vector) { 13403 var i: usize = 0; 13404 while (i < rhs_ty.vectorLen(mod)) : (i += 1) { 13405 const rhs_elem = try rhs_val.elemValue(mod, i); 13406 if (rhs_elem.compareHetero(.lt, try mod.intValue(rhs_ty.childType(mod), 0), mod)) { 13407 return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{ 13408 rhs_elem.fmtValue(scalar_ty, mod), 13409 i, 13410 }); 13411 } 13412 } 13413 } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) { 13414 return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{ 13415 rhs_val.fmtValue(scalar_ty, mod), 13416 }); 13417 } 13418 if (maybe_lhs_val) |lhs_val| { 13419 if (lhs_val.isUndef(mod)) { 13420 return mod.undefRef(lhs_ty); 13421 } 13422 if (air_tag == .shr_exact) { 13423 // Detect if any ones would be shifted out. 13424 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, mod); 13425 if (!(try truncated.compareAllWithZeroAdvanced(.eq, sema))) { 13426 return sema.fail(block, src, "exact shift shifted out 1 bits", .{}); 13427 } 13428 } 13429 const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, mod); 13430 return Air.internedToRef(val.toIntern()); 13431 } else { 13432 break :rs lhs_src; 13433 } 13434 } else rhs_src; 13435 13436 if (maybe_rhs_val == null and scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 13437 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 13438 } 13439 13440 try sema.requireRuntimeBlock(block, src, runtime_src); 13441 const result = try block.addBinOp(air_tag, lhs, rhs); 13442 if (block.wantSafety()) { 13443 const bit_count = scalar_ty.intInfo(mod).bits; 13444 if (!std.math.isPowerOfTwo(bit_count)) { 13445 const bit_count_val = try mod.intValue(rhs_ty.scalarType(mod), bit_count); 13446 13447 const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { 13448 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern()); 13449 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); 13450 break :ok try block.addInst(.{ 13451 .tag = .reduce, 13452 .data = .{ .reduce = .{ 13453 .operand = lt, 13454 .operation = .And, 13455 } }, 13456 }); 13457 } else ok: { 13458 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern()); 13459 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); 13460 }; 13461 try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big); 13462 } 13463 13464 if (air_tag == .shr_exact) { 13465 const back = try block.addBinOp(.shl, result, rhs); 13466 13467 const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { 13468 const eql = try block.addCmpVector(lhs, back, .eq); 13469 break :ok try block.addInst(.{ 13470 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 13471 .data = .{ .reduce = .{ 13472 .operand = eql, 13473 .operation = .And, 13474 } }, 13475 }); 13476 } else try block.addBinOp(.cmp_eq, lhs, back); 13477 try sema.addSafetyCheck(block, src, ok, .shr_overflow); 13478 } 13479 } 13480 return result; 13481 } 13482 13483 fn zirBitwise( 13484 sema: *Sema, 13485 block: *Block, 13486 inst: Zir.Inst.Index, 13487 air_tag: Air.Inst.Tag, 13488 ) CompileError!Air.Inst.Ref { 13489 const tracy = trace(@src()); 13490 defer tracy.end(); 13491 13492 const mod = sema.mod; 13493 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13494 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 13495 sema.src = src; 13496 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13497 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13498 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13499 const lhs = try sema.resolveInst(extra.lhs); 13500 const rhs = try sema.resolveInst(extra.rhs); 13501 const lhs_ty = sema.typeOf(lhs); 13502 const rhs_ty = sema.typeOf(rhs); 13503 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13504 13505 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 13506 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); 13507 const scalar_type = resolved_type.scalarType(mod); 13508 const scalar_tag = scalar_type.zigTypeTag(mod); 13509 13510 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 13511 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 13512 13513 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 13514 13515 if (!is_int) { 13516 return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(mod)), @tagName(rhs_ty.zigTypeTag(mod)) }); 13517 } 13518 13519 const runtime_src = runtime: { 13520 // TODO: ask the linker what kind of relocations are available, and 13521 // in some cases emit a Value that means "this decl's address AND'd with this operand". 13522 if (try sema.resolveValueIntable(casted_lhs)) |lhs_val| { 13523 if (try sema.resolveValueIntable(casted_rhs)) |rhs_val| { 13524 const result_val = switch (air_tag) { 13525 .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, mod), 13526 .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, mod), 13527 .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, mod), 13528 else => unreachable, 13529 }; 13530 return Air.internedToRef(result_val.toIntern()); 13531 } else { 13532 break :runtime rhs_src; 13533 } 13534 } else { 13535 break :runtime lhs_src; 13536 } 13537 }; 13538 13539 try sema.requireRuntimeBlock(block, src, runtime_src); 13540 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 13541 } 13542 13543 fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13544 const tracy = trace(@src()); 13545 defer tracy.end(); 13546 13547 const mod = sema.mod; 13548 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 13549 const src = inst_data.src(); 13550 const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 13551 13552 const operand = try sema.resolveInst(inst_data.operand); 13553 const operand_type = sema.typeOf(operand); 13554 const scalar_type = operand_type.scalarType(mod); 13555 13556 if (scalar_type.zigTypeTag(mod) != .Int) { 13557 return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{ 13558 operand_type.fmt(mod), 13559 }); 13560 } 13561 13562 if (try sema.resolveValue(operand)) |val| { 13563 if (val.isUndef(mod)) { 13564 return mod.undefRef(operand_type); 13565 } else if (operand_type.zigTypeTag(mod) == .Vector) { 13566 const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen(mod)); 13567 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 13568 for (elems, 0..) |*elem, i| { 13569 const elem_val = try val.elemValue(mod, i); 13570 elem.* = try (try elem_val.bitwiseNot(scalar_type, sema.arena, mod)).intern(scalar_type, mod); 13571 } 13572 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 13573 .ty = operand_type.toIntern(), 13574 .storage = .{ .elems = elems }, 13575 } }))); 13576 } else { 13577 const result_val = try val.bitwiseNot(operand_type, sema.arena, mod); 13578 return Air.internedToRef(result_val.toIntern()); 13579 } 13580 } 13581 13582 try sema.requireRuntimeBlock(block, src, null); 13583 return block.addTyOp(.not, operand_type, operand); 13584 } 13585 13586 fn analyzeTupleCat( 13587 sema: *Sema, 13588 block: *Block, 13589 src_node: i32, 13590 lhs: Air.Inst.Ref, 13591 rhs: Air.Inst.Ref, 13592 ) CompileError!Air.Inst.Ref { 13593 const mod = sema.mod; 13594 const lhs_ty = sema.typeOf(lhs); 13595 const rhs_ty = sema.typeOf(rhs); 13596 const src = LazySrcLoc.nodeOffset(src_node); 13597 13598 const lhs_len = lhs_ty.structFieldCount(mod); 13599 const rhs_len = rhs_ty.structFieldCount(mod); 13600 const dest_fields = lhs_len + rhs_len; 13601 13602 if (dest_fields == 0) { 13603 return Air.internedToRef(Value.empty_struct.toIntern()); 13604 } 13605 if (lhs_len == 0) { 13606 return rhs; 13607 } 13608 if (rhs_len == 0) { 13609 return lhs; 13610 } 13611 const final_len = try sema.usizeCast(block, src, dest_fields); 13612 13613 const types = try sema.arena.alloc(InternPool.Index, final_len); 13614 const values = try sema.arena.alloc(InternPool.Index, final_len); 13615 13616 const opt_runtime_src = rs: { 13617 var runtime_src: ?LazySrcLoc = null; 13618 var i: u32 = 0; 13619 while (i < lhs_len) : (i += 1) { 13620 types[i] = lhs_ty.structFieldType(i, mod).toIntern(); 13621 const default_val = lhs_ty.structFieldDefaultValue(i, mod); 13622 values[i] = default_val.toIntern(); 13623 const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{ 13624 .array_cat_offset = src_node, 13625 .elem_index = i, 13626 } }; 13627 if (default_val.toIntern() == .unreachable_value) { 13628 runtime_src = operand_src; 13629 values[i] = .none; 13630 } 13631 } 13632 i = 0; 13633 while (i < rhs_len) : (i += 1) { 13634 types[i + lhs_len] = rhs_ty.structFieldType(i, mod).toIntern(); 13635 const default_val = rhs_ty.structFieldDefaultValue(i, mod); 13636 values[i + lhs_len] = default_val.toIntern(); 13637 const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{ 13638 .array_cat_offset = src_node, 13639 .elem_index = i, 13640 } }; 13641 if (default_val.toIntern() == .unreachable_value) { 13642 runtime_src = operand_src; 13643 values[i + lhs_len] = .none; 13644 } 13645 } 13646 break :rs runtime_src; 13647 }; 13648 13649 const tuple_ty = try mod.intern_pool.getAnonStructType(mod.gpa, .{ 13650 .types = types, 13651 .values = values, 13652 .names = &.{}, 13653 }); 13654 13655 const runtime_src = opt_runtime_src orelse { 13656 const tuple_val = try mod.intern(.{ .aggregate = .{ 13657 .ty = tuple_ty, 13658 .storage = .{ .elems = values }, 13659 } }); 13660 return Air.internedToRef(tuple_val); 13661 }; 13662 13663 try sema.requireRuntimeBlock(block, src, runtime_src); 13664 13665 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); 13666 var i: u32 = 0; 13667 while (i < lhs_len) : (i += 1) { 13668 const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{ 13669 .array_cat_offset = src_node, 13670 .elem_index = i, 13671 } }; 13672 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, i, lhs_ty); 13673 } 13674 i = 0; 13675 while (i < rhs_len) : (i += 1) { 13676 const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{ 13677 .array_cat_offset = src_node, 13678 .elem_index = i, 13679 } }; 13680 element_refs[i + lhs_len] = 13681 try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty); 13682 } 13683 13684 return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs); 13685 } 13686 13687 fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13688 const tracy = trace(@src()); 13689 defer tracy.end(); 13690 13691 const mod = sema.mod; 13692 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 13693 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13694 const lhs = try sema.resolveInst(extra.lhs); 13695 const rhs = try sema.resolveInst(extra.rhs); 13696 const lhs_ty = sema.typeOf(lhs); 13697 const rhs_ty = sema.typeOf(rhs); 13698 const src = inst_data.src(); 13699 13700 const lhs_is_tuple = lhs_ty.isTuple(mod); 13701 const rhs_is_tuple = rhs_ty.isTuple(mod); 13702 if (lhs_is_tuple and rhs_is_tuple) { 13703 return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); 13704 } 13705 13706 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13707 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13708 13709 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: { 13710 if (lhs_is_tuple) break :lhs_info @as(Type.ArrayInfo, undefined); 13711 return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)}); 13712 }; 13713 const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse { 13714 assert(!rhs_is_tuple); 13715 return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(mod)}); 13716 }; 13717 13718 const resolved_elem_ty = t: { 13719 var trash_block = block.makeSubBlock(); 13720 trash_block.is_comptime = false; 13721 defer trash_block.instructions.deinit(sema.gpa); 13722 13723 const instructions = [_]Air.Inst.Ref{ 13724 try trash_block.addBitCast(lhs_info.elem_type, .void_value), 13725 try trash_block.addBitCast(rhs_info.elem_type, .void_value), 13726 }; 13727 break :t try sema.resolvePeerTypes(block, src, &instructions, .{ 13728 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 13729 }); 13730 }; 13731 13732 // When there is a sentinel mismatch, no sentinel on the result. 13733 // Otherwise, use the sentinel value provided by either operand, 13734 // coercing it to the peer-resolved element type. 13735 const res_sent_val: ?Value = s: { 13736 if (lhs_info.sentinel) |lhs_sent_val| { 13737 const lhs_sent = Air.internedToRef(lhs_sent_val.toIntern()); 13738 if (rhs_info.sentinel) |rhs_sent_val| { 13739 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern()); 13740 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); 13741 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); 13742 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?; 13743 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?; 13744 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { 13745 break :s lhs_sent_casted_val; 13746 } else { 13747 break :s null; 13748 } 13749 } else { 13750 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); 13751 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?; 13752 break :s lhs_sent_casted_val; 13753 } 13754 } else { 13755 if (rhs_info.sentinel) |rhs_sent_val| { 13756 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern()); 13757 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); 13758 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?; 13759 break :s rhs_sent_casted_val; 13760 } else { 13761 break :s null; 13762 } 13763 } 13764 }; 13765 13766 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); 13767 const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len); 13768 const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) { 13769 error.Overflow => return sema.fail( 13770 block, 13771 src, 13772 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle", 13773 .{ lhs_len, rhs_len }, 13774 ), 13775 }; 13776 13777 const result_ty = try mod.arrayType(.{ 13778 .len = result_len, 13779 .sentinel = if (res_sent_val) |v| v.toIntern() else .none, 13780 .child = resolved_elem_ty.toIntern(), 13781 }); 13782 const ptr_addrspace = p: { 13783 if (lhs_ty.zigTypeTag(mod) == .Pointer) break :p lhs_ty.ptrAddressSpace(mod); 13784 if (rhs_ty.zigTypeTag(mod) == .Pointer) break :p rhs_ty.ptrAddressSpace(mod); 13785 break :p null; 13786 }; 13787 13788 const runtime_src = if (switch (lhs_ty.zigTypeTag(mod)) { 13789 .Array, .Struct => try sema.resolveValue(lhs), 13790 .Pointer => try sema.resolveDefinedValue(block, lhs_src, lhs), 13791 else => unreachable, 13792 }) |lhs_val| rs: { 13793 if (switch (rhs_ty.zigTypeTag(mod)) { 13794 .Array, .Struct => try sema.resolveValue(rhs), 13795 .Pointer => try sema.resolveDefinedValue(block, rhs_src, rhs), 13796 else => unreachable, 13797 }) |rhs_val| { 13798 const lhs_sub_val = if (lhs_ty.isSinglePointer(mod)) 13799 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? 13800 else 13801 lhs_val; 13802 13803 const rhs_sub_val = if (rhs_ty.isSinglePointer(mod)) 13804 (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? 13805 else 13806 rhs_val; 13807 13808 const element_vals = try sema.arena.alloc(InternPool.Index, result_len); 13809 var elem_i: u32 = 0; 13810 while (elem_i < lhs_len) : (elem_i += 1) { 13811 const lhs_elem_i = elem_i; 13812 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, mod) else Value.@"unreachable"; 13813 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(mod, lhs_elem_i) else elem_default_val; 13814 const elem_val_inst = Air.internedToRef(elem_val.toIntern()); 13815 const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{ 13816 .array_cat_offset = inst_data.src_node, 13817 .elem_index = elem_i, 13818 } }; 13819 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src); 13820 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined); 13821 element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod); 13822 } 13823 while (elem_i < result_len) : (elem_i += 1) { 13824 const rhs_elem_i = elem_i - lhs_len; 13825 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, mod) else Value.@"unreachable"; 13826 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(mod, rhs_elem_i) else elem_default_val; 13827 const elem_val_inst = Air.internedToRef(elem_val.toIntern()); 13828 const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{ 13829 .array_cat_offset = inst_data.src_node, 13830 .elem_index = @intCast(rhs_elem_i), 13831 } }; 13832 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src); 13833 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined); 13834 element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod); 13835 } 13836 return sema.addConstantMaybeRef(try mod.intern(.{ .aggregate = .{ 13837 .ty = result_ty.toIntern(), 13838 .storage = .{ .elems = element_vals }, 13839 } }), ptr_addrspace != null); 13840 } else break :rs rhs_src; 13841 } else lhs_src; 13842 13843 try sema.requireRuntimeBlock(block, src, runtime_src); 13844 13845 if (ptr_addrspace) |ptr_as| { 13846 const alloc_ty = try sema.ptrType(.{ 13847 .child = result_ty.toIntern(), 13848 .flags = .{ .address_space = ptr_as }, 13849 }); 13850 const alloc = try block.addTy(.alloc, alloc_ty); 13851 const elem_ptr_ty = try sema.ptrType(.{ 13852 .child = resolved_elem_ty.toIntern(), 13853 .flags = .{ .address_space = ptr_as }, 13854 }); 13855 13856 var elem_i: u32 = 0; 13857 while (elem_i < lhs_len) : (elem_i += 1) { 13858 const elem_index = try mod.intRef(Type.usize, elem_i); 13859 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 13860 const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{ 13861 .array_cat_offset = inst_data.src_node, 13862 .elem_index = elem_i, 13863 } }; 13864 const init = try sema.elemVal(block, operand_src, lhs, elem_index, src, true); 13865 try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store); 13866 } 13867 while (elem_i < result_len) : (elem_i += 1) { 13868 const rhs_elem_i = elem_i - lhs_len; 13869 const elem_index = try mod.intRef(Type.usize, elem_i); 13870 const rhs_index = try mod.intRef(Type.usize, rhs_elem_i); 13871 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 13872 const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{ 13873 .array_cat_offset = inst_data.src_node, 13874 .elem_index = @intCast(rhs_elem_i), 13875 } }; 13876 const init = try sema.elemVal(block, operand_src, rhs, rhs_index, src, true); 13877 try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store); 13878 } 13879 if (res_sent_val) |sent_val| { 13880 const elem_index = try mod.intRef(Type.usize, result_len); 13881 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 13882 const init = Air.internedToRef((try mod.getCoerced(sent_val, lhs_info.elem_type)).toIntern()); 13883 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 13884 } 13885 13886 return alloc; 13887 } 13888 13889 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); 13890 { 13891 var elem_i: u32 = 0; 13892 while (elem_i < lhs_len) : (elem_i += 1) { 13893 const index = try mod.intRef(Type.usize, elem_i); 13894 const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{ 13895 .array_cat_offset = inst_data.src_node, 13896 .elem_index = elem_i, 13897 } }; 13898 const init = try sema.elemVal(block, operand_src, lhs, index, src, true); 13899 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src); 13900 } 13901 while (elem_i < result_len) : (elem_i += 1) { 13902 const rhs_elem_i = elem_i - lhs_len; 13903 const index = try mod.intRef(Type.usize, rhs_elem_i); 13904 const operand_src: LazySrcLoc = .{ .array_cat_rhs = .{ 13905 .array_cat_offset = inst_data.src_node, 13906 .elem_index = @intCast(rhs_elem_i), 13907 } }; 13908 const init = try sema.elemVal(block, operand_src, rhs, index, src, true); 13909 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src); 13910 } 13911 } 13912 13913 return block.addAggregateInit(result_ty, element_refs); 13914 } 13915 13916 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo { 13917 const mod = sema.mod; 13918 const operand_ty = sema.typeOf(operand); 13919 switch (operand_ty.zigTypeTag(mod)) { 13920 .Array => return operand_ty.arrayInfo(mod), 13921 .Pointer => { 13922 const ptr_info = operand_ty.ptrInfo(mod); 13923 switch (ptr_info.flags.size) { 13924 // TODO: in the Many case here this should only work if the type 13925 // has a sentinel, and this code should compute the length based 13926 // on the sentinel value. 13927 .Slice, .Many => { 13928 const val = try sema.resolveConstDefinedValue(block, src, operand, .{ 13929 .needed_comptime_reason = "slice value being concatenated must be comptime-known", 13930 }); 13931 return Type.ArrayInfo{ 13932 .elem_type = Type.fromInterned(ptr_info.child), 13933 .sentinel = switch (ptr_info.sentinel) { 13934 .none => null, 13935 else => Value.fromInterned(ptr_info.sentinel), 13936 }, 13937 .len = val.sliceLen(mod), 13938 }; 13939 }, 13940 .One => { 13941 if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Array) { 13942 return Type.fromInterned(ptr_info.child).arrayInfo(mod); 13943 } 13944 }, 13945 .C => {}, 13946 } 13947 }, 13948 .Struct => { 13949 if (operand_ty.isTuple(mod) and peer_ty.isIndexable(mod)) { 13950 assert(!peer_ty.isTuple(mod)); 13951 return .{ 13952 .elem_type = peer_ty.elemType2(mod), 13953 .sentinel = null, 13954 .len = operand_ty.arrayLen(mod), 13955 }; 13956 } 13957 }, 13958 else => {}, 13959 } 13960 return null; 13961 } 13962 13963 fn analyzeTupleMul( 13964 sema: *Sema, 13965 block: *Block, 13966 src_node: i32, 13967 operand: Air.Inst.Ref, 13968 factor: usize, 13969 ) CompileError!Air.Inst.Ref { 13970 const mod = sema.mod; 13971 const operand_ty = sema.typeOf(operand); 13972 const src = LazySrcLoc.nodeOffset(src_node); 13973 const len_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; 13974 13975 const tuple_len = operand_ty.structFieldCount(mod); 13976 const final_len = std.math.mul(usize, tuple_len, factor) catch 13977 return sema.fail(block, len_src, "operation results in overflow", .{}); 13978 13979 if (final_len == 0) { 13980 return Air.internedToRef(Value.empty_struct.toIntern()); 13981 } 13982 const types = try sema.arena.alloc(InternPool.Index, final_len); 13983 const values = try sema.arena.alloc(InternPool.Index, final_len); 13984 13985 const opt_runtime_src = rs: { 13986 var runtime_src: ?LazySrcLoc = null; 13987 for (0..tuple_len) |i| { 13988 types[i] = operand_ty.structFieldType(i, mod).toIntern(); 13989 values[i] = operand_ty.structFieldDefaultValue(i, mod).toIntern(); 13990 const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{ 13991 .array_cat_offset = src_node, 13992 .elem_index = @intCast(i), 13993 } }; 13994 if (values[i] == .unreachable_value) { 13995 runtime_src = operand_src; 13996 values[i] = .none; // TODO don't treat unreachable_value as special 13997 } 13998 } 13999 for (0..factor) |i| { 14000 mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]); 14001 mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]); 14002 } 14003 break :rs runtime_src; 14004 }; 14005 14006 const tuple_ty = try mod.intern_pool.getAnonStructType(mod.gpa, .{ 14007 .types = types, 14008 .values = values, 14009 .names = &.{}, 14010 }); 14011 14012 const runtime_src = opt_runtime_src orelse { 14013 const tuple_val = try mod.intern(.{ .aggregate = .{ 14014 .ty = tuple_ty, 14015 .storage = .{ .elems = values }, 14016 } }); 14017 return Air.internedToRef(tuple_val); 14018 }; 14019 14020 try sema.requireRuntimeBlock(block, src, runtime_src); 14021 14022 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); 14023 var i: u32 = 0; 14024 while (i < tuple_len) : (i += 1) { 14025 const operand_src: LazySrcLoc = .{ .array_cat_lhs = .{ 14026 .array_cat_offset = src_node, 14027 .elem_index = i, 14028 } }; 14029 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(i), operand_ty); 14030 } 14031 i = 1; 14032 while (i < factor) : (i += 1) { 14033 @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]); 14034 } 14035 14036 return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs); 14037 } 14038 14039 fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14040 const tracy = trace(@src()); 14041 defer tracy.end(); 14042 14043 const mod = sema.mod; 14044 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14045 const extra = sema.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data; 14046 const uncoerced_lhs = try sema.resolveInst(extra.lhs); 14047 const uncoerced_lhs_ty = sema.typeOf(uncoerced_lhs); 14048 const src: LazySrcLoc = inst_data.src(); 14049 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14050 const operator_src: LazySrcLoc = .{ .node_offset_main_token = inst_data.src_node }; 14051 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14052 14053 const lhs, const lhs_ty = coerced_lhs: { 14054 // If we have a result type, we might be able to do this more efficiently 14055 // by coercing the LHS first. Specifically, if we want an array or vector 14056 // and have a tuple, coerce the tuple immediately. 14057 no_coerce: { 14058 if (extra.res_ty == .none) break :no_coerce; 14059 const res_ty_inst = try sema.resolveInst(extra.res_ty); 14060 const res_ty = try sema.analyzeAsType(block, src, res_ty_inst); 14061 if (res_ty.isGenericPoison()) break :no_coerce; 14062 if (!uncoerced_lhs_ty.isTuple(mod)) break :no_coerce; 14063 const lhs_len = uncoerced_lhs_ty.structFieldCount(mod); 14064 const lhs_dest_ty = switch (res_ty.zigTypeTag(mod)) { 14065 else => break :no_coerce, 14066 .Array => try mod.arrayType(.{ 14067 .child = res_ty.childType(mod).toIntern(), 14068 .len = lhs_len, 14069 .sentinel = if (res_ty.sentinel(mod)) |s| s.toIntern() else .none, 14070 }), 14071 .Vector => try mod.vectorType(.{ 14072 .child = res_ty.childType(mod).toIntern(), 14073 .len = lhs_len, 14074 }), 14075 }; 14076 // Attempt to coerce to this type, but don't emit an error if it fails. Instead, 14077 // just exit out of this path and let the usual error happen later, so that error 14078 // messages are consistent. 14079 const coerced = sema.coerceExtra(block, lhs_dest_ty, uncoerced_lhs, lhs_src, .{ .report_err = false }) catch |err| switch (err) { 14080 error.NotCoercible => break :no_coerce, 14081 else => |e| return e, 14082 }; 14083 break :coerced_lhs .{ coerced, lhs_dest_ty }; 14084 } 14085 break :coerced_lhs .{ uncoerced_lhs, uncoerced_lhs_ty }; 14086 }; 14087 14088 if (lhs_ty.isTuple(mod)) { 14089 // In `**` rhs must be comptime-known, but lhs can be runtime-known 14090 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, .{ 14091 .needed_comptime_reason = "array multiplication factor must be comptime-known", 14092 }); 14093 const factor_casted = try sema.usizeCast(block, rhs_src, factor); 14094 return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted); 14095 } 14096 14097 // Analyze the lhs first, to catch the case that someone tried to do exponentiation 14098 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse { 14099 const msg = msg: { 14100 const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)}); 14101 errdefer msg.destroy(sema.gpa); 14102 switch (lhs_ty.zigTypeTag(mod)) { 14103 .Int, .Float, .ComptimeFloat, .ComptimeInt, .Vector => { 14104 try sema.errNote(block, operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{}); 14105 }, 14106 else => {}, 14107 } 14108 break :msg msg; 14109 }; 14110 return sema.failWithOwnedErrorMsg(block, msg); 14111 }; 14112 14113 // In `**` rhs must be comptime-known, but lhs can be runtime-known 14114 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, .{ 14115 .needed_comptime_reason = "array multiplication factor must be comptime-known", 14116 }); 14117 14118 const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch 14119 return sema.fail(block, rhs_src, "operation results in overflow", .{}); 14120 const result_len = try sema.usizeCast(block, src, result_len_u64); 14121 14122 const result_ty = try mod.arrayType(.{ 14123 .len = result_len, 14124 .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none, 14125 .child = lhs_info.elem_type.toIntern(), 14126 }); 14127 14128 const ptr_addrspace = if (lhs_ty.zigTypeTag(mod) == .Pointer) lhs_ty.ptrAddressSpace(mod) else null; 14129 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); 14130 14131 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { 14132 const lhs_sub_val = if (lhs_ty.isSinglePointer(mod)) 14133 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? 14134 else 14135 lhs_val; 14136 14137 const val = v: { 14138 // Optimization for the common pattern of a single element repeated N times, such 14139 // as zero-filling a byte array. 14140 if (lhs_len == 1 and lhs_info.sentinel == null) { 14141 const elem_val = try lhs_sub_val.elemValue(mod, 0); 14142 break :v try mod.intern(.{ .aggregate = .{ 14143 .ty = result_ty.toIntern(), 14144 .storage = .{ .repeated_elem = elem_val.toIntern() }, 14145 } }); 14146 } 14147 14148 const element_vals = try sema.arena.alloc(InternPool.Index, result_len); 14149 var elem_i: usize = 0; 14150 while (elem_i < result_len) { 14151 var lhs_i: usize = 0; 14152 while (lhs_i < lhs_len) : (lhs_i += 1) { 14153 const elem_val = try lhs_sub_val.elemValue(mod, lhs_i); 14154 element_vals[elem_i] = elem_val.toIntern(); 14155 elem_i += 1; 14156 } 14157 } 14158 break :v try mod.intern(.{ .aggregate = .{ 14159 .ty = result_ty.toIntern(), 14160 .storage = .{ .elems = element_vals }, 14161 } }); 14162 }; 14163 return sema.addConstantMaybeRef(val, ptr_addrspace != null); 14164 } 14165 14166 try sema.requireRuntimeBlock(block, src, lhs_src); 14167 14168 // Grab all the LHS values ahead of time, rather than repeatedly emitting instructions 14169 // to get the same elem values. 14170 const lhs_vals = try sema.arena.alloc(Air.Inst.Ref, lhs_len); 14171 for (lhs_vals, 0..) |*lhs_val, idx| { 14172 const idx_ref = try mod.intRef(Type.usize, idx); 14173 lhs_val.* = try sema.elemVal(block, lhs_src, lhs, idx_ref, src, false); 14174 } 14175 14176 if (ptr_addrspace) |ptr_as| { 14177 const alloc_ty = try sema.ptrType(.{ 14178 .child = result_ty.toIntern(), 14179 .flags = .{ .address_space = ptr_as }, 14180 }); 14181 const alloc = try block.addTy(.alloc, alloc_ty); 14182 const elem_ptr_ty = try sema.ptrType(.{ 14183 .child = lhs_info.elem_type.toIntern(), 14184 .flags = .{ .address_space = ptr_as }, 14185 }); 14186 14187 var elem_i: usize = 0; 14188 while (elem_i < result_len) { 14189 for (lhs_vals) |lhs_val| { 14190 const elem_index = try mod.intRef(Type.usize, elem_i); 14191 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 14192 try sema.storePtr2(block, src, elem_ptr, src, lhs_val, lhs_src, .store); 14193 elem_i += 1; 14194 } 14195 } 14196 if (lhs_info.sentinel) |sent_val| { 14197 const elem_index = try mod.intRef(Type.usize, result_len); 14198 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 14199 const init = Air.internedToRef(sent_val.toIntern()); 14200 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 14201 } 14202 14203 return alloc; 14204 } 14205 14206 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); 14207 for (0..try sema.usizeCast(block, rhs_src, factor)) |i| { 14208 @memcpy(element_refs[i * lhs_len ..][0..lhs_len], lhs_vals); 14209 } 14210 return block.addAggregateInit(result_ty, element_refs); 14211 } 14212 14213 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14214 const mod = sema.mod; 14215 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 14216 const src = inst_data.src(); 14217 const lhs_src = src; 14218 const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 14219 14220 const rhs = try sema.resolveInst(inst_data.operand); 14221 const rhs_ty = sema.typeOf(rhs); 14222 const rhs_scalar_ty = rhs_ty.scalarType(mod); 14223 14224 if (rhs_scalar_ty.isUnsignedInt(mod) or switch (rhs_scalar_ty.zigTypeTag(mod)) { 14225 .Int, .ComptimeInt, .Float, .ComptimeFloat => false, 14226 else => true, 14227 }) { 14228 return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)}); 14229 } 14230 14231 if (rhs_scalar_ty.isAnyFloat()) { 14232 // We handle float negation here to ensure negative zero is represented in the bits. 14233 if (try sema.resolveValue(rhs)) |rhs_val| { 14234 if (rhs_val.isUndef(mod)) return mod.undefRef(rhs_ty); 14235 return Air.internedToRef((try rhs_val.floatNeg(rhs_ty, sema.arena, mod)).toIntern()); 14236 } 14237 try sema.requireRuntimeBlock(block, src, null); 14238 return block.addUnOp(if (block.float_mode == .Optimized) .neg_optimized else .neg, rhs); 14239 } 14240 14241 const lhs = Air.internedToRef((try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0))).toIntern()); 14242 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true); 14243 } 14244 14245 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14246 const mod = sema.mod; 14247 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 14248 const src = inst_data.src(); 14249 const lhs_src = src; 14250 const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 14251 14252 const rhs = try sema.resolveInst(inst_data.operand); 14253 const rhs_ty = sema.typeOf(rhs); 14254 const rhs_scalar_ty = rhs_ty.scalarType(mod); 14255 14256 switch (rhs_scalar_ty.zigTypeTag(mod)) { 14257 .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, 14258 else => return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)}), 14259 } 14260 14261 const lhs = Air.internedToRef((try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0))).toIntern()); 14262 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true); 14263 } 14264 14265 fn zirArithmetic( 14266 sema: *Sema, 14267 block: *Block, 14268 inst: Zir.Inst.Index, 14269 zir_tag: Zir.Inst.Tag, 14270 safety: bool, 14271 ) CompileError!Air.Inst.Ref { 14272 const tracy = trace(@src()); 14273 defer tracy.end(); 14274 14275 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14276 sema.src = .{ .node_offset_bin_op = inst_data.src_node }; 14277 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14278 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14279 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14280 const lhs = try sema.resolveInst(extra.lhs); 14281 const rhs = try sema.resolveInst(extra.rhs); 14282 14283 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, safety); 14284 } 14285 14286 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14287 const mod = sema.mod; 14288 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14289 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14290 sema.src = src; 14291 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14292 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14293 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14294 const lhs = try sema.resolveInst(extra.lhs); 14295 const rhs = try sema.resolveInst(extra.rhs); 14296 const lhs_ty = sema.typeOf(lhs); 14297 const rhs_ty = sema.typeOf(rhs); 14298 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14299 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14300 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14301 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14302 14303 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14304 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14305 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14306 }); 14307 14308 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14309 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14310 14311 const lhs_scalar_ty = lhs_ty.scalarType(mod); 14312 const rhs_scalar_ty = rhs_ty.scalarType(mod); 14313 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14314 14315 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14316 14317 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div); 14318 14319 const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs); 14320 const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs); 14321 14322 if ((lhs_ty.zigTypeTag(mod) == .ComptimeFloat and rhs_ty.zigTypeTag(mod) == .ComptimeInt) or 14323 (lhs_ty.zigTypeTag(mod) == .ComptimeInt and rhs_ty.zigTypeTag(mod) == .ComptimeFloat)) 14324 { 14325 // If it makes a difference whether we coerce to ints or floats before doing the division, error. 14326 // If lhs % rhs is 0, it doesn't matter. 14327 const lhs_val = maybe_lhs_val orelse unreachable; 14328 const rhs_val = maybe_rhs_val orelse unreachable; 14329 const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod) catch unreachable; 14330 if (!rem.compareAllWithZero(.eq, mod)) { 14331 return sema.fail( 14332 block, 14333 src, 14334 "ambiguous coercion of division operands '{}' and '{}'; non-zero remainder '{}'", 14335 .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod), rem.fmtValue(resolved_type, mod) }, 14336 ); 14337 } 14338 } 14339 14340 // TODO: emit compile error when .div is used on integers and there would be an 14341 // ambiguous result between div_floor and div_trunc. 14342 14343 // For integers: 14344 // If the lhs is zero, then zero is returned regardless of rhs. 14345 // If the rhs is zero, compile error for division by zero. 14346 // If the rhs is undefined, compile error because there is a possible 14347 // value (zero) for which the division would be illegal behavior. 14348 // If the lhs is undefined: 14349 // * if lhs type is signed: 14350 // * if rhs is comptime-known and not -1, result is undefined 14351 // * if rhs is -1 or runtime-known, compile error because there is a 14352 // possible value (-min_int / -1) for which division would be 14353 // illegal behavior. 14354 // * if lhs type is unsigned, undef is returned regardless of rhs. 14355 // 14356 // For floats: 14357 // If the rhs is zero: 14358 // * comptime_float: compile error for division by zero. 14359 // * other float type: 14360 // * if the lhs is zero: QNaN 14361 // * otherwise: +Inf or -Inf depending on lhs sign 14362 // If the rhs is undefined: 14363 // * comptime_float: compile error because there is a possible 14364 // value (zero) for which the division would be illegal behavior. 14365 // * other float type: result is undefined 14366 // If the lhs is undefined, result is undefined. 14367 switch (scalar_tag) { 14368 .Int, .ComptimeInt, .ComptimeFloat => { 14369 if (maybe_lhs_val) |lhs_val| { 14370 if (!lhs_val.isUndef(mod)) { 14371 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14372 const scalar_zero = switch (scalar_tag) { 14373 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14374 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14375 else => unreachable, 14376 }; 14377 const zero_val = try sema.splat(resolved_type, scalar_zero); 14378 return Air.internedToRef(zero_val.toIntern()); 14379 } 14380 } 14381 } 14382 if (maybe_rhs_val) |rhs_val| { 14383 if (rhs_val.isUndef(mod)) { 14384 return sema.failWithUseOfUndef(block, rhs_src); 14385 } 14386 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14387 return sema.failWithDivideByZero(block, rhs_src); 14388 } 14389 // TODO: if the RHS is one, return the LHS directly 14390 } 14391 }, 14392 else => {}, 14393 } 14394 14395 const runtime_src = rs: { 14396 if (maybe_lhs_val) |lhs_val| { 14397 if (lhs_val.isUndef(mod)) { 14398 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { 14399 if (maybe_rhs_val) |rhs_val| { 14400 if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { 14401 return mod.undefRef(resolved_type); 14402 } 14403 } 14404 return sema.failWithUseOfUndef(block, rhs_src); 14405 } 14406 return mod.undefRef(resolved_type); 14407 } 14408 14409 if (maybe_rhs_val) |rhs_val| { 14410 if (is_int) { 14411 var overflow_idx: ?usize = null; 14412 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); 14413 if (overflow_idx) |vec_idx| { 14414 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); 14415 } 14416 return Air.internedToRef(res.toIntern()); 14417 } else { 14418 return Air.internedToRef((try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 14419 } 14420 } else { 14421 break :rs rhs_src; 14422 } 14423 } else { 14424 break :rs lhs_src; 14425 } 14426 }; 14427 14428 try sema.requireRuntimeBlock(block, src, runtime_src); 14429 14430 if (block.wantSafety()) { 14431 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14432 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14433 } 14434 14435 const air_tag = if (is_int) blk: { 14436 if (lhs_ty.isSignedInt(mod) or rhs_ty.isSignedInt(mod)) { 14437 return sema.fail( 14438 block, 14439 src, 14440 "division with '{}' and '{}': signed integers must use @divTrunc, @divFloor, or @divExact", 14441 .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod) }, 14442 ); 14443 } 14444 break :blk Air.Inst.Tag.div_trunc; 14445 } else switch (block.float_mode) { 14446 .Optimized => Air.Inst.Tag.div_float_optimized, 14447 .Strict => Air.Inst.Tag.div_float, 14448 }; 14449 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 14450 } 14451 14452 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14453 const mod = sema.mod; 14454 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14455 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14456 sema.src = src; 14457 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14458 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14459 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14460 const lhs = try sema.resolveInst(extra.lhs); 14461 const rhs = try sema.resolveInst(extra.rhs); 14462 const lhs_ty = sema.typeOf(lhs); 14463 const rhs_ty = sema.typeOf(rhs); 14464 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14465 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14466 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14467 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14468 14469 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14470 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14471 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14472 }); 14473 14474 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14475 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14476 14477 const lhs_scalar_ty = lhs_ty.scalarType(mod); 14478 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14479 14480 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14481 14482 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); 14483 14484 const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs); 14485 const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs); 14486 14487 const runtime_src = rs: { 14488 // For integers: 14489 // If the lhs is zero, then zero is returned regardless of rhs. 14490 // If the rhs is zero, compile error for division by zero. 14491 // If the rhs is undefined, compile error because there is a possible 14492 // value (zero) for which the division would be illegal behavior. 14493 // If the lhs is undefined, compile error because there is a possible 14494 // value for which the division would result in a remainder. 14495 // TODO: emit runtime safety for if there is a remainder 14496 // TODO: emit runtime safety for division by zero 14497 // 14498 // For floats: 14499 // If the rhs is zero, compile error for division by zero. 14500 // If the rhs is undefined, compile error because there is a possible 14501 // value (zero) for which the division would be illegal behavior. 14502 // If the lhs is undefined, compile error because there is a possible 14503 // value for which the division would result in a remainder. 14504 if (maybe_lhs_val) |lhs_val| { 14505 if (lhs_val.isUndef(mod)) { 14506 return sema.failWithUseOfUndef(block, rhs_src); 14507 } else { 14508 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14509 const scalar_zero = switch (scalar_tag) { 14510 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14511 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14512 else => unreachable, 14513 }; 14514 const zero_val = try sema.splat(resolved_type, scalar_zero); 14515 return Air.internedToRef(zero_val.toIntern()); 14516 } 14517 } 14518 } 14519 if (maybe_rhs_val) |rhs_val| { 14520 if (rhs_val.isUndef(mod)) { 14521 return sema.failWithUseOfUndef(block, rhs_src); 14522 } 14523 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14524 return sema.failWithDivideByZero(block, rhs_src); 14525 } 14526 // TODO: if the RHS is one, return the LHS directly 14527 } 14528 if (maybe_lhs_val) |lhs_val| { 14529 if (maybe_rhs_val) |rhs_val| { 14530 if (is_int) { 14531 const modulus_val = try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod); 14532 if (!(modulus_val.compareAllWithZero(.eq, mod))) { 14533 return sema.fail(block, src, "exact division produced remainder", .{}); 14534 } 14535 var overflow_idx: ?usize = null; 14536 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); 14537 if (overflow_idx) |vec_idx| { 14538 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); 14539 } 14540 return Air.internedToRef(res.toIntern()); 14541 } else { 14542 const modulus_val = try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod); 14543 if (!(modulus_val.compareAllWithZero(.eq, mod))) { 14544 return sema.fail(block, src, "exact division produced remainder", .{}); 14545 } 14546 return Air.internedToRef((try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 14547 } 14548 } else break :rs rhs_src; 14549 } else break :rs lhs_src; 14550 }; 14551 14552 try sema.requireRuntimeBlock(block, src, runtime_src); 14553 14554 // Depending on whether safety is enabled, we will have a slightly different strategy 14555 // here. The `div_exact` AIR instruction causes undefined behavior if a remainder 14556 // is produced, so in the safety check case, it cannot be used. Instead we do a 14557 // div_trunc and check for remainder. 14558 14559 if (block.wantSafety()) { 14560 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14561 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14562 14563 const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs); 14564 const ok = if (!is_int) ok: { 14565 const floored = try block.addUnOp(.floor, result); 14566 14567 if (resolved_type.zigTypeTag(mod) == .Vector) { 14568 const eql = try block.addCmpVector(result, floored, .eq); 14569 break :ok try block.addInst(.{ 14570 .tag = switch (block.float_mode) { 14571 .Strict => .reduce, 14572 .Optimized => .reduce_optimized, 14573 }, 14574 .data = .{ .reduce = .{ 14575 .operand = eql, 14576 .operation = .And, 14577 } }, 14578 }); 14579 } else { 14580 const is_in_range = try block.addBinOp(switch (block.float_mode) { 14581 .Strict => .cmp_eq, 14582 .Optimized => .cmp_eq_optimized, 14583 }, result, floored); 14584 break :ok is_in_range; 14585 } 14586 } else ok: { 14587 const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); 14588 14589 const scalar_zero = switch (scalar_tag) { 14590 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14591 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14592 else => unreachable, 14593 }; 14594 if (resolved_type.zigTypeTag(mod) == .Vector) { 14595 const zero_val = try sema.splat(resolved_type, scalar_zero); 14596 const zero = Air.internedToRef(zero_val.toIntern()); 14597 const eql = try block.addCmpVector(remainder, zero, .eq); 14598 break :ok try block.addInst(.{ 14599 .tag = .reduce, 14600 .data = .{ .reduce = .{ 14601 .operand = eql, 14602 .operation = .And, 14603 } }, 14604 }); 14605 } else { 14606 const zero = Air.internedToRef(scalar_zero.toIntern()); 14607 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero); 14608 break :ok is_in_range; 14609 } 14610 }; 14611 try sema.addSafetyCheck(block, src, ok, .exact_division_remainder); 14612 return result; 14613 } 14614 14615 return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs); 14616 } 14617 14618 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14619 const mod = sema.mod; 14620 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14621 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14622 sema.src = src; 14623 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14624 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14625 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14626 const lhs = try sema.resolveInst(extra.lhs); 14627 const rhs = try sema.resolveInst(extra.rhs); 14628 const lhs_ty = sema.typeOf(lhs); 14629 const rhs_ty = sema.typeOf(rhs); 14630 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14631 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14632 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14633 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14634 14635 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14636 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14637 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14638 }); 14639 14640 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14641 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14642 14643 const lhs_scalar_ty = lhs_ty.scalarType(mod); 14644 const rhs_scalar_ty = rhs_ty.scalarType(mod); 14645 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14646 14647 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14648 14649 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor); 14650 14651 const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs); 14652 const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs); 14653 14654 const runtime_src = rs: { 14655 // For integers: 14656 // If the lhs is zero, then zero is returned regardless of rhs. 14657 // If the rhs is zero, compile error for division by zero. 14658 // If the rhs is undefined, compile error because there is a possible 14659 // value (zero) for which the division would be illegal behavior. 14660 // If the lhs is undefined: 14661 // * if lhs type is signed: 14662 // * if rhs is comptime-known and not -1, result is undefined 14663 // * if rhs is -1 or runtime-known, compile error because there is a 14664 // possible value (-min_int / -1) for which division would be 14665 // illegal behavior. 14666 // * if lhs type is unsigned, undef is returned regardless of rhs. 14667 // TODO: emit runtime safety for division by zero 14668 // 14669 // For floats: 14670 // If the rhs is zero, compile error for division by zero. 14671 // If the rhs is undefined, compile error because there is a possible 14672 // value (zero) for which the division would be illegal behavior. 14673 // If the lhs is undefined, result is undefined. 14674 if (maybe_lhs_val) |lhs_val| { 14675 if (!lhs_val.isUndef(mod)) { 14676 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14677 const scalar_zero = switch (scalar_tag) { 14678 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14679 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14680 else => unreachable, 14681 }; 14682 const zero_val = try sema.splat(resolved_type, scalar_zero); 14683 return Air.internedToRef(zero_val.toIntern()); 14684 } 14685 } 14686 } 14687 if (maybe_rhs_val) |rhs_val| { 14688 if (rhs_val.isUndef(mod)) { 14689 return sema.failWithUseOfUndef(block, rhs_src); 14690 } 14691 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14692 return sema.failWithDivideByZero(block, rhs_src); 14693 } 14694 // TODO: if the RHS is one, return the LHS directly 14695 } 14696 if (maybe_lhs_val) |lhs_val| { 14697 if (lhs_val.isUndef(mod)) { 14698 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { 14699 if (maybe_rhs_val) |rhs_val| { 14700 if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { 14701 return mod.undefRef(resolved_type); 14702 } 14703 } 14704 return sema.failWithUseOfUndef(block, rhs_src); 14705 } 14706 return mod.undefRef(resolved_type); 14707 } 14708 14709 if (maybe_rhs_val) |rhs_val| { 14710 if (is_int) { 14711 return Air.internedToRef((try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 14712 } else { 14713 return Air.internedToRef((try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 14714 } 14715 } else break :rs rhs_src; 14716 } else break :rs lhs_src; 14717 }; 14718 14719 try sema.requireRuntimeBlock(block, src, runtime_src); 14720 14721 if (block.wantSafety()) { 14722 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14723 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14724 } 14725 14726 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); 14727 } 14728 14729 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14730 const mod = sema.mod; 14731 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14732 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14733 sema.src = src; 14734 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14735 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14736 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14737 const lhs = try sema.resolveInst(extra.lhs); 14738 const rhs = try sema.resolveInst(extra.rhs); 14739 const lhs_ty = sema.typeOf(lhs); 14740 const rhs_ty = sema.typeOf(rhs); 14741 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14742 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14743 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14744 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14745 14746 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14747 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14748 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14749 }); 14750 14751 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14752 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14753 14754 const lhs_scalar_ty = lhs_ty.scalarType(mod); 14755 const rhs_scalar_ty = rhs_ty.scalarType(mod); 14756 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14757 14758 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14759 14760 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc); 14761 14762 const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs); 14763 const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs); 14764 14765 const runtime_src = rs: { 14766 // For integers: 14767 // If the lhs is zero, then zero is returned regardless of rhs. 14768 // If the rhs is zero, compile error for division by zero. 14769 // If the rhs is undefined, compile error because there is a possible 14770 // value (zero) for which the division would be illegal behavior. 14771 // If the lhs is undefined: 14772 // * if lhs type is signed: 14773 // * if rhs is comptime-known and not -1, result is undefined 14774 // * if rhs is -1 or runtime-known, compile error because there is a 14775 // possible value (-min_int / -1) for which division would be 14776 // illegal behavior. 14777 // * if lhs type is unsigned, undef is returned regardless of rhs. 14778 // TODO: emit runtime safety for division by zero 14779 // 14780 // For floats: 14781 // If the rhs is zero, compile error for division by zero. 14782 // If the rhs is undefined, compile error because there is a possible 14783 // value (zero) for which the division would be illegal behavior. 14784 // If the lhs is undefined, result is undefined. 14785 if (maybe_lhs_val) |lhs_val| { 14786 if (!lhs_val.isUndef(mod)) { 14787 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14788 const scalar_zero = switch (scalar_tag) { 14789 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14790 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14791 else => unreachable, 14792 }; 14793 const zero_val = try sema.splat(resolved_type, scalar_zero); 14794 return Air.internedToRef(zero_val.toIntern()); 14795 } 14796 } 14797 } 14798 if (maybe_rhs_val) |rhs_val| { 14799 if (rhs_val.isUndef(mod)) { 14800 return sema.failWithUseOfUndef(block, rhs_src); 14801 } 14802 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14803 return sema.failWithDivideByZero(block, rhs_src); 14804 } 14805 } 14806 if (maybe_lhs_val) |lhs_val| { 14807 if (lhs_val.isUndef(mod)) { 14808 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { 14809 if (maybe_rhs_val) |rhs_val| { 14810 if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { 14811 return mod.undefRef(resolved_type); 14812 } 14813 } 14814 return sema.failWithUseOfUndef(block, rhs_src); 14815 } 14816 return mod.undefRef(resolved_type); 14817 } 14818 14819 if (maybe_rhs_val) |rhs_val| { 14820 if (is_int) { 14821 var overflow_idx: ?usize = null; 14822 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); 14823 if (overflow_idx) |vec_idx| { 14824 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); 14825 } 14826 return Air.internedToRef(res.toIntern()); 14827 } else { 14828 return Air.internedToRef((try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 14829 } 14830 } else break :rs rhs_src; 14831 } else break :rs lhs_src; 14832 }; 14833 14834 try sema.requireRuntimeBlock(block, src, runtime_src); 14835 14836 if (block.wantSafety()) { 14837 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14838 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14839 } 14840 14841 return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs); 14842 } 14843 14844 fn addDivIntOverflowSafety( 14845 sema: *Sema, 14846 block: *Block, 14847 src: LazySrcLoc, 14848 resolved_type: Type, 14849 lhs_scalar_ty: Type, 14850 maybe_lhs_val: ?Value, 14851 maybe_rhs_val: ?Value, 14852 casted_lhs: Air.Inst.Ref, 14853 casted_rhs: Air.Inst.Ref, 14854 is_int: bool, 14855 ) CompileError!void { 14856 const mod = sema.mod; 14857 if (!is_int) return; 14858 14859 // If the LHS is unsigned, it cannot cause overflow. 14860 if (!lhs_scalar_ty.isSignedInt(mod)) return; 14861 14862 // If the LHS is widened to a larger integer type, no overflow is possible. 14863 if (lhs_scalar_ty.intInfo(mod).bits < resolved_type.intInfo(mod).bits) { 14864 return; 14865 } 14866 14867 const min_int = try resolved_type.minInt(mod, resolved_type); 14868 const neg_one_scalar = try mod.intValue(lhs_scalar_ty, -1); 14869 const neg_one = try sema.splat(resolved_type, neg_one_scalar); 14870 14871 // If the LHS is comptime-known to be not equal to the min int, 14872 // no overflow is possible. 14873 if (maybe_lhs_val) |lhs_val| { 14874 if (try lhs_val.compareAll(.neq, min_int, resolved_type, mod)) return; 14875 } 14876 14877 // If the RHS is comptime-known to not be equal to -1, no overflow is possible. 14878 if (maybe_rhs_val) |rhs_val| { 14879 if (try rhs_val.compareAll(.neq, neg_one, resolved_type, mod)) return; 14880 } 14881 14882 var ok: Air.Inst.Ref = .none; 14883 if (resolved_type.zigTypeTag(mod) == .Vector) { 14884 if (maybe_lhs_val == null) { 14885 const min_int_ref = Air.internedToRef(min_int.toIntern()); 14886 ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq); 14887 } 14888 if (maybe_rhs_val == null) { 14889 const neg_one_ref = Air.internedToRef(neg_one.toIntern()); 14890 const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq); 14891 if (ok == .none) { 14892 ok = rhs_ok; 14893 } else { 14894 ok = try block.addBinOp(.bool_or, ok, rhs_ok); 14895 } 14896 } 14897 assert(ok != .none); 14898 ok = try block.addInst(.{ 14899 .tag = .reduce, 14900 .data = .{ .reduce = .{ 14901 .operand = ok, 14902 .operation = .And, 14903 } }, 14904 }); 14905 } else { 14906 if (maybe_lhs_val == null) { 14907 const min_int_ref = Air.internedToRef(min_int.toIntern()); 14908 ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); 14909 } 14910 if (maybe_rhs_val == null) { 14911 const neg_one_ref = Air.internedToRef(neg_one.toIntern()); 14912 const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref); 14913 if (ok == .none) { 14914 ok = rhs_ok; 14915 } else { 14916 ok = try block.addBinOp(.bool_or, ok, rhs_ok); 14917 } 14918 } 14919 assert(ok != .none); 14920 } 14921 try sema.addSafetyCheck(block, src, ok, .integer_overflow); 14922 } 14923 14924 fn addDivByZeroSafety( 14925 sema: *Sema, 14926 block: *Block, 14927 src: LazySrcLoc, 14928 resolved_type: Type, 14929 maybe_rhs_val: ?Value, 14930 casted_rhs: Air.Inst.Ref, 14931 is_int: bool, 14932 ) CompileError!void { 14933 // Strict IEEE floats have well-defined division by zero. 14934 if (!is_int and block.float_mode == .Strict) return; 14935 14936 // If rhs was comptime-known to be zero a compile error would have been 14937 // emitted above. 14938 if (maybe_rhs_val != null) return; 14939 14940 const mod = sema.mod; 14941 const scalar_zero = if (is_int) 14942 try mod.intValue(resolved_type.scalarType(mod), 0) 14943 else 14944 try mod.floatValue(resolved_type.scalarType(mod), 0.0); 14945 const ok = if (resolved_type.zigTypeTag(mod) == .Vector) ok: { 14946 const zero_val = try sema.splat(resolved_type, scalar_zero); 14947 const zero = Air.internedToRef(zero_val.toIntern()); 14948 const ok = try block.addCmpVector(casted_rhs, zero, .neq); 14949 break :ok try block.addInst(.{ 14950 .tag = if (is_int) .reduce else .reduce_optimized, 14951 .data = .{ .reduce = .{ 14952 .operand = ok, 14953 .operation = .And, 14954 } }, 14955 }); 14956 } else ok: { 14957 const zero = Air.internedToRef(scalar_zero.toIntern()); 14958 break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); 14959 }; 14960 try sema.addSafetyCheck(block, src, ok, .divide_by_zero); 14961 } 14962 14963 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag { 14964 if (is_int) return normal; 14965 return switch (block.float_mode) { 14966 .Strict => normal, 14967 .Optimized => optimized, 14968 }; 14969 } 14970 14971 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14972 const mod = sema.mod; 14973 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 14974 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14975 sema.src = src; 14976 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14977 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14978 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14979 const lhs = try sema.resolveInst(extra.lhs); 14980 const rhs = try sema.resolveInst(extra.rhs); 14981 const lhs_ty = sema.typeOf(lhs); 14982 const rhs_ty = sema.typeOf(rhs); 14983 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14984 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14985 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14986 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14987 14988 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14989 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14990 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14991 }); 14992 14993 const is_vector = resolved_type.zigTypeTag(mod) == .Vector; 14994 14995 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14996 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14997 14998 const lhs_scalar_ty = lhs_ty.scalarType(mod); 14999 const rhs_scalar_ty = rhs_ty.scalarType(mod); 15000 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 15001 15002 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 15003 15004 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem); 15005 15006 const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs); 15007 const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs); 15008 15009 const runtime_src = rs: { 15010 // For integers: 15011 // Either operand being undef is a compile error because there exists 15012 // a possible value (TODO what is it?) that would invoke illegal behavior. 15013 // TODO: can lhs undef be handled better? 15014 // 15015 // For floats: 15016 // If the rhs is zero, compile error for division by zero. 15017 // If the rhs is undefined, compile error because there is a possible 15018 // value (zero) for which the division would be illegal behavior. 15019 // If the lhs is undefined, result is undefined. 15020 // 15021 // For either one: if the result would be different between @mod and @rem, 15022 // then emit a compile error saying you have to pick one. 15023 if (is_int) { 15024 if (maybe_lhs_val) |lhs_val| { 15025 if (lhs_val.isUndef(mod)) { 15026 return sema.failWithUseOfUndef(block, lhs_src); 15027 } 15028 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15029 const scalar_zero = switch (scalar_tag) { 15030 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 15031 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 15032 else => unreachable, 15033 }; 15034 const zero_val = if (is_vector) Value.fromInterned((try mod.intern(.{ .aggregate = .{ 15035 .ty = resolved_type.toIntern(), 15036 .storage = .{ .repeated_elem = scalar_zero.toIntern() }, 15037 } }))) else scalar_zero; 15038 return Air.internedToRef(zero_val.toIntern()); 15039 } 15040 } else if (lhs_scalar_ty.isSignedInt(mod)) { 15041 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); 15042 } 15043 if (maybe_rhs_val) |rhs_val| { 15044 if (rhs_val.isUndef(mod)) { 15045 return sema.failWithUseOfUndef(block, rhs_src); 15046 } 15047 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 15048 return sema.failWithDivideByZero(block, rhs_src); 15049 } 15050 if (!(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))) { 15051 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); 15052 } 15053 if (maybe_lhs_val) |lhs_val| { 15054 const rem_result = try sema.intRem(resolved_type, lhs_val, rhs_val); 15055 // If this answer could possibly be different by doing `intMod`, 15056 // we must emit a compile error. Otherwise, it's OK. 15057 if (!(try lhs_val.compareAllWithZeroAdvanced(.gte, sema)) and 15058 !(try rem_result.compareAllWithZeroAdvanced(.eq, sema))) 15059 { 15060 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); 15061 } 15062 return Air.internedToRef(rem_result.toIntern()); 15063 } 15064 break :rs lhs_src; 15065 } else if (rhs_scalar_ty.isSignedInt(mod)) { 15066 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); 15067 } else { 15068 break :rs rhs_src; 15069 } 15070 } 15071 // float operands 15072 if (maybe_rhs_val) |rhs_val| { 15073 if (rhs_val.isUndef(mod)) { 15074 return sema.failWithUseOfUndef(block, rhs_src); 15075 } 15076 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 15077 return sema.failWithDivideByZero(block, rhs_src); 15078 } 15079 if (!(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))) { 15080 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); 15081 } 15082 if (maybe_lhs_val) |lhs_val| { 15083 if (lhs_val.isUndef(mod) or !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))) { 15084 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); 15085 } 15086 return Air.internedToRef((try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 15087 } else { 15088 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); 15089 } 15090 } else { 15091 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); 15092 } 15093 }; 15094 15095 try sema.requireRuntimeBlock(block, src, runtime_src); 15096 15097 if (block.wantSafety()) { 15098 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15099 } 15100 15101 const air_tag = airTag(block, is_int, .rem, .rem_optimized); 15102 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15103 } 15104 15105 fn intRem( 15106 sema: *Sema, 15107 ty: Type, 15108 lhs: Value, 15109 rhs: Value, 15110 ) CompileError!Value { 15111 const mod = sema.mod; 15112 if (ty.zigTypeTag(mod) == .Vector) { 15113 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 15114 const scalar_ty = ty.scalarType(mod); 15115 for (result_data, 0..) |*scalar, i| { 15116 const lhs_elem = try lhs.elemValue(mod, i); 15117 const rhs_elem = try rhs.elemValue(mod, i); 15118 scalar.* = try (try sema.intRemScalar(lhs_elem, rhs_elem, scalar_ty)).intern(scalar_ty, mod); 15119 } 15120 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 15121 .ty = ty.toIntern(), 15122 .storage = .{ .elems = result_data }, 15123 } }))); 15124 } 15125 return sema.intRemScalar(lhs, rhs, ty); 15126 } 15127 15128 fn intRemScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) CompileError!Value { 15129 const mod = sema.mod; 15130 // TODO is this a performance issue? maybe we should try the operation without 15131 // resorting to BigInt first. 15132 var lhs_space: Value.BigIntSpace = undefined; 15133 var rhs_space: Value.BigIntSpace = undefined; 15134 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 15135 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 15136 const limbs_q = try sema.arena.alloc( 15137 math.big.Limb, 15138 lhs_bigint.limbs.len, 15139 ); 15140 const limbs_r = try sema.arena.alloc( 15141 math.big.Limb, 15142 // TODO: consider reworking Sema to re-use Values rather than 15143 // always producing new Value objects. 15144 rhs_bigint.limbs.len, 15145 ); 15146 const limbs_buffer = try sema.arena.alloc( 15147 math.big.Limb, 15148 math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), 15149 ); 15150 var result_q = math.big.int.Mutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; 15151 var result_r = math.big.int.Mutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; 15152 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); 15153 return mod.intValue_big(scalar_ty, result_r.toConst()); 15154 } 15155 15156 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15157 const mod = sema.mod; 15158 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15159 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 15160 sema.src = src; 15161 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 15162 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 15163 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15164 const lhs = try sema.resolveInst(extra.lhs); 15165 const rhs = try sema.resolveInst(extra.rhs); 15166 const lhs_ty = sema.typeOf(lhs); 15167 const rhs_ty = sema.typeOf(rhs); 15168 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 15169 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 15170 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15171 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 15172 15173 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 15174 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 15175 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 15176 }); 15177 15178 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15179 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15180 15181 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 15182 15183 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 15184 15185 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod); 15186 15187 const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs); 15188 const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs); 15189 15190 const runtime_src = rs: { 15191 // For integers: 15192 // Either operand being undef is a compile error because there exists 15193 // a possible value (TODO what is it?) that would invoke illegal behavior. 15194 // TODO: can lhs zero be handled better? 15195 // TODO: can lhs undef be handled better? 15196 // 15197 // For floats: 15198 // If the rhs is zero, compile error for division by zero. 15199 // If the rhs is undefined, compile error because there is a possible 15200 // value (zero) for which the division would be illegal behavior. 15201 // If the lhs is undefined, result is undefined. 15202 if (is_int) { 15203 if (maybe_lhs_val) |lhs_val| { 15204 if (lhs_val.isUndef(mod)) { 15205 return sema.failWithUseOfUndef(block, lhs_src); 15206 } 15207 } 15208 if (maybe_rhs_val) |rhs_val| { 15209 if (rhs_val.isUndef(mod)) { 15210 return sema.failWithUseOfUndef(block, rhs_src); 15211 } 15212 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 15213 return sema.failWithDivideByZero(block, rhs_src); 15214 } 15215 if (maybe_lhs_val) |lhs_val| { 15216 return Air.internedToRef((try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 15217 } 15218 break :rs lhs_src; 15219 } else { 15220 break :rs rhs_src; 15221 } 15222 } 15223 // float operands 15224 if (maybe_rhs_val) |rhs_val| { 15225 if (rhs_val.isUndef(mod)) { 15226 return sema.failWithUseOfUndef(block, rhs_src); 15227 } 15228 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 15229 return sema.failWithDivideByZero(block, rhs_src); 15230 } 15231 } 15232 if (maybe_lhs_val) |lhs_val| { 15233 if (lhs_val.isUndef(mod)) { 15234 return mod.undefRef(resolved_type); 15235 } 15236 if (maybe_rhs_val) |rhs_val| { 15237 return Air.internedToRef((try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 15238 } else break :rs rhs_src; 15239 } else break :rs lhs_src; 15240 }; 15241 15242 try sema.requireRuntimeBlock(block, src, runtime_src); 15243 15244 if (block.wantSafety()) { 15245 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15246 } 15247 15248 const air_tag = airTag(block, is_int, .mod, .mod_optimized); 15249 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15250 } 15251 15252 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15253 const mod = sema.mod; 15254 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 15255 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 15256 sema.src = src; 15257 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 15258 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 15259 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15260 const lhs = try sema.resolveInst(extra.lhs); 15261 const rhs = try sema.resolveInst(extra.rhs); 15262 const lhs_ty = sema.typeOf(lhs); 15263 const rhs_ty = sema.typeOf(rhs); 15264 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 15265 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 15266 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15267 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 15268 15269 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 15270 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 15271 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 15272 }); 15273 15274 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15275 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15276 15277 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 15278 15279 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 15280 15281 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem); 15282 15283 const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs); 15284 const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs); 15285 15286 const runtime_src = rs: { 15287 // For integers: 15288 // Either operand being undef is a compile error because there exists 15289 // a possible value (TODO what is it?) that would invoke illegal behavior. 15290 // TODO: can lhs zero be handled better? 15291 // TODO: can lhs undef be handled better? 15292 // 15293 // For floats: 15294 // If the rhs is zero, compile error for division by zero. 15295 // If the rhs is undefined, compile error because there is a possible 15296 // value (zero) for which the division would be illegal behavior. 15297 // If the lhs is undefined, result is undefined. 15298 if (is_int) { 15299 if (maybe_lhs_val) |lhs_val| { 15300 if (lhs_val.isUndef(mod)) { 15301 return sema.failWithUseOfUndef(block, lhs_src); 15302 } 15303 } 15304 if (maybe_rhs_val) |rhs_val| { 15305 if (rhs_val.isUndef(mod)) { 15306 return sema.failWithUseOfUndef(block, rhs_src); 15307 } 15308 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 15309 return sema.failWithDivideByZero(block, rhs_src); 15310 } 15311 if (maybe_lhs_val) |lhs_val| { 15312 return Air.internedToRef((try sema.intRem(resolved_type, lhs_val, rhs_val)).toIntern()); 15313 } 15314 break :rs lhs_src; 15315 } else { 15316 break :rs rhs_src; 15317 } 15318 } 15319 // float operands 15320 if (maybe_rhs_val) |rhs_val| { 15321 if (rhs_val.isUndef(mod)) { 15322 return sema.failWithUseOfUndef(block, rhs_src); 15323 } 15324 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 15325 return sema.failWithDivideByZero(block, rhs_src); 15326 } 15327 } 15328 if (maybe_lhs_val) |lhs_val| { 15329 if (lhs_val.isUndef(mod)) { 15330 return mod.undefRef(resolved_type); 15331 } 15332 if (maybe_rhs_val) |rhs_val| { 15333 return Air.internedToRef((try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 15334 } else break :rs rhs_src; 15335 } else break :rs lhs_src; 15336 }; 15337 15338 try sema.requireRuntimeBlock(block, src, runtime_src); 15339 15340 if (block.wantSafety()) { 15341 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); 15342 } 15343 15344 const air_tag = airTag(block, is_int, .rem, .rem_optimized); 15345 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 15346 } 15347 15348 fn zirOverflowArithmetic( 15349 sema: *Sema, 15350 block: *Block, 15351 extended: Zir.Inst.Extended.InstData, 15352 zir_tag: Zir.Inst.Extended, 15353 ) CompileError!Air.Inst.Ref { 15354 const tracy = trace(@src()); 15355 defer tracy.end(); 15356 15357 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 15358 const src = LazySrcLoc.nodeOffset(extra.node); 15359 15360 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 15361 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 15362 15363 const uncasted_lhs = try sema.resolveInst(extra.lhs); 15364 const uncasted_rhs = try sema.resolveInst(extra.rhs); 15365 15366 const lhs_ty = sema.typeOf(uncasted_lhs); 15367 const rhs_ty = sema.typeOf(uncasted_rhs); 15368 const mod = sema.mod; 15369 const ip = &mod.intern_pool; 15370 15371 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15372 15373 const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs }; 15374 const dest_ty = if (zir_tag == .shl_with_overflow) 15375 lhs_ty 15376 else 15377 try sema.resolvePeerTypes(block, src, instructions, .{ 15378 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 15379 }); 15380 15381 const rhs_dest_ty = if (zir_tag == .shl_with_overflow) 15382 try sema.log2IntType(block, lhs_ty, src) 15383 else 15384 dest_ty; 15385 15386 const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src); 15387 const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src); 15388 15389 if (dest_ty.scalarType(mod).zigTypeTag(mod) != .Int) { 15390 return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)}); 15391 } 15392 15393 const maybe_lhs_val = try sema.resolveValue(lhs); 15394 const maybe_rhs_val = try sema.resolveValue(rhs); 15395 15396 const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty); 15397 const overflow_ty = Type.fromInterned(ip.indexToKey(tuple_ty.toIntern()).anon_struct_type.types.get(ip)[1]); 15398 15399 var result: struct { 15400 inst: Air.Inst.Ref = .none, 15401 wrapped: Value = Value.@"unreachable", 15402 overflow_bit: Value, 15403 } = result: { 15404 const zero_bit = try mod.intValue(Type.u1, 0); 15405 switch (zir_tag) { 15406 .add_with_overflow => { 15407 // If either of the arguments is zero, `false` is returned and the other is stored 15408 // to the result, even if it is undefined.. 15409 // Otherwise, if either of the argument is undefined, undefined is returned. 15410 if (maybe_lhs_val) |lhs_val| { 15411 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15412 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; 15413 } 15414 } 15415 if (maybe_rhs_val) |rhs_val| { 15416 if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15417 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 15418 } 15419 } 15420 if (maybe_lhs_val) |lhs_val| { 15421 if (maybe_rhs_val) |rhs_val| { 15422 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 15423 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15424 } 15425 15426 const result = try sema.intAddWithOverflow(lhs_val, rhs_val, dest_ty); 15427 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15428 } 15429 } 15430 }, 15431 .sub_with_overflow => { 15432 // If the rhs is zero, then the result is lhs and no overflow occured. 15433 // Otherwise, if either result is undefined, both results are undefined. 15434 if (maybe_rhs_val) |rhs_val| { 15435 if (rhs_val.isUndef(mod)) { 15436 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15437 } else if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15438 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 15439 } else if (maybe_lhs_val) |lhs_val| { 15440 if (lhs_val.isUndef(mod)) { 15441 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15442 } 15443 15444 const result = try sema.intSubWithOverflow(lhs_val, rhs_val, dest_ty); 15445 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15446 } 15447 } 15448 }, 15449 .mul_with_overflow => { 15450 // If either of the arguments is zero, the result is zero and no overflow occured. 15451 // If either of the arguments is one, the result is the other and no overflow occured. 15452 // Otherwise, if either of the arguments is undefined, both results are undefined. 15453 const scalar_one = try mod.intValue(dest_ty.scalarType(mod), 1); 15454 if (maybe_lhs_val) |lhs_val| { 15455 if (!lhs_val.isUndef(mod)) { 15456 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15457 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 15458 } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { 15459 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; 15460 } 15461 } 15462 } 15463 15464 if (maybe_rhs_val) |rhs_val| { 15465 if (!rhs_val.isUndef(mod)) { 15466 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15467 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; 15468 } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { 15469 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 15470 } 15471 } 15472 } 15473 15474 if (maybe_lhs_val) |lhs_val| { 15475 if (maybe_rhs_val) |rhs_val| { 15476 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 15477 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15478 } 15479 15480 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, mod); 15481 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15482 } 15483 } 15484 }, 15485 .shl_with_overflow => { 15486 // If lhs is zero, the result is zero and no overflow occurred. 15487 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred. 15488 // Oterhwise if either of the arguments is undefined, both results are undefined. 15489 if (maybe_lhs_val) |lhs_val| { 15490 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15491 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 15492 } 15493 } 15494 if (maybe_rhs_val) |rhs_val| { 15495 if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15496 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 15497 } 15498 } 15499 if (maybe_lhs_val) |lhs_val| { 15500 if (maybe_rhs_val) |rhs_val| { 15501 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 15502 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15503 } 15504 15505 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, mod); 15506 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15507 } 15508 } 15509 }, 15510 else => unreachable, 15511 } 15512 15513 const air_tag: Air.Inst.Tag = switch (zir_tag) { 15514 .add_with_overflow => .add_with_overflow, 15515 .mul_with_overflow => .mul_with_overflow, 15516 .sub_with_overflow => .sub_with_overflow, 15517 .shl_with_overflow => .shl_with_overflow, 15518 else => unreachable, 15519 }; 15520 15521 const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src; 15522 try sema.requireRuntimeBlock(block, src, runtime_src); 15523 15524 return block.addInst(.{ 15525 .tag = air_tag, 15526 .data = .{ .ty_pl = .{ 15527 .ty = Air.internedToRef(tuple_ty.toIntern()), 15528 .payload = try block.sema.addExtra(Air.Bin{ 15529 .lhs = lhs, 15530 .rhs = rhs, 15531 }), 15532 } }, 15533 }); 15534 }; 15535 15536 if (result.inst != .none) { 15537 if (try sema.resolveValue(result.inst)) |some| { 15538 result.wrapped = some; 15539 result.inst = .none; 15540 } 15541 } 15542 15543 if (result.inst == .none) { 15544 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 15545 .ty = tuple_ty.toIntern(), 15546 .storage = .{ .elems = &.{ 15547 result.wrapped.toIntern(), 15548 result.overflow_bit.toIntern(), 15549 } }, 15550 } }))); 15551 } 15552 15553 const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2); 15554 element_refs[0] = result.inst; 15555 element_refs[1] = Air.internedToRef(result.overflow_bit.toIntern()); 15556 return block.addAggregateInit(tuple_ty, element_refs); 15557 } 15558 15559 fn splat(sema: *Sema, ty: Type, val: Value) !Value { 15560 const mod = sema.mod; 15561 if (ty.zigTypeTag(mod) != .Vector) return val; 15562 const repeated = try mod.intern(.{ .aggregate = .{ 15563 .ty = ty.toIntern(), 15564 .storage = .{ .repeated_elem = val.toIntern() }, 15565 } }); 15566 return Value.fromInterned(repeated); 15567 } 15568 15569 fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { 15570 const mod = sema.mod; 15571 const ip = &mod.intern_pool; 15572 const ov_ty = if (ty.zigTypeTag(mod) == .Vector) try mod.vectorType(.{ 15573 .len = ty.vectorLen(mod), 15574 .child = .u1_type, 15575 }) else Type.u1; 15576 15577 const types = [2]InternPool.Index{ ty.toIntern(), ov_ty.toIntern() }; 15578 const values = [2]InternPool.Index{ .none, .none }; 15579 const tuple_ty = try ip.getAnonStructType(mod.gpa, .{ 15580 .types = &types, 15581 .values = &values, 15582 .names = &.{}, 15583 }); 15584 return Type.fromInterned(tuple_ty); 15585 } 15586 15587 fn analyzeArithmetic( 15588 sema: *Sema, 15589 block: *Block, 15590 /// TODO performance investigation: make this comptime? 15591 zir_tag: Zir.Inst.Tag, 15592 lhs: Air.Inst.Ref, 15593 rhs: Air.Inst.Ref, 15594 src: LazySrcLoc, 15595 lhs_src: LazySrcLoc, 15596 rhs_src: LazySrcLoc, 15597 want_safety: bool, 15598 ) CompileError!Air.Inst.Ref { 15599 const mod = sema.mod; 15600 const lhs_ty = sema.typeOf(lhs); 15601 const rhs_ty = sema.typeOf(rhs); 15602 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 15603 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 15604 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15605 15606 if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize(mod)) { 15607 .One, .Slice => {}, 15608 .Many, .C => { 15609 const air_tag: Air.Inst.Tag = switch (zir_tag) { 15610 .add => .ptr_add, 15611 .sub => .ptr_sub, 15612 else => return sema.fail(block, src, "invalid pointer arithmetic operator", .{}), 15613 }; 15614 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); 15615 }, 15616 }; 15617 15618 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 15619 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 15620 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 15621 }); 15622 15623 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15624 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15625 15626 const scalar_type = resolved_type.scalarType(mod); 15627 const scalar_tag = scalar_type.zigTypeTag(mod); 15628 15629 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 15630 15631 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag); 15632 15633 const maybe_lhs_val = try sema.resolveValueIntable(casted_lhs); 15634 const maybe_rhs_val = try sema.resolveValueIntable(casted_rhs); 15635 const runtime_src: LazySrcLoc, const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag = rs: { 15636 switch (zir_tag) { 15637 .add, .add_unsafe => { 15638 // For integers:intAddSat 15639 // If either of the operands are zero, then the other operand is 15640 // returned, even if it is undefined. 15641 // If either of the operands are undefined, it's a compile error 15642 // because there is a possible value for which the addition would 15643 // overflow (max_int), causing illegal behavior. 15644 // For floats: either operand being undef makes the result undef. 15645 if (maybe_lhs_val) |lhs_val| { 15646 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15647 return casted_rhs; 15648 } 15649 } 15650 if (maybe_rhs_val) |rhs_val| { 15651 if (rhs_val.isUndef(mod)) { 15652 if (is_int) { 15653 return sema.failWithUseOfUndef(block, rhs_src); 15654 } else { 15655 return mod.undefRef(resolved_type); 15656 } 15657 } 15658 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15659 return casted_lhs; 15660 } 15661 } 15662 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .add_optimized else .add; 15663 if (maybe_lhs_val) |lhs_val| { 15664 if (lhs_val.isUndef(mod)) { 15665 if (is_int) { 15666 return sema.failWithUseOfUndef(block, lhs_src); 15667 } else { 15668 return mod.undefRef(resolved_type); 15669 } 15670 } 15671 if (maybe_rhs_val) |rhs_val| { 15672 if (is_int) { 15673 var overflow_idx: ?usize = null; 15674 const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type, &overflow_idx); 15675 if (overflow_idx) |vec_idx| { 15676 return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vec_idx); 15677 } 15678 return Air.internedToRef(sum.toIntern()); 15679 } else { 15680 return Air.internedToRef((try Value.floatAdd(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern()); 15681 } 15682 } else break :rs .{ rhs_src, air_tag, .add_safe }; 15683 } else break :rs .{ lhs_src, air_tag, .add_safe }; 15684 }, 15685 .addwrap => { 15686 // Integers only; floats are checked above. 15687 // If either of the operands are zero, the other operand is returned. 15688 // If either of the operands are undefined, the result is undefined. 15689 if (maybe_lhs_val) |lhs_val| { 15690 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15691 return casted_rhs; 15692 } 15693 } 15694 if (maybe_rhs_val) |rhs_val| { 15695 if (rhs_val.isUndef(mod)) { 15696 return mod.undefRef(resolved_type); 15697 } 15698 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15699 return casted_lhs; 15700 } 15701 if (maybe_lhs_val) |lhs_val| { 15702 return Air.internedToRef((try sema.numberAddWrapScalar(lhs_val, rhs_val, resolved_type)).toIntern()); 15703 } else break :rs .{ lhs_src, .add_wrap, .add_wrap }; 15704 } else break :rs .{ rhs_src, .add_wrap, .add_wrap }; 15705 }, 15706 .add_sat => { 15707 // Integers only; floats are checked above. 15708 // If either of the operands are zero, then the other operand is returned. 15709 // If either of the operands are undefined, the result is undefined. 15710 if (maybe_lhs_val) |lhs_val| { 15711 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15712 return casted_rhs; 15713 } 15714 } 15715 if (maybe_rhs_val) |rhs_val| { 15716 if (rhs_val.isUndef(mod)) { 15717 return mod.undefRef(resolved_type); 15718 } 15719 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15720 return casted_lhs; 15721 } 15722 if (maybe_lhs_val) |lhs_val| { 15723 const val = if (scalar_tag == .ComptimeInt) 15724 try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined) 15725 else 15726 try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, mod); 15727 15728 return Air.internedToRef(val.toIntern()); 15729 } else break :rs .{ 15730 lhs_src, 15731 .add_sat, 15732 .add_sat, 15733 }; 15734 } else break :rs .{ 15735 rhs_src, 15736 .add_sat, 15737 .add_sat, 15738 }; 15739 }, 15740 .sub => { 15741 // For integers: 15742 // If the rhs is zero, then the other operand is 15743 // returned, even if it is undefined. 15744 // If either of the operands are undefined, it's a compile error 15745 // because there is a possible value for which the subtraction would 15746 // overflow, causing illegal behavior. 15747 // For floats: either operand being undef makes the result undef. 15748 if (maybe_rhs_val) |rhs_val| { 15749 if (rhs_val.isUndef(mod)) { 15750 if (is_int) { 15751 return sema.failWithUseOfUndef(block, rhs_src); 15752 } else { 15753 return mod.undefRef(resolved_type); 15754 } 15755 } 15756 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15757 return casted_lhs; 15758 } 15759 } 15760 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .sub_optimized else .sub; 15761 if (maybe_lhs_val) |lhs_val| { 15762 if (lhs_val.isUndef(mod)) { 15763 if (is_int) { 15764 return sema.failWithUseOfUndef(block, lhs_src); 15765 } else { 15766 return mod.undefRef(resolved_type); 15767 } 15768 } 15769 if (maybe_rhs_val) |rhs_val| { 15770 if (is_int) { 15771 var overflow_idx: ?usize = null; 15772 const diff = try sema.intSub(lhs_val, rhs_val, resolved_type, &overflow_idx); 15773 if (overflow_idx) |vec_idx| { 15774 return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vec_idx); 15775 } 15776 return Air.internedToRef(diff.toIntern()); 15777 } else { 15778 return Air.internedToRef((try Value.floatSub(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern()); 15779 } 15780 } else break :rs .{ rhs_src, air_tag, .sub_safe }; 15781 } else break :rs .{ lhs_src, air_tag, .sub_safe }; 15782 }, 15783 .subwrap => { 15784 // Integers only; floats are checked above. 15785 // If the RHS is zero, then the other operand is returned, even if it is undefined. 15786 // If either of the operands are undefined, the result is undefined. 15787 if (maybe_rhs_val) |rhs_val| { 15788 if (rhs_val.isUndef(mod)) { 15789 return mod.undefRef(resolved_type); 15790 } 15791 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15792 return casted_lhs; 15793 } 15794 } 15795 if (maybe_lhs_val) |lhs_val| { 15796 if (lhs_val.isUndef(mod)) { 15797 return mod.undefRef(resolved_type); 15798 } 15799 if (maybe_rhs_val) |rhs_val| { 15800 return Air.internedToRef((try sema.numberSubWrapScalar(lhs_val, rhs_val, resolved_type)).toIntern()); 15801 } else break :rs .{ rhs_src, .sub_wrap, .sub_wrap }; 15802 } else break :rs .{ lhs_src, .sub_wrap, .sub_wrap }; 15803 }, 15804 .sub_sat => { 15805 // Integers only; floats are checked above. 15806 // If the RHS is zero, result is LHS. 15807 // If either of the operands are undefined, result is undefined. 15808 if (maybe_rhs_val) |rhs_val| { 15809 if (rhs_val.isUndef(mod)) { 15810 return mod.undefRef(resolved_type); 15811 } 15812 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15813 return casted_lhs; 15814 } 15815 } 15816 if (maybe_lhs_val) |lhs_val| { 15817 if (lhs_val.isUndef(mod)) { 15818 return mod.undefRef(resolved_type); 15819 } 15820 if (maybe_rhs_val) |rhs_val| { 15821 const val = if (scalar_tag == .ComptimeInt) 15822 try sema.intSub(lhs_val, rhs_val, resolved_type, undefined) 15823 else 15824 try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod); 15825 15826 return Air.internedToRef(val.toIntern()); 15827 } else break :rs .{ rhs_src, .sub_sat, .sub_sat }; 15828 } else break :rs .{ lhs_src, .sub_sat, .sub_sat }; 15829 }, 15830 .mul => { 15831 // For integers: 15832 // If either of the operands are zero, the result is zero. 15833 // If either of the operands are one, the result is the other 15834 // operand, even if it is undefined. 15835 // If either of the operands are undefined, it's a compile error 15836 // because there is a possible value for which the addition would 15837 // overflow (max_int), causing illegal behavior. 15838 // For floats: either operand being undef makes the result undef. 15839 // If either of the operands are inf, and the other operand is zero, 15840 // the result is nan. 15841 // If either of the operands are nan, the result is nan. 15842 const scalar_zero = switch (scalar_tag) { 15843 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), 15844 .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), 15845 else => unreachable, 15846 }; 15847 const scalar_one = switch (scalar_tag) { 15848 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), 15849 .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), 15850 else => unreachable, 15851 }; 15852 if (maybe_lhs_val) |lhs_val| { 15853 if (!lhs_val.isUndef(mod)) { 15854 if (lhs_val.isNan(mod)) { 15855 return Air.internedToRef(lhs_val.toIntern()); 15856 } 15857 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) lz: { 15858 if (maybe_rhs_val) |rhs_val| { 15859 if (rhs_val.isNan(mod)) { 15860 return Air.internedToRef(rhs_val.toIntern()); 15861 } 15862 if (rhs_val.isInf(mod)) { 15863 return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan(f128))).toIntern()); 15864 } 15865 } else if (resolved_type.isAnyFloat()) { 15866 break :lz; 15867 } 15868 const zero_val = try sema.splat(resolved_type, scalar_zero); 15869 return Air.internedToRef(zero_val.toIntern()); 15870 } 15871 if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15872 return casted_rhs; 15873 } 15874 } 15875 } 15876 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .mul_optimized else .mul; 15877 if (maybe_rhs_val) |rhs_val| { 15878 if (rhs_val.isUndef(mod)) { 15879 if (is_int) { 15880 return sema.failWithUseOfUndef(block, rhs_src); 15881 } else { 15882 return mod.undefRef(resolved_type); 15883 } 15884 } 15885 if (rhs_val.isNan(mod)) { 15886 return Air.internedToRef(rhs_val.toIntern()); 15887 } 15888 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) rz: { 15889 if (maybe_lhs_val) |lhs_val| { 15890 if (lhs_val.isInf(mod)) { 15891 return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan(f128))).toIntern()); 15892 } 15893 } else if (resolved_type.isAnyFloat()) { 15894 break :rz; 15895 } 15896 const zero_val = try sema.splat(resolved_type, scalar_zero); 15897 return Air.internedToRef(zero_val.toIntern()); 15898 } 15899 if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15900 return casted_lhs; 15901 } 15902 if (maybe_lhs_val) |lhs_val| { 15903 if (lhs_val.isUndef(mod)) { 15904 if (is_int) { 15905 return sema.failWithUseOfUndef(block, lhs_src); 15906 } else { 15907 return mod.undefRef(resolved_type); 15908 } 15909 } 15910 if (is_int) { 15911 var overflow_idx: ?usize = null; 15912 const product = try lhs_val.intMul(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); 15913 if (overflow_idx) |vec_idx| { 15914 return sema.failWithIntegerOverflow(block, src, resolved_type, product, vec_idx); 15915 } 15916 return Air.internedToRef(product.toIntern()); 15917 } else { 15918 return Air.internedToRef((try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 15919 } 15920 } else break :rs .{ lhs_src, air_tag, .mul_safe }; 15921 } else break :rs .{ rhs_src, air_tag, .mul_safe }; 15922 }, 15923 .mulwrap => { 15924 // Integers only; floats are handled above. 15925 // If either of the operands are zero, result is zero. 15926 // If either of the operands are one, result is the other operand. 15927 // If either of the operands are undefined, result is undefined. 15928 const scalar_zero = switch (scalar_tag) { 15929 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), 15930 .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), 15931 else => unreachable, 15932 }; 15933 const scalar_one = switch (scalar_tag) { 15934 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), 15935 .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), 15936 else => unreachable, 15937 }; 15938 if (maybe_lhs_val) |lhs_val| { 15939 if (!lhs_val.isUndef(mod)) { 15940 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15941 const zero_val = try sema.splat(resolved_type, scalar_zero); 15942 return Air.internedToRef(zero_val.toIntern()); 15943 } 15944 if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15945 return casted_rhs; 15946 } 15947 } 15948 } 15949 if (maybe_rhs_val) |rhs_val| { 15950 if (rhs_val.isUndef(mod)) { 15951 return mod.undefRef(resolved_type); 15952 } 15953 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15954 const zero_val = try sema.splat(resolved_type, scalar_zero); 15955 return Air.internedToRef(zero_val.toIntern()); 15956 } 15957 if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15958 return casted_lhs; 15959 } 15960 if (maybe_lhs_val) |lhs_val| { 15961 if (lhs_val.isUndef(mod)) { 15962 return mod.undefRef(resolved_type); 15963 } 15964 return Air.internedToRef((try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, mod)).toIntern()); 15965 } else break :rs .{ lhs_src, .mul_wrap, .mul_wrap }; 15966 } else break :rs .{ rhs_src, .mul_wrap, .mul_wrap }; 15967 }, 15968 .mul_sat => { 15969 // Integers only; floats are checked above. 15970 // If either of the operands are zero, result is zero. 15971 // If either of the operands are one, result is the other operand. 15972 // If either of the operands are undefined, result is undefined. 15973 const scalar_zero = switch (scalar_tag) { 15974 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), 15975 .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), 15976 else => unreachable, 15977 }; 15978 const scalar_one = switch (scalar_tag) { 15979 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), 15980 .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), 15981 else => unreachable, 15982 }; 15983 if (maybe_lhs_val) |lhs_val| { 15984 if (!lhs_val.isUndef(mod)) { 15985 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15986 const zero_val = try sema.splat(resolved_type, scalar_zero); 15987 return Air.internedToRef(zero_val.toIntern()); 15988 } 15989 if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15990 return casted_rhs; 15991 } 15992 } 15993 } 15994 if (maybe_rhs_val) |rhs_val| { 15995 if (rhs_val.isUndef(mod)) { 15996 return mod.undefRef(resolved_type); 15997 } 15998 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15999 const zero_val = try sema.splat(resolved_type, scalar_zero); 16000 return Air.internedToRef(zero_val.toIntern()); 16001 } 16002 if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 16003 return casted_lhs; 16004 } 16005 if (maybe_lhs_val) |lhs_val| { 16006 if (lhs_val.isUndef(mod)) { 16007 return mod.undefRef(resolved_type); 16008 } 16009 16010 const val = if (scalar_tag == .ComptimeInt) 16011 try lhs_val.intMul(rhs_val, resolved_type, undefined, sema.arena, mod) 16012 else 16013 try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, mod); 16014 16015 return Air.internedToRef(val.toIntern()); 16016 } else break :rs .{ lhs_src, .mul_sat, .mul_sat }; 16017 } else break :rs .{ rhs_src, .mul_sat, .mul_sat }; 16018 }, 16019 else => unreachable, 16020 } 16021 }; 16022 16023 try sema.requireRuntimeBlock(block, src, runtime_src); 16024 16025 if (block.wantSafety() and want_safety and scalar_tag == .Int) { 16026 if (mod.backendSupportsFeature(.safety_checked_instructions)) { 16027 if (air_tag != air_tag_safe) { 16028 _ = try sema.preparePanicId(block, .integer_overflow); 16029 } 16030 return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs); 16031 } else { 16032 const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) { 16033 .add => .add_with_overflow, 16034 .sub => .sub_with_overflow, 16035 .mul => .mul_with_overflow, 16036 else => null, 16037 }; 16038 if (maybe_op_ov) |op_ov_tag| { 16039 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(resolved_type); 16040 const op_ov = try block.addInst(.{ 16041 .tag = op_ov_tag, 16042 .data = .{ .ty_pl = .{ 16043 .ty = Air.internedToRef(op_ov_tuple_ty.toIntern()), 16044 .payload = try sema.addExtra(Air.Bin{ 16045 .lhs = casted_lhs, 16046 .rhs = casted_rhs, 16047 }), 16048 } }, 16049 }); 16050 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); 16051 const any_ov_bit = if (resolved_type.zigTypeTag(mod) == .Vector) 16052 try block.addInst(.{ 16053 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 16054 .data = .{ .reduce = .{ 16055 .operand = ov_bit, 16056 .operation = .Or, 16057 } }, 16058 }) 16059 else 16060 ov_bit; 16061 const zero_ov = Air.internedToRef((try mod.intValue(Type.u1, 0)).toIntern()); 16062 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); 16063 16064 try sema.addSafetyCheck(block, src, no_ov, .integer_overflow); 16065 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); 16066 } 16067 } 16068 } 16069 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 16070 } 16071 16072 fn analyzePtrArithmetic( 16073 sema: *Sema, 16074 block: *Block, 16075 op_src: LazySrcLoc, 16076 ptr: Air.Inst.Ref, 16077 uncasted_offset: Air.Inst.Ref, 16078 air_tag: Air.Inst.Tag, 16079 ptr_src: LazySrcLoc, 16080 offset_src: LazySrcLoc, 16081 ) CompileError!Air.Inst.Ref { 16082 // TODO if the operand is comptime-known to be negative, or is a negative int, 16083 // coerce to isize instead of usize. 16084 const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); 16085 const mod = sema.mod; 16086 const opt_ptr_val = try sema.resolveValue(ptr); 16087 const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset); 16088 const ptr_ty = sema.typeOf(ptr); 16089 const ptr_info = ptr_ty.ptrInfo(mod); 16090 assert(ptr_info.flags.size == .Many or ptr_info.flags.size == .C); 16091 16092 const new_ptr_ty = t: { 16093 // Calculate the new pointer alignment. 16094 // This code is duplicated in `elemPtrType`. 16095 if (ptr_info.flags.alignment == .none) { 16096 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. 16097 break :t ptr_ty; 16098 } 16099 // If the addend is not a comptime-known value we can still count on 16100 // it being a multiple of the type size. 16101 const elem_size = Type.fromInterned(ptr_info.child).abiSize(mod); 16102 const addend = if (opt_off_val) |off_val| a: { 16103 const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(mod)); 16104 break :a elem_size * off_int; 16105 } else elem_size; 16106 16107 // The resulting pointer is aligned to the lcd between the offset (an 16108 // arbitrary number) and the alignment factor (always a power of two, 16109 // non zero). 16110 const new_align: Alignment = @enumFromInt(@min( 16111 @ctz(addend), 16112 @intFromEnum(ptr_info.flags.alignment), 16113 )); 16114 assert(new_align != .none); 16115 16116 break :t try sema.ptrType(.{ 16117 .child = ptr_info.child, 16118 .sentinel = ptr_info.sentinel, 16119 .flags = .{ 16120 .size = ptr_info.flags.size, 16121 .alignment = new_align, 16122 .is_const = ptr_info.flags.is_const, 16123 .is_volatile = ptr_info.flags.is_volatile, 16124 .is_allowzero = ptr_info.flags.is_allowzero, 16125 .address_space = ptr_info.flags.address_space, 16126 }, 16127 }); 16128 }; 16129 16130 const runtime_src = rs: { 16131 if (opt_ptr_val) |ptr_val| { 16132 if (opt_off_val) |offset_val| { 16133 if (ptr_val.isUndef(mod)) return mod.undefRef(new_ptr_ty); 16134 16135 const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(mod)); 16136 if (offset_int == 0) return ptr; 16137 if (try ptr_val.getUnsignedIntAdvanced(mod, sema)) |addr| { 16138 const elem_size = Type.fromInterned(ptr_info.child).abiSize(mod); 16139 const new_addr = switch (air_tag) { 16140 .ptr_add => addr + elem_size * offset_int, 16141 .ptr_sub => addr - elem_size * offset_int, 16142 else => unreachable, 16143 }; 16144 const new_ptr_val = try mod.ptrIntValue(new_ptr_ty, new_addr); 16145 return Air.internedToRef(new_ptr_val.toIntern()); 16146 } 16147 if (air_tag == .ptr_sub) { 16148 return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{}); 16149 } 16150 const new_ptr_val = try ptr_val.elemPtr(new_ptr_ty, offset_int, mod); 16151 return Air.internedToRef(new_ptr_val.toIntern()); 16152 } else break :rs offset_src; 16153 } else break :rs ptr_src; 16154 }; 16155 16156 try sema.requireRuntimeBlock(block, op_src, runtime_src); 16157 return block.addInst(.{ 16158 .tag = air_tag, 16159 .data = .{ .ty_pl = .{ 16160 .ty = Air.internedToRef(new_ptr_ty.toIntern()), 16161 .payload = try sema.addExtra(Air.Bin{ 16162 .lhs = ptr, 16163 .rhs = offset, 16164 }), 16165 } }, 16166 }); 16167 } 16168 16169 fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16170 const tracy = trace(@src()); 16171 defer tracy.end(); 16172 16173 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16174 const src = inst_data.src(); 16175 const ptr_src = src; // TODO better source location 16176 const ptr = try sema.resolveInst(inst_data.operand); 16177 return sema.analyzeLoad(block, src, ptr, ptr_src); 16178 } 16179 16180 fn zirAsm( 16181 sema: *Sema, 16182 block: *Block, 16183 extended: Zir.Inst.Extended.InstData, 16184 tmpl_is_expr: bool, 16185 ) CompileError!Air.Inst.Ref { 16186 const tracy = trace(@src()); 16187 defer tracy.end(); 16188 16189 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); 16190 const src = LazySrcLoc.nodeOffset(extra.data.src_node); 16191 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node }; 16192 const outputs_len: u5 = @truncate(extended.small); 16193 const inputs_len: u5 = @truncate(extended.small >> 5); 16194 const clobbers_len: u5 = @truncate(extended.small >> 10); 16195 const is_volatile = @as(u1, @truncate(extended.small >> 15)) != 0; 16196 const is_global_assembly = sema.func_index == .none; 16197 16198 const asm_source: []const u8 = if (tmpl_is_expr) blk: { 16199 const tmpl: Zir.Inst.Ref = @enumFromInt(extra.data.asm_source); 16200 const s: []const u8 = try sema.resolveConstString(block, src, tmpl, .{ 16201 .needed_comptime_reason = "assembly code must be comptime-known", 16202 }); 16203 break :blk s; 16204 } else sema.code.nullTerminatedString(extra.data.asm_source); 16205 16206 if (is_global_assembly) { 16207 if (outputs_len != 0) { 16208 return sema.fail(block, src, "module-level assembly does not support outputs", .{}); 16209 } 16210 if (inputs_len != 0) { 16211 return sema.fail(block, src, "module-level assembly does not support inputs", .{}); 16212 } 16213 if (clobbers_len != 0) { 16214 return sema.fail(block, src, "module-level assembly does not support clobbers", .{}); 16215 } 16216 if (is_volatile) { 16217 return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{}); 16218 } 16219 try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source); 16220 return .void_value; 16221 } 16222 16223 if (block.is_comptime) { 16224 try sema.requireRuntimeBlock(block, src, null); 16225 } 16226 16227 var extra_i = extra.end; 16228 var output_type_bits = extra.data.output_type_bits; 16229 var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len; 16230 16231 const ConstraintName = struct { c: []const u8, n: []const u8 }; 16232 const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len); 16233 const outputs = try sema.arena.alloc(ConstraintName, outputs_len); 16234 var expr_ty = Air.Inst.Ref.void_type; 16235 16236 for (out_args, 0..) |*arg, out_i| { 16237 const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); 16238 extra_i = output.end; 16239 16240 const is_type = @as(u1, @truncate(output_type_bits)) != 0; 16241 output_type_bits >>= 1; 16242 16243 if (is_type) { 16244 // Indicate the output is the asm instruction return value. 16245 arg.* = .none; 16246 const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); 16247 try sema.queueFullTypeResolution(out_ty); 16248 expr_ty = Air.internedToRef(out_ty.toIntern()); 16249 } else { 16250 arg.* = try sema.resolveInst(output.data.operand); 16251 } 16252 16253 const constraint = sema.code.nullTerminatedString(output.data.constraint); 16254 const name = sema.code.nullTerminatedString(output.data.name); 16255 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; 16256 16257 outputs[out_i] = .{ .c = constraint, .n = name }; 16258 } 16259 16260 const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len); 16261 const inputs = try sema.arena.alloc(ConstraintName, inputs_len); 16262 const mod = sema.mod; 16263 16264 for (args, 0..) |*arg, arg_i| { 16265 const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); 16266 extra_i = input.end; 16267 16268 const uncasted_arg = try sema.resolveInst(input.data.operand); 16269 const uncasted_arg_ty = sema.typeOf(uncasted_arg); 16270 switch (uncasted_arg_ty.zigTypeTag(mod)) { 16271 .ComptimeInt => arg.* = try sema.coerce(block, Type.usize, uncasted_arg, src), 16272 .ComptimeFloat => arg.* = try sema.coerce(block, Type.f64, uncasted_arg, src), 16273 else => { 16274 arg.* = uncasted_arg; 16275 try sema.queueFullTypeResolution(uncasted_arg_ty); 16276 }, 16277 } 16278 16279 const constraint = sema.code.nullTerminatedString(input.data.constraint); 16280 const name = sema.code.nullTerminatedString(input.data.name); 16281 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; 16282 inputs[arg_i] = .{ .c = constraint, .n = name }; 16283 } 16284 16285 const clobbers = try sema.arena.alloc([]const u8, clobbers_len); 16286 for (clobbers) |*name| { 16287 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); 16288 extra_i += 1; 16289 16290 needed_capacity += name.*.len / 4 + 1; 16291 } 16292 16293 needed_capacity += (asm_source.len + 3) / 4; 16294 16295 const gpa = sema.gpa; 16296 try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); 16297 const asm_air = try block.addInst(.{ 16298 .tag = .assembly, 16299 .data = .{ .ty_pl = .{ 16300 .ty = expr_ty, 16301 .payload = sema.addExtraAssumeCapacity(Air.Asm{ 16302 .source_len = @intCast(asm_source.len), 16303 .outputs_len = outputs_len, 16304 .inputs_len = @intCast(args.len), 16305 .flags = (@as(u32, @intFromBool(is_volatile)) << 31) | @as(u32, @intCast(clobbers.len)), 16306 }), 16307 } }, 16308 }); 16309 sema.appendRefsAssumeCapacity(out_args); 16310 sema.appendRefsAssumeCapacity(args); 16311 for (outputs) |o| { 16312 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16313 @memcpy(buffer[0..o.c.len], o.c); 16314 buffer[o.c.len] = 0; 16315 @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n); 16316 buffer[o.c.len + 1 + o.n.len] = 0; 16317 sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4; 16318 } 16319 for (inputs) |input| { 16320 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16321 @memcpy(buffer[0..input.c.len], input.c); 16322 buffer[input.c.len] = 0; 16323 @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n); 16324 buffer[input.c.len + 1 + input.n.len] = 0; 16325 sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; 16326 } 16327 for (clobbers) |clobber| { 16328 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16329 @memcpy(buffer[0..clobber.len], clobber); 16330 buffer[clobber.len] = 0; 16331 sema.air_extra.items.len += clobber.len / 4 + 1; 16332 } 16333 { 16334 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 16335 @memcpy(buffer[0..asm_source.len], asm_source); 16336 sema.air_extra.items.len += (asm_source.len + 3) / 4; 16337 } 16338 return asm_air; 16339 } 16340 16341 /// Only called for equality operators. See also `zirCmp`. 16342 fn zirCmpEq( 16343 sema: *Sema, 16344 block: *Block, 16345 inst: Zir.Inst.Index, 16346 op: std.math.CompareOperator, 16347 air_tag: Air.Inst.Tag, 16348 ) CompileError!Air.Inst.Ref { 16349 const tracy = trace(@src()); 16350 defer tracy.end(); 16351 16352 const mod = sema.mod; 16353 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 16354 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 16355 const src: LazySrcLoc = inst_data.src(); 16356 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 16357 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 16358 const lhs = try sema.resolveInst(extra.lhs); 16359 const rhs = try sema.resolveInst(extra.rhs); 16360 16361 const lhs_ty = sema.typeOf(lhs); 16362 const rhs_ty = sema.typeOf(rhs); 16363 const lhs_ty_tag = lhs_ty.zigTypeTag(mod); 16364 const rhs_ty_tag = rhs_ty.zigTypeTag(mod); 16365 if (lhs_ty_tag == .Null and rhs_ty_tag == .Null) { 16366 // null == null, null != null 16367 return if (op == .eq) .bool_true else .bool_false; 16368 } 16369 16370 // comparing null with optionals 16371 if (lhs_ty_tag == .Null and (rhs_ty_tag == .Optional or rhs_ty.isCPtr(mod))) { 16372 return sema.analyzeIsNull(block, src, rhs, op == .neq); 16373 } 16374 if (rhs_ty_tag == .Null and (lhs_ty_tag == .Optional or lhs_ty.isCPtr(mod))) { 16375 return sema.analyzeIsNull(block, src, lhs, op == .neq); 16376 } 16377 16378 if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { 16379 const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; 16380 return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(mod)}); 16381 } 16382 16383 if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) { 16384 return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op); 16385 } 16386 if (rhs_ty_tag == .Union and (lhs_ty_tag == .EnumLiteral or lhs_ty_tag == .Enum)) { 16387 return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op); 16388 } 16389 16390 if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { 16391 const runtime_src: LazySrcLoc = src: { 16392 if (try sema.resolveValue(lhs)) |lval| { 16393 if (try sema.resolveValue(rhs)) |rval| { 16394 if (lval.isUndef(mod) or rval.isUndef(mod)) { 16395 return mod.undefRef(Type.bool); 16396 } 16397 const lkey = mod.intern_pool.indexToKey(lval.toIntern()); 16398 const rkey = mod.intern_pool.indexToKey(rval.toIntern()); 16399 return if ((lkey.err.name == rkey.err.name) == (op == .eq)) 16400 .bool_true 16401 else 16402 .bool_false; 16403 } else { 16404 break :src rhs_src; 16405 } 16406 } else { 16407 break :src lhs_src; 16408 } 16409 }; 16410 try sema.requireRuntimeBlock(block, src, runtime_src); 16411 return block.addBinOp(air_tag, lhs, rhs); 16412 } 16413 if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { 16414 const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); 16415 const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); 16416 return if (lhs_as_type.eql(rhs_as_type, mod) == (op == .eq)) .bool_true else .bool_false; 16417 } 16418 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); 16419 } 16420 16421 fn analyzeCmpUnionTag( 16422 sema: *Sema, 16423 block: *Block, 16424 src: LazySrcLoc, 16425 un: Air.Inst.Ref, 16426 un_src: LazySrcLoc, 16427 tag: Air.Inst.Ref, 16428 tag_src: LazySrcLoc, 16429 op: std.math.CompareOperator, 16430 ) CompileError!Air.Inst.Ref { 16431 const mod = sema.mod; 16432 const union_ty = sema.typeOf(un); 16433 try sema.resolveTypeFields(union_ty); 16434 const union_tag_ty = union_ty.unionTagType(mod) orelse { 16435 const msg = msg: { 16436 const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); 16437 errdefer msg.destroy(sema.gpa); 16438 try mod.errNoteNonLazy(union_ty.declSrcLoc(mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(mod)}); 16439 break :msg msg; 16440 }; 16441 return sema.failWithOwnedErrorMsg(block, msg); 16442 }; 16443 // Coerce both the union and the tag to the union's tag type, and then execute the 16444 // enum comparison codepath. 16445 const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); 16446 const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); 16447 16448 if (try sema.resolveValue(coerced_tag)) |enum_val| { 16449 if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool); 16450 const field_ty = union_ty.unionFieldType(enum_val, mod).?; 16451 if (field_ty.zigTypeTag(mod) == .NoReturn) { 16452 return .bool_false; 16453 } 16454 } 16455 16456 return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src); 16457 } 16458 16459 /// Only called for non-equality operators. See also `zirCmpEq`. 16460 fn zirCmp( 16461 sema: *Sema, 16462 block: *Block, 16463 inst: Zir.Inst.Index, 16464 op: std.math.CompareOperator, 16465 ) CompileError!Air.Inst.Ref { 16466 const tracy = trace(@src()); 16467 defer tracy.end(); 16468 16469 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 16470 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 16471 const src: LazySrcLoc = inst_data.src(); 16472 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 16473 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 16474 const lhs = try sema.resolveInst(extra.lhs); 16475 const rhs = try sema.resolveInst(extra.rhs); 16476 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false); 16477 } 16478 16479 fn analyzeCmp( 16480 sema: *Sema, 16481 block: *Block, 16482 src: LazySrcLoc, 16483 lhs: Air.Inst.Ref, 16484 rhs: Air.Inst.Ref, 16485 op: std.math.CompareOperator, 16486 lhs_src: LazySrcLoc, 16487 rhs_src: LazySrcLoc, 16488 is_equality_cmp: bool, 16489 ) CompileError!Air.Inst.Ref { 16490 const mod = sema.mod; 16491 const lhs_ty = sema.typeOf(lhs); 16492 const rhs_ty = sema.typeOf(rhs); 16493 if (lhs_ty.zigTypeTag(mod) != .Optional and rhs_ty.zigTypeTag(mod) != .Optional) { 16494 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 16495 } 16496 16497 if (lhs_ty.zigTypeTag(mod) == .Vector and rhs_ty.zigTypeTag(mod) == .Vector) { 16498 return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src); 16499 } 16500 if (lhs_ty.isNumeric(mod) and rhs_ty.isNumeric(mod)) { 16501 // This operation allows any combination of integer and float types, regardless of the 16502 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for 16503 // numeric types. 16504 return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); 16505 } 16506 if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorUnion and rhs_ty.zigTypeTag(mod) == .ErrorSet) { 16507 const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs); 16508 return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src); 16509 } 16510 if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorSet and rhs_ty.zigTypeTag(mod) == .ErrorUnion) { 16511 const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs); 16512 return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src); 16513 } 16514 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 16515 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); 16516 if (!resolved_type.isSelfComparable(mod, is_equality_cmp)) { 16517 return sema.fail(block, src, "operator {s} not allowed for type '{}'", .{ 16518 compareOperatorName(op), resolved_type.fmt(mod), 16519 }); 16520 } 16521 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 16522 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 16523 return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src); 16524 } 16525 16526 fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { 16527 return switch (comp) { 16528 .lt => "<", 16529 .lte => "<=", 16530 .eq => "==", 16531 .gte => ">=", 16532 .gt => ">", 16533 .neq => "!=", 16534 }; 16535 } 16536 16537 fn cmpSelf( 16538 sema: *Sema, 16539 block: *Block, 16540 src: LazySrcLoc, 16541 casted_lhs: Air.Inst.Ref, 16542 casted_rhs: Air.Inst.Ref, 16543 op: std.math.CompareOperator, 16544 lhs_src: LazySrcLoc, 16545 rhs_src: LazySrcLoc, 16546 ) CompileError!Air.Inst.Ref { 16547 const mod = sema.mod; 16548 const resolved_type = sema.typeOf(casted_lhs); 16549 const runtime_src: LazySrcLoc = src: { 16550 if (try sema.resolveValue(casted_lhs)) |lhs_val| { 16551 if (lhs_val.isUndef(mod)) return mod.undefRef(Type.bool); 16552 if (try sema.resolveValue(casted_rhs)) |rhs_val| { 16553 if (rhs_val.isUndef(mod)) return mod.undefRef(Type.bool); 16554 16555 if (resolved_type.zigTypeTag(mod) == .Vector) { 16556 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type); 16557 return Air.internedToRef(cmp_val.toIntern()); 16558 } 16559 16560 return if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type)) 16561 .bool_true 16562 else 16563 .bool_false; 16564 } else { 16565 if (resolved_type.zigTypeTag(mod) == .Bool) { 16566 // We can lower bool eq/neq more efficiently. 16567 return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src); 16568 } 16569 break :src rhs_src; 16570 } 16571 } else { 16572 // For bools, we still check the other operand, because we can lower 16573 // bool eq/neq more efficiently. 16574 if (resolved_type.zigTypeTag(mod) == .Bool) { 16575 if (try sema.resolveValue(casted_rhs)) |rhs_val| { 16576 if (rhs_val.isUndef(mod)) return mod.undefRef(Type.bool); 16577 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src); 16578 } 16579 } 16580 break :src lhs_src; 16581 } 16582 }; 16583 try sema.requireRuntimeBlock(block, src, runtime_src); 16584 if (resolved_type.zigTypeTag(mod) == .Vector) { 16585 return block.addCmpVector(casted_lhs, casted_rhs, op); 16586 } 16587 const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized); 16588 return block.addBinOp(tag, casted_lhs, casted_rhs); 16589 } 16590 16591 /// cmp_eq (x, false) => not(x) 16592 /// cmp_eq (x, true ) => x 16593 /// cmp_neq(x, false) => x 16594 /// cmp_neq(x, true ) => not(x) 16595 fn runtimeBoolCmp( 16596 sema: *Sema, 16597 block: *Block, 16598 src: LazySrcLoc, 16599 op: std.math.CompareOperator, 16600 lhs: Air.Inst.Ref, 16601 rhs: bool, 16602 runtime_src: LazySrcLoc, 16603 ) CompileError!Air.Inst.Ref { 16604 if ((op == .neq) == rhs) { 16605 try sema.requireRuntimeBlock(block, src, runtime_src); 16606 return block.addTyOp(.not, Type.bool, lhs); 16607 } else { 16608 return lhs; 16609 } 16610 } 16611 16612 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16613 const mod = sema.mod; 16614 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16615 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 16616 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 16617 switch (ty.zigTypeTag(mod)) { 16618 .Fn, 16619 .NoReturn, 16620 .Undefined, 16621 .Null, 16622 .Opaque, 16623 => return sema.fail(block, operand_src, "no size available for type '{}'", .{ty.fmt(mod)}), 16624 16625 .Type, 16626 .EnumLiteral, 16627 .ComptimeFloat, 16628 .ComptimeInt, 16629 .Void, 16630 => return mod.intRef(Type.comptime_int, 0), 16631 16632 .Bool, 16633 .Int, 16634 .Float, 16635 .Pointer, 16636 .Array, 16637 .Struct, 16638 .Optional, 16639 .ErrorUnion, 16640 .ErrorSet, 16641 .Enum, 16642 .Union, 16643 .Vector, 16644 .Frame, 16645 .AnyFrame, 16646 => {}, 16647 } 16648 const val = try ty.lazyAbiSize(mod); 16649 if (val.isLazySize(mod)) { 16650 try sema.queueFullTypeResolution(ty); 16651 } 16652 return Air.internedToRef(val.toIntern()); 16653 } 16654 16655 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16656 const mod = sema.mod; 16657 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16658 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 16659 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); 16660 switch (operand_ty.zigTypeTag(mod)) { 16661 .Fn, 16662 .NoReturn, 16663 .Undefined, 16664 .Null, 16665 .Opaque, 16666 => return sema.fail(block, operand_src, "no size available for type '{}'", .{operand_ty.fmt(mod)}), 16667 16668 .Type, 16669 .EnumLiteral, 16670 .ComptimeFloat, 16671 .ComptimeInt, 16672 .Void, 16673 => return mod.intRef(Type.comptime_int, 0), 16674 16675 .Bool, 16676 .Int, 16677 .Float, 16678 .Pointer, 16679 .Array, 16680 .Struct, 16681 .Optional, 16682 .ErrorUnion, 16683 .ErrorSet, 16684 .Enum, 16685 .Union, 16686 .Vector, 16687 .Frame, 16688 .AnyFrame, 16689 => {}, 16690 } 16691 const bit_size = try operand_ty.bitSizeAdvanced(mod, sema); 16692 return mod.intRef(Type.comptime_int, bit_size); 16693 } 16694 16695 fn zirThis( 16696 sema: *Sema, 16697 block: *Block, 16698 extended: Zir.Inst.Extended.InstData, 16699 ) CompileError!Air.Inst.Ref { 16700 const mod = sema.mod; 16701 const this_decl_index = mod.namespaceDeclIndex(block.namespace); 16702 const src = LazySrcLoc.nodeOffset(@bitCast(extended.operand)); 16703 return sema.analyzeDeclVal(block, src, this_decl_index); 16704 } 16705 16706 fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 16707 const mod = sema.mod; 16708 const gpa = sema.gpa; 16709 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 16710 // Closures are not necessarily constant values. For example, the 16711 // code might do something like this: 16712 // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; } 16713 // ...in which case the closure_capture instruction has access to a runtime 16714 // value only. In such case only the type is saved into the scope. 16715 const operand = try sema.resolveInst(inst_data.operand); 16716 const ty = sema.typeOf(operand); 16717 const key: CaptureScope.Key = .{ 16718 .zir_index = inst, 16719 .index = block.wip_capture_scope, 16720 }; 16721 if (try sema.resolveValue(operand)) |val| { 16722 try mod.comptime_capture_scopes.put(gpa, key, try val.intern(ty, mod)); 16723 } else { 16724 try mod.runtime_capture_scopes.put(gpa, key, ty.toIntern()); 16725 } 16726 } 16727 16728 fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16729 const mod = sema.mod; 16730 //const ip = &mod.intern_pool; 16731 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].inst_node; 16732 var scope: CaptureScope.Index = mod.declPtr(block.src_decl).src_scope; 16733 assert(scope != .none); 16734 // Note: The target closure must be in this scope list. 16735 // If it's not here, the zir is invalid, or the list is broken. 16736 const capture_ty = while (true) { 16737 // Note: We don't need to add a dependency here, because 16738 // decls always depend on their lexical parents. 16739 const key: CaptureScope.Key = .{ 16740 .zir_index = inst_data.inst, 16741 .index = scope, 16742 }; 16743 if (mod.comptime_capture_scopes.get(key)) |val| 16744 return Air.internedToRef(val); 16745 if (mod.runtime_capture_scopes.get(key)) |ty| 16746 break ty; 16747 scope = scope.parent(mod); 16748 assert(scope != .none); 16749 }; 16750 16751 // The comptime case is handled already above. Runtime case below. 16752 16753 if (!block.is_typeof and sema.func_index == .none) { 16754 const msg = msg: { 16755 const name = name: { 16756 const file = sema.owner_decl.getFileScope(mod); 16757 const tree = file.getTree(sema.gpa) catch |err| { 16758 // In this case we emit a warning + a less precise source location. 16759 log.warn("unable to load {s}: {s}", .{ 16760 file.sub_file_path, @errorName(err), 16761 }); 16762 break :name null; 16763 }; 16764 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node); 16765 const token = tree.nodes.items(.main_token)[node]; 16766 break :name tree.tokenSlice(token); 16767 }; 16768 16769 const msg = if (name) |some| 16770 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible outside function scope", .{some}) 16771 else 16772 try sema.errMsg(block, inst_data.src(), "variable not accessible outside function scope", .{}); 16773 errdefer msg.destroy(sema.gpa); 16774 16775 // TODO add "declared here" note 16776 break :msg msg; 16777 }; 16778 return sema.failWithOwnedErrorMsg(block, msg); 16779 } 16780 16781 if (!block.is_typeof and !block.is_comptime and sema.func_index != .none) { 16782 const msg = msg: { 16783 const name = name: { 16784 const file = sema.owner_decl.getFileScope(mod); 16785 const tree = file.getTree(sema.gpa) catch |err| { 16786 // In this case we emit a warning + a less precise source location. 16787 log.warn("unable to load {s}: {s}", .{ 16788 file.sub_file_path, @errorName(err), 16789 }); 16790 break :name null; 16791 }; 16792 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node); 16793 const token = tree.nodes.items(.main_token)[node]; 16794 break :name tree.tokenSlice(token); 16795 }; 16796 16797 const msg = if (name) |some| 16798 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible from inner function", .{some}) 16799 else 16800 try sema.errMsg(block, inst_data.src(), "variable not accessible from inner function", .{}); 16801 errdefer msg.destroy(sema.gpa); 16802 16803 try sema.errNote(block, LazySrcLoc.nodeOffset(0), msg, "crossed function definition here", .{}); 16804 16805 // TODO add "declared here" note 16806 break :msg msg; 16807 }; 16808 return sema.failWithOwnedErrorMsg(block, msg); 16809 } 16810 16811 assert(block.is_typeof); 16812 // We need a dummy runtime instruction with the correct type. 16813 return block.addTy(.alloc, Type.fromInterned(capture_ty)); 16814 } 16815 16816 fn zirRetAddr( 16817 sema: *Sema, 16818 block: *Block, 16819 extended: Zir.Inst.Extended.InstData, 16820 ) CompileError!Air.Inst.Ref { 16821 _ = extended; 16822 if (block.is_comptime) { 16823 // TODO: we could give a meaningful lazy value here. #14938 16824 return sema.mod.intRef(Type.usize, 0); 16825 } else { 16826 return block.addNoOp(.ret_addr); 16827 } 16828 } 16829 16830 fn zirFrameAddress( 16831 sema: *Sema, 16832 block: *Block, 16833 extended: Zir.Inst.Extended.InstData, 16834 ) CompileError!Air.Inst.Ref { 16835 const src = LazySrcLoc.nodeOffset(@bitCast(extended.operand)); 16836 try sema.requireRuntimeBlock(block, src, null); 16837 return try block.addNoOp(.frame_addr); 16838 } 16839 16840 fn zirBuiltinSrc( 16841 sema: *Sema, 16842 block: *Block, 16843 extended: Zir.Inst.Extended.InstData, 16844 ) CompileError!Air.Inst.Ref { 16845 _ = block; 16846 const tracy = trace(@src()); 16847 defer tracy.end(); 16848 16849 const mod = sema.mod; 16850 const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data; 16851 const fn_owner_decl = mod.funcOwnerDeclPtr(sema.func_index); 16852 const ip = &mod.intern_pool; 16853 const gpa = sema.gpa; 16854 16855 const func_name_val = v: { 16856 // This dupe prevents InternPool string pool memory from being reallocated 16857 // while a reference exists. 16858 const bytes = try sema.arena.dupe(u8, ip.stringToSlice(fn_owner_decl.name)); 16859 const array_ty = try ip.get(gpa, .{ .array_type = .{ 16860 .len = bytes.len, 16861 .sentinel = .zero_u8, 16862 .child = .u8_type, 16863 } }); 16864 break :v try ip.get(gpa, .{ .ptr = .{ 16865 .ty = .slice_const_u8_sentinel_0_type, 16866 .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), 16867 .addr = .{ .anon_decl = .{ 16868 .orig_ty = .slice_const_u8_sentinel_0_type, 16869 .val = try ip.get(gpa, .{ .aggregate = .{ 16870 .ty = array_ty, 16871 .storage = .{ .bytes = bytes }, 16872 } }), 16873 } }, 16874 } }); 16875 }; 16876 16877 const file_name_val = v: { 16878 // The compiler must not call realpath anywhere. 16879 const bytes = try fn_owner_decl.getFileScope(mod).fullPathZ(sema.arena); 16880 const array_ty = try ip.get(gpa, .{ .array_type = .{ 16881 .len = bytes.len, 16882 .sentinel = .zero_u8, 16883 .child = .u8_type, 16884 } }); 16885 break :v try ip.get(gpa, .{ .ptr = .{ 16886 .ty = .slice_const_u8_sentinel_0_type, 16887 .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), 16888 .addr = .{ .anon_decl = .{ 16889 .orig_ty = .slice_const_u8_sentinel_0_type, 16890 .val = try ip.get(gpa, .{ .aggregate = .{ 16891 .ty = array_ty, 16892 .storage = .{ .bytes = bytes }, 16893 } }), 16894 } }, 16895 } }); 16896 }; 16897 16898 const src_loc_ty = try sema.getBuiltinType("SourceLocation"); 16899 const fields = .{ 16900 // file: [:0]const u8, 16901 file_name_val, 16902 // fn_name: [:0]const u8, 16903 func_name_val, 16904 // line: u32, 16905 (try mod.intValue(Type.u32, extra.line + 1)).toIntern(), 16906 // column: u32, 16907 (try mod.intValue(Type.u32, extra.column + 1)).toIntern(), 16908 }; 16909 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 16910 .ty = src_loc_ty.toIntern(), 16911 .storage = .{ .elems = &fields }, 16912 } }))); 16913 } 16914 16915 fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16916 const mod = sema.mod; 16917 const gpa = sema.gpa; 16918 const ip = &mod.intern_pool; 16919 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 16920 const src = inst_data.src(); 16921 const ty = try sema.resolveType(block, src, inst_data.operand); 16922 const type_info_ty = try sema.getBuiltinType("Type"); 16923 const type_info_tag_ty = type_info_ty.unionTagType(mod).?; 16924 16925 switch (ty.zigTypeTag(mod)) { 16926 .Type, 16927 .Void, 16928 .Bool, 16929 .NoReturn, 16930 .ComptimeFloat, 16931 .ComptimeInt, 16932 .Undefined, 16933 .Null, 16934 .EnumLiteral, 16935 => |type_info_tag| return Air.internedToRef((try mod.intern(.{ .un = .{ 16936 .ty = type_info_ty.toIntern(), 16937 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(type_info_tag))).toIntern(), 16938 .val = .void_value, 16939 } }))), 16940 .Fn => { 16941 const fn_info_decl_index = (try sema.namespaceLookup( 16942 block, 16943 src, 16944 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16945 try ip.getOrPutString(gpa, "Fn"), 16946 )).?; 16947 try sema.ensureDeclAnalyzed(fn_info_decl_index); 16948 const fn_info_decl = mod.declPtr(fn_info_decl_index); 16949 const fn_info_ty = fn_info_decl.val.toType(); 16950 16951 const param_info_decl_index = (try sema.namespaceLookup( 16952 block, 16953 src, 16954 fn_info_ty.getNamespaceIndex(mod).unwrap().?, 16955 try ip.getOrPutString(gpa, "Param"), 16956 )).?; 16957 try sema.ensureDeclAnalyzed(param_info_decl_index); 16958 const param_info_decl = mod.declPtr(param_info_decl_index); 16959 const param_info_ty = param_info_decl.val.toType(); 16960 16961 const func_ty_info = mod.typeToFunc(ty).?; 16962 const param_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); 16963 for (param_vals, 0..) |*param_val, i| { 16964 const param_ty = func_ty_info.param_types.get(ip)[i]; 16965 const is_generic = param_ty == .generic_poison_type; 16966 const param_ty_val = try ip.get(gpa, .{ .opt = .{ 16967 .ty = try ip.get(gpa, .{ .opt_type = .type_type }), 16968 .val = if (is_generic) .none else param_ty, 16969 } }); 16970 16971 const is_noalias = blk: { 16972 const index = std.math.cast(u5, i) orelse break :blk false; 16973 break :blk @as(u1, @truncate(func_ty_info.noalias_bits >> index)) != 0; 16974 }; 16975 16976 const param_fields = .{ 16977 // is_generic: bool, 16978 Value.makeBool(is_generic).toIntern(), 16979 // is_noalias: bool, 16980 Value.makeBool(is_noalias).toIntern(), 16981 // type: ?type, 16982 param_ty_val, 16983 }; 16984 param_val.* = try mod.intern(.{ .aggregate = .{ 16985 .ty = param_info_ty.toIntern(), 16986 .storage = .{ .elems = ¶m_fields }, 16987 } }); 16988 } 16989 16990 const args_val = v: { 16991 const new_decl_ty = try mod.arrayType(.{ 16992 .len = param_vals.len, 16993 .child = param_info_ty.toIntern(), 16994 }); 16995 const new_decl_val = try mod.intern(.{ .aggregate = .{ 16996 .ty = new_decl_ty.toIntern(), 16997 .storage = .{ .elems = param_vals }, 16998 } }); 16999 const ptr_ty = (try sema.ptrType(.{ 17000 .child = param_info_ty.toIntern(), 17001 .flags = .{ 17002 .size = .Slice, 17003 .is_const = true, 17004 }, 17005 })).toIntern(); 17006 break :v try mod.intern(.{ .ptr = .{ 17007 .ty = ptr_ty, 17008 .addr = .{ .anon_decl = .{ 17009 .orig_ty = ptr_ty, 17010 .val = new_decl_val, 17011 } }, 17012 .len = (try mod.intValue(Type.usize, param_vals.len)).toIntern(), 17013 } }); 17014 }; 17015 17016 const ret_ty_opt = try mod.intern(.{ .opt = .{ 17017 .ty = try ip.get(gpa, .{ .opt_type = .type_type }), 17018 .val = if (func_ty_info.return_type == .generic_poison_type) 17019 .none 17020 else 17021 func_ty_info.return_type, 17022 } }); 17023 17024 const callconv_ty = try sema.getBuiltinType("CallingConvention"); 17025 17026 const field_values = .{ 17027 // calling_convention: CallingConvention, 17028 (try mod.enumValueFieldIndex(callconv_ty, @intFromEnum(func_ty_info.cc))).toIntern(), 17029 // alignment: comptime_int, 17030 (try mod.intValue(Type.comptime_int, ty.abiAlignment(mod).toByteUnits(0))).toIntern(), 17031 // is_generic: bool, 17032 Value.makeBool(func_ty_info.is_generic).toIntern(), 17033 // is_var_args: bool, 17034 Value.makeBool(func_ty_info.is_var_args).toIntern(), 17035 // return_type: ?type, 17036 ret_ty_opt, 17037 // args: []const Fn.Param, 17038 args_val, 17039 }; 17040 return Air.internedToRef((try mod.intern(.{ .un = .{ 17041 .ty = type_info_ty.toIntern(), 17042 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Fn))).toIntern(), 17043 .val = try mod.intern(.{ .aggregate = .{ 17044 .ty = fn_info_ty.toIntern(), 17045 .storage = .{ .elems = &field_values }, 17046 } }), 17047 } }))); 17048 }, 17049 .Int => { 17050 const int_info_decl_index = (try sema.namespaceLookup( 17051 block, 17052 src, 17053 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17054 try ip.getOrPutString(gpa, "Int"), 17055 )).?; 17056 try sema.ensureDeclAnalyzed(int_info_decl_index); 17057 const int_info_decl = mod.declPtr(int_info_decl_index); 17058 const int_info_ty = int_info_decl.val.toType(); 17059 17060 const signedness_ty = try sema.getBuiltinType("Signedness"); 17061 const info = ty.intInfo(mod); 17062 const field_values = .{ 17063 // signedness: Signedness, 17064 try (try mod.enumValueFieldIndex(signedness_ty, @intFromEnum(info.signedness))).intern(signedness_ty, mod), 17065 // bits: u16, 17066 (try mod.intValue(Type.u16, info.bits)).toIntern(), 17067 }; 17068 return Air.internedToRef((try mod.intern(.{ .un = .{ 17069 .ty = type_info_ty.toIntern(), 17070 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Int))).toIntern(), 17071 .val = try mod.intern(.{ .aggregate = .{ 17072 .ty = int_info_ty.toIntern(), 17073 .storage = .{ .elems = &field_values }, 17074 } }), 17075 } }))); 17076 }, 17077 .Float => { 17078 const float_info_decl_index = (try sema.namespaceLookup( 17079 block, 17080 src, 17081 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17082 try ip.getOrPutString(gpa, "Float"), 17083 )).?; 17084 try sema.ensureDeclAnalyzed(float_info_decl_index); 17085 const float_info_decl = mod.declPtr(float_info_decl_index); 17086 const float_info_ty = float_info_decl.val.toType(); 17087 17088 const field_vals = .{ 17089 // bits: u16, 17090 (try mod.intValue(Type.u16, ty.bitSize(mod))).toIntern(), 17091 }; 17092 return Air.internedToRef((try mod.intern(.{ .un = .{ 17093 .ty = type_info_ty.toIntern(), 17094 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Float))).toIntern(), 17095 .val = try mod.intern(.{ .aggregate = .{ 17096 .ty = float_info_ty.toIntern(), 17097 .storage = .{ .elems = &field_vals }, 17098 } }), 17099 } }))); 17100 }, 17101 .Pointer => { 17102 const info = ty.ptrInfo(mod); 17103 const alignment = if (info.flags.alignment.toByteUnitsOptional()) |alignment| 17104 try mod.intValue(Type.comptime_int, alignment) 17105 else 17106 try Type.fromInterned(info.child).lazyAbiAlignment(mod); 17107 17108 const addrspace_ty = try sema.getBuiltinType("AddressSpace"); 17109 const pointer_ty = t: { 17110 const decl_index = (try sema.namespaceLookup( 17111 block, 17112 src, 17113 (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, 17114 try ip.getOrPutString(gpa, "Pointer"), 17115 )).?; 17116 try sema.ensureDeclAnalyzed(decl_index); 17117 const decl = mod.declPtr(decl_index); 17118 break :t decl.val.toType(); 17119 }; 17120 const ptr_size_ty = t: { 17121 const decl_index = (try sema.namespaceLookup( 17122 block, 17123 src, 17124 pointer_ty.getNamespaceIndex(mod).unwrap().?, 17125 try ip.getOrPutString(gpa, "Size"), 17126 )).?; 17127 try sema.ensureDeclAnalyzed(decl_index); 17128 const decl = mod.declPtr(decl_index); 17129 break :t decl.val.toType(); 17130 }; 17131 17132 const field_values = .{ 17133 // size: Size, 17134 try (try mod.enumValueFieldIndex(ptr_size_ty, @intFromEnum(info.flags.size))).intern(ptr_size_ty, mod), 17135 // is_const: bool, 17136 Value.makeBool(info.flags.is_const).toIntern(), 17137 // is_volatile: bool, 17138 Value.makeBool(info.flags.is_volatile).toIntern(), 17139 // alignment: comptime_int, 17140 alignment.toIntern(), 17141 // address_space: AddressSpace 17142 try (try mod.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).intern(addrspace_ty, mod), 17143 // child: type, 17144 info.child, 17145 // is_allowzero: bool, 17146 Value.makeBool(info.flags.is_allowzero).toIntern(), 17147 // sentinel: ?*const anyopaque, 17148 (try sema.optRefValue(switch (info.sentinel) { 17149 .none => null, 17150 else => Value.fromInterned(info.sentinel), 17151 })).toIntern(), 17152 }; 17153 return Air.internedToRef((try mod.intern(.{ .un = .{ 17154 .ty = type_info_ty.toIntern(), 17155 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Pointer))).toIntern(), 17156 .val = try mod.intern(.{ .aggregate = .{ 17157 .ty = pointer_ty.toIntern(), 17158 .storage = .{ .elems = &field_values }, 17159 } }), 17160 } }))); 17161 }, 17162 .Array => { 17163 const array_field_ty = t: { 17164 const array_field_ty_decl_index = (try sema.namespaceLookup( 17165 block, 17166 src, 17167 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17168 try ip.getOrPutString(gpa, "Array"), 17169 )).?; 17170 try sema.ensureDeclAnalyzed(array_field_ty_decl_index); 17171 const array_field_ty_decl = mod.declPtr(array_field_ty_decl_index); 17172 break :t array_field_ty_decl.val.toType(); 17173 }; 17174 17175 const info = ty.arrayInfo(mod); 17176 const field_values = .{ 17177 // len: comptime_int, 17178 (try mod.intValue(Type.comptime_int, info.len)).toIntern(), 17179 // child: type, 17180 info.elem_type.toIntern(), 17181 // sentinel: ?*const anyopaque, 17182 (try sema.optRefValue(info.sentinel)).toIntern(), 17183 }; 17184 return Air.internedToRef((try mod.intern(.{ .un = .{ 17185 .ty = type_info_ty.toIntern(), 17186 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Array))).toIntern(), 17187 .val = try mod.intern(.{ .aggregate = .{ 17188 .ty = array_field_ty.toIntern(), 17189 .storage = .{ .elems = &field_values }, 17190 } }), 17191 } }))); 17192 }, 17193 .Vector => { 17194 const vector_field_ty = t: { 17195 const vector_field_ty_decl_index = (try sema.namespaceLookup( 17196 block, 17197 src, 17198 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17199 try ip.getOrPutString(gpa, "Vector"), 17200 )).?; 17201 try sema.ensureDeclAnalyzed(vector_field_ty_decl_index); 17202 const vector_field_ty_decl = mod.declPtr(vector_field_ty_decl_index); 17203 break :t vector_field_ty_decl.val.toType(); 17204 }; 17205 17206 const info = ty.arrayInfo(mod); 17207 const field_values = .{ 17208 // len: comptime_int, 17209 (try mod.intValue(Type.comptime_int, info.len)).toIntern(), 17210 // child: type, 17211 info.elem_type.toIntern(), 17212 }; 17213 return Air.internedToRef((try mod.intern(.{ .un = .{ 17214 .ty = type_info_ty.toIntern(), 17215 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Vector))).toIntern(), 17216 .val = try mod.intern(.{ .aggregate = .{ 17217 .ty = vector_field_ty.toIntern(), 17218 .storage = .{ .elems = &field_values }, 17219 } }), 17220 } }))); 17221 }, 17222 .Optional => { 17223 const optional_field_ty = t: { 17224 const optional_field_ty_decl_index = (try sema.namespaceLookup( 17225 block, 17226 src, 17227 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17228 try ip.getOrPutString(gpa, "Optional"), 17229 )).?; 17230 try sema.ensureDeclAnalyzed(optional_field_ty_decl_index); 17231 const optional_field_ty_decl = mod.declPtr(optional_field_ty_decl_index); 17232 break :t optional_field_ty_decl.val.toType(); 17233 }; 17234 17235 const field_values = .{ 17236 // child: type, 17237 ty.optionalChild(mod).toIntern(), 17238 }; 17239 return Air.internedToRef((try mod.intern(.{ .un = .{ 17240 .ty = type_info_ty.toIntern(), 17241 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Optional))).toIntern(), 17242 .val = try mod.intern(.{ .aggregate = .{ 17243 .ty = optional_field_ty.toIntern(), 17244 .storage = .{ .elems = &field_values }, 17245 } }), 17246 } }))); 17247 }, 17248 .ErrorSet => { 17249 // Get the Error type 17250 const error_field_ty = t: { 17251 const set_field_ty_decl_index = (try sema.namespaceLookup( 17252 block, 17253 src, 17254 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17255 try ip.getOrPutString(gpa, "Error"), 17256 )).?; 17257 try sema.ensureDeclAnalyzed(set_field_ty_decl_index); 17258 const set_field_ty_decl = mod.declPtr(set_field_ty_decl_index); 17259 break :t set_field_ty_decl.val.toType(); 17260 }; 17261 17262 try sema.queueFullTypeResolution(error_field_ty); 17263 17264 // Build our list of Error values 17265 // Optional value is only null if anyerror 17266 // Value can be zero-length slice otherwise 17267 const error_field_vals = switch (try sema.resolveInferredErrorSetTy(block, src, ty.toIntern())) { 17268 .anyerror_type => null, 17269 else => |err_set_ty_index| blk: { 17270 const names = ip.indexToKey(err_set_ty_index).error_set_type.names; 17271 const vals = try sema.arena.alloc(InternPool.Index, names.len); 17272 for (vals, 0..) |*field_val, i| { 17273 // TODO: write something like getCoercedInts to avoid needing to dupe 17274 const name = try sema.arena.dupe(u8, ip.stringToSlice(names.get(ip)[i])); 17275 const name_val = v: { 17276 const new_decl_ty = try mod.arrayType(.{ 17277 .len = name.len, 17278 .child = .u8_type, 17279 }); 17280 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17281 .ty = new_decl_ty.toIntern(), 17282 .storage = .{ .bytes = name }, 17283 } }); 17284 break :v try mod.intern(.{ .ptr = .{ 17285 .ty = .slice_const_u8_type, 17286 .addr = .{ .anon_decl = .{ 17287 .val = new_decl_val, 17288 .orig_ty = .slice_const_u8_type, 17289 } }, 17290 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17291 } }); 17292 }; 17293 17294 const error_field_fields = .{ 17295 // name: []const u8, 17296 name_val, 17297 }; 17298 field_val.* = try mod.intern(.{ .aggregate = .{ 17299 .ty = error_field_ty.toIntern(), 17300 .storage = .{ .elems = &error_field_fields }, 17301 } }); 17302 } 17303 17304 break :blk vals; 17305 }, 17306 }; 17307 17308 // Build our ?[]const Error value 17309 const slice_errors_ty = try sema.ptrType(.{ 17310 .child = error_field_ty.toIntern(), 17311 .flags = .{ 17312 .size = .Slice, 17313 .is_const = true, 17314 }, 17315 }); 17316 const opt_slice_errors_ty = try mod.optionalType(slice_errors_ty.toIntern()); 17317 const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: { 17318 const array_errors_ty = try mod.arrayType(.{ 17319 .len = vals.len, 17320 .child = error_field_ty.toIntern(), 17321 }); 17322 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17323 .ty = array_errors_ty.toIntern(), 17324 .storage = .{ .elems = vals }, 17325 } }); 17326 break :v try mod.intern(.{ .ptr = .{ 17327 .ty = slice_errors_ty.toIntern(), 17328 .addr = .{ .anon_decl = .{ 17329 .orig_ty = slice_errors_ty.toIntern(), 17330 .val = new_decl_val, 17331 } }, 17332 .len = (try mod.intValue(Type.usize, vals.len)).toIntern(), 17333 } }); 17334 } else .none; 17335 const errors_val = try mod.intern(.{ .opt = .{ 17336 .ty = opt_slice_errors_ty.toIntern(), 17337 .val = errors_payload_val, 17338 } }); 17339 17340 // Construct Type{ .ErrorSet = errors_val } 17341 return Air.internedToRef((try mod.intern(.{ .un = .{ 17342 .ty = type_info_ty.toIntern(), 17343 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.ErrorSet))).toIntern(), 17344 .val = errors_val, 17345 } }))); 17346 }, 17347 .ErrorUnion => { 17348 const error_union_field_ty = t: { 17349 const error_union_field_ty_decl_index = (try sema.namespaceLookup( 17350 block, 17351 src, 17352 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17353 try ip.getOrPutString(gpa, "ErrorUnion"), 17354 )).?; 17355 try sema.ensureDeclAnalyzed(error_union_field_ty_decl_index); 17356 const error_union_field_ty_decl = mod.declPtr(error_union_field_ty_decl_index); 17357 break :t error_union_field_ty_decl.val.toType(); 17358 }; 17359 17360 const field_values = .{ 17361 // error_set: type, 17362 ty.errorUnionSet(mod).toIntern(), 17363 // payload: type, 17364 ty.errorUnionPayload(mod).toIntern(), 17365 }; 17366 return Air.internedToRef((try mod.intern(.{ .un = .{ 17367 .ty = type_info_ty.toIntern(), 17368 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.ErrorUnion))).toIntern(), 17369 .val = try mod.intern(.{ .aggregate = .{ 17370 .ty = error_union_field_ty.toIntern(), 17371 .storage = .{ .elems = &field_values }, 17372 } }), 17373 } }))); 17374 }, 17375 .Enum => { 17376 const is_exhaustive = Value.makeBool(ip.indexToKey(ty.toIntern()).enum_type.tag_mode != .nonexhaustive); 17377 17378 const enum_field_ty = t: { 17379 const enum_field_ty_decl_index = (try sema.namespaceLookup( 17380 block, 17381 src, 17382 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17383 try ip.getOrPutString(gpa, "EnumField"), 17384 )).?; 17385 try sema.ensureDeclAnalyzed(enum_field_ty_decl_index); 17386 const enum_field_ty_decl = mod.declPtr(enum_field_ty_decl_index); 17387 break :t enum_field_ty_decl.val.toType(); 17388 }; 17389 17390 const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.indexToKey(ty.toIntern()).enum_type.names.len); 17391 for (enum_field_vals, 0..) |*field_val, i| { 17392 const enum_type = ip.indexToKey(ty.toIntern()).enum_type; 17393 const value_val = if (enum_type.values.len > 0) 17394 try mod.intern_pool.getCoercedInts( 17395 mod.gpa, 17396 mod.intern_pool.indexToKey(enum_type.values.get(ip)[i]).int, 17397 .comptime_int_type, 17398 ) 17399 else 17400 (try mod.intValue(Type.comptime_int, i)).toIntern(); 17401 // TODO: write something like getCoercedInts to avoid needing to dupe 17402 const name = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names.get(ip)[i])); 17403 const name_val = v: { 17404 const new_decl_ty = try mod.arrayType(.{ 17405 .len = name.len, 17406 .child = .u8_type, 17407 }); 17408 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17409 .ty = new_decl_ty.toIntern(), 17410 .storage = .{ .bytes = name }, 17411 } }); 17412 break :v try mod.intern(.{ .ptr = .{ 17413 .ty = .slice_const_u8_type, 17414 .addr = .{ .anon_decl = .{ 17415 .val = new_decl_val, 17416 .orig_ty = .slice_const_u8_type, 17417 } }, 17418 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17419 } }); 17420 }; 17421 17422 const enum_field_fields = .{ 17423 // name: []const u8, 17424 name_val, 17425 // value: comptime_int, 17426 value_val, 17427 }; 17428 field_val.* = try mod.intern(.{ .aggregate = .{ 17429 .ty = enum_field_ty.toIntern(), 17430 .storage = .{ .elems = &enum_field_fields }, 17431 } }); 17432 } 17433 17434 const fields_val = v: { 17435 const fields_array_ty = try mod.arrayType(.{ 17436 .len = enum_field_vals.len, 17437 .child = enum_field_ty.toIntern(), 17438 }); 17439 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17440 .ty = fields_array_ty.toIntern(), 17441 .storage = .{ .elems = enum_field_vals }, 17442 } }); 17443 const ptr_ty = (try sema.ptrType(.{ 17444 .child = enum_field_ty.toIntern(), 17445 .flags = .{ 17446 .size = .Slice, 17447 .is_const = true, 17448 }, 17449 })).toIntern(); 17450 break :v try mod.intern(.{ .ptr = .{ 17451 .ty = ptr_ty, 17452 .addr = .{ .anon_decl = .{ 17453 .val = new_decl_val, 17454 .orig_ty = ptr_ty, 17455 } }, 17456 .len = (try mod.intValue(Type.usize, enum_field_vals.len)).toIntern(), 17457 } }); 17458 }; 17459 17460 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ip.indexToKey(ty.toIntern()).enum_type.namespace); 17461 17462 const type_enum_ty = t: { 17463 const type_enum_ty_decl_index = (try sema.namespaceLookup( 17464 block, 17465 src, 17466 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17467 try ip.getOrPutString(gpa, "Enum"), 17468 )).?; 17469 try sema.ensureDeclAnalyzed(type_enum_ty_decl_index); 17470 const type_enum_ty_decl = mod.declPtr(type_enum_ty_decl_index); 17471 break :t type_enum_ty_decl.val.toType(); 17472 }; 17473 17474 const field_values = .{ 17475 // tag_type: type, 17476 ip.indexToKey(ty.toIntern()).enum_type.tag_ty, 17477 // fields: []const EnumField, 17478 fields_val, 17479 // decls: []const Declaration, 17480 decls_val, 17481 // is_exhaustive: bool, 17482 is_exhaustive.toIntern(), 17483 }; 17484 return Air.internedToRef((try mod.intern(.{ .un = .{ 17485 .ty = type_info_ty.toIntern(), 17486 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Enum))).toIntern(), 17487 .val = try mod.intern(.{ .aggregate = .{ 17488 .ty = type_enum_ty.toIntern(), 17489 .storage = .{ .elems = &field_values }, 17490 } }), 17491 } }))); 17492 }, 17493 .Union => { 17494 const type_union_ty = t: { 17495 const type_union_ty_decl_index = (try sema.namespaceLookup( 17496 block, 17497 src, 17498 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17499 try ip.getOrPutString(gpa, "Union"), 17500 )).?; 17501 try sema.ensureDeclAnalyzed(type_union_ty_decl_index); 17502 const type_union_ty_decl = mod.declPtr(type_union_ty_decl_index); 17503 break :t type_union_ty_decl.val.toType(); 17504 }; 17505 17506 const union_field_ty = t: { 17507 const union_field_ty_decl_index = (try sema.namespaceLookup( 17508 block, 17509 src, 17510 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17511 try ip.getOrPutString(gpa, "UnionField"), 17512 )).?; 17513 try sema.ensureDeclAnalyzed(union_field_ty_decl_index); 17514 const union_field_ty_decl = mod.declPtr(union_field_ty_decl_index); 17515 break :t union_field_ty_decl.val.toType(); 17516 }; 17517 17518 try sema.resolveTypeLayout(ty); // Getting alignment requires type layout 17519 const union_obj = mod.typeToUnion(ty).?; 17520 const layout = union_obj.getLayout(ip); 17521 17522 const union_field_vals = try gpa.alloc(InternPool.Index, union_obj.field_names.len); 17523 defer gpa.free(union_field_vals); 17524 17525 for (union_field_vals, 0..) |*field_val, i| { 17526 // TODO: write something like getCoercedInts to avoid needing to dupe 17527 const name = try sema.arena.dupe(u8, ip.stringToSlice(union_obj.field_names.get(ip)[i])); 17528 const name_val = v: { 17529 const new_decl_ty = try mod.arrayType(.{ 17530 .len = name.len, 17531 .child = .u8_type, 17532 }); 17533 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17534 .ty = new_decl_ty.toIntern(), 17535 .storage = .{ .bytes = name }, 17536 } }); 17537 break :v try mod.intern(.{ .ptr = .{ 17538 .ty = .slice_const_u8_type, 17539 .addr = .{ .anon_decl = .{ 17540 .val = new_decl_val, 17541 .orig_ty = .slice_const_u8_type, 17542 } }, 17543 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17544 } }); 17545 }; 17546 17547 const alignment = switch (layout) { 17548 .Auto, .Extern => try sema.unionFieldAlignment(union_obj, @intCast(i)), 17549 .Packed => .none, 17550 }; 17551 17552 const field_ty = union_obj.field_types.get(ip)[i]; 17553 const union_field_fields = .{ 17554 // name: []const u8, 17555 name_val, 17556 // type: type, 17557 field_ty, 17558 // alignment: comptime_int, 17559 (try mod.intValue(Type.comptime_int, alignment.toByteUnits(0))).toIntern(), 17560 }; 17561 field_val.* = try mod.intern(.{ .aggregate = .{ 17562 .ty = union_field_ty.toIntern(), 17563 .storage = .{ .elems = &union_field_fields }, 17564 } }); 17565 } 17566 17567 const fields_val = v: { 17568 const array_fields_ty = try mod.arrayType(.{ 17569 .len = union_field_vals.len, 17570 .child = union_field_ty.toIntern(), 17571 }); 17572 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17573 .ty = array_fields_ty.toIntern(), 17574 .storage = .{ .elems = union_field_vals }, 17575 } }); 17576 const ptr_ty = (try sema.ptrType(.{ 17577 .child = union_field_ty.toIntern(), 17578 .flags = .{ 17579 .size = .Slice, 17580 .is_const = true, 17581 }, 17582 })).toIntern(); 17583 break :v try mod.intern(.{ .ptr = .{ 17584 .ty = ptr_ty, 17585 .addr = .{ .anon_decl = .{ 17586 .orig_ty = ptr_ty, 17587 .val = new_decl_val, 17588 } }, 17589 .len = (try mod.intValue(Type.usize, union_field_vals.len)).toIntern(), 17590 } }); 17591 }; 17592 17593 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod)); 17594 17595 const enum_tag_ty_val = try mod.intern(.{ .opt = .{ 17596 .ty = (try mod.optionalType(.type_type)).toIntern(), 17597 .val = if (ty.unionTagType(mod)) |tag_ty| tag_ty.toIntern() else .none, 17598 } }); 17599 17600 const container_layout_ty = t: { 17601 const decl_index = (try sema.namespaceLookup( 17602 block, 17603 src, 17604 (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, 17605 try ip.getOrPutString(gpa, "ContainerLayout"), 17606 )).?; 17607 try sema.ensureDeclAnalyzed(decl_index); 17608 const decl = mod.declPtr(decl_index); 17609 break :t decl.val.toType(); 17610 }; 17611 17612 const field_values = .{ 17613 // layout: ContainerLayout, 17614 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17615 17616 // tag_type: ?type, 17617 enum_tag_ty_val, 17618 // fields: []const UnionField, 17619 fields_val, 17620 // decls: []const Declaration, 17621 decls_val, 17622 }; 17623 return Air.internedToRef((try mod.intern(.{ .un = .{ 17624 .ty = type_info_ty.toIntern(), 17625 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Union))).toIntern(), 17626 .val = try mod.intern(.{ .aggregate = .{ 17627 .ty = type_union_ty.toIntern(), 17628 .storage = .{ .elems = &field_values }, 17629 } }), 17630 } }))); 17631 }, 17632 .Struct => { 17633 const type_struct_ty = t: { 17634 const type_struct_ty_decl_index = (try sema.namespaceLookup( 17635 block, 17636 src, 17637 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17638 try ip.getOrPutString(gpa, "Struct"), 17639 )).?; 17640 try sema.ensureDeclAnalyzed(type_struct_ty_decl_index); 17641 const type_struct_ty_decl = mod.declPtr(type_struct_ty_decl_index); 17642 break :t type_struct_ty_decl.val.toType(); 17643 }; 17644 17645 const struct_field_ty = t: { 17646 const struct_field_ty_decl_index = (try sema.namespaceLookup( 17647 block, 17648 src, 17649 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17650 try ip.getOrPutString(gpa, "StructField"), 17651 )).?; 17652 try sema.ensureDeclAnalyzed(struct_field_ty_decl_index); 17653 const struct_field_ty_decl = mod.declPtr(struct_field_ty_decl_index); 17654 break :t struct_field_ty_decl.val.toType(); 17655 }; 17656 17657 try sema.resolveTypeLayout(ty); // Getting alignment requires type layout 17658 17659 var struct_field_vals: []InternPool.Index = &.{}; 17660 defer gpa.free(struct_field_vals); 17661 fv: { 17662 const struct_type = switch (ip.indexToKey(ty.toIntern())) { 17663 .anon_struct_type => |tuple| { 17664 struct_field_vals = try gpa.alloc(InternPool.Index, tuple.types.len); 17665 for (struct_field_vals, 0..) |*struct_field_val, i| { 17666 const anon_struct_type = ip.indexToKey(ty.toIntern()).anon_struct_type; 17667 const field_ty = anon_struct_type.types.get(ip)[i]; 17668 const field_val = anon_struct_type.values.get(ip)[i]; 17669 const name_val = v: { 17670 // TODO: write something like getCoercedInts to avoid needing to dupe 17671 const bytes = if (tuple.names.len != 0) 17672 // https://github.com/ziglang/zig/issues/15709 17673 try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(ty.toIntern()).anon_struct_type.names.get(ip)[i])) 17674 else 17675 try std.fmt.allocPrint(sema.arena, "{d}", .{i}); 17676 const new_decl_ty = try mod.arrayType(.{ 17677 .len = bytes.len, 17678 .child = .u8_type, 17679 }); 17680 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17681 .ty = new_decl_ty.toIntern(), 17682 .storage = .{ .bytes = bytes }, 17683 } }); 17684 break :v try mod.intern(.{ .ptr = .{ 17685 .ty = .slice_const_u8_type, 17686 .addr = .{ .anon_decl = .{ 17687 .val = new_decl_val, 17688 .orig_ty = .slice_const_u8_type, 17689 } }, 17690 .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), 17691 } }); 17692 }; 17693 17694 try sema.resolveTypeLayout(Type.fromInterned(field_ty)); 17695 17696 const is_comptime = field_val != .none; 17697 const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null; 17698 const default_val_ptr = try sema.optRefValue(opt_default_val); 17699 const struct_field_fields = .{ 17700 // name: []const u8, 17701 name_val, 17702 // type: type, 17703 field_ty, 17704 // default_value: ?*const anyopaque, 17705 default_val_ptr.toIntern(), 17706 // is_comptime: bool, 17707 Value.makeBool(is_comptime).toIntern(), 17708 // alignment: comptime_int, 17709 (try mod.intValue(Type.comptime_int, Type.fromInterned(field_ty).abiAlignment(mod).toByteUnits(0))).toIntern(), 17710 }; 17711 struct_field_val.* = try mod.intern(.{ .aggregate = .{ 17712 .ty = struct_field_ty.toIntern(), 17713 .storage = .{ .elems = &struct_field_fields }, 17714 } }); 17715 } 17716 break :fv; 17717 }, 17718 .struct_type => |s| s, 17719 else => unreachable, 17720 }; 17721 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len); 17722 17723 try sema.resolveStructFieldInits(ty); 17724 17725 for (struct_field_vals, 0..) |*field_val, i| { 17726 // TODO: write something like getCoercedInts to avoid needing to dupe 17727 const name = if (struct_type.fieldName(ip, i).unwrap()) |name_nts| 17728 try sema.arena.dupe(u8, ip.stringToSlice(name_nts)) 17729 else 17730 try std.fmt.allocPrintZ(sema.arena, "{d}", .{i}); 17731 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 17732 const field_init = struct_type.fieldInit(ip, i); 17733 const field_is_comptime = struct_type.fieldIsComptime(ip, i); 17734 const name_val = v: { 17735 const new_decl_ty = try mod.arrayType(.{ 17736 .len = name.len, 17737 .child = .u8_type, 17738 }); 17739 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17740 .ty = new_decl_ty.toIntern(), 17741 .storage = .{ .bytes = name }, 17742 } }); 17743 break :v try mod.intern(.{ .ptr = .{ 17744 .ty = .slice_const_u8_type, 17745 .addr = .{ .anon_decl = .{ 17746 .val = new_decl_val, 17747 .orig_ty = .slice_const_u8_type, 17748 } }, 17749 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17750 } }); 17751 }; 17752 17753 const opt_default_val = if (field_init == .none) null else Value.fromInterned(field_init); 17754 const default_val_ptr = try sema.optRefValue(opt_default_val); 17755 const alignment = switch (struct_type.layout) { 17756 .Packed => .none, 17757 else => try sema.structFieldAlignment( 17758 struct_type.fieldAlign(ip, i), 17759 field_ty, 17760 struct_type.layout, 17761 ), 17762 }; 17763 17764 const struct_field_fields = .{ 17765 // name: []const u8, 17766 name_val, 17767 // type: type, 17768 field_ty.toIntern(), 17769 // default_value: ?*const anyopaque, 17770 default_val_ptr.toIntern(), 17771 // is_comptime: bool, 17772 Value.makeBool(field_is_comptime).toIntern(), 17773 // alignment: comptime_int, 17774 (try mod.intValue(Type.comptime_int, alignment.toByteUnits(0))).toIntern(), 17775 }; 17776 field_val.* = try mod.intern(.{ .aggregate = .{ 17777 .ty = struct_field_ty.toIntern(), 17778 .storage = .{ .elems = &struct_field_fields }, 17779 } }); 17780 } 17781 } 17782 17783 const fields_val = v: { 17784 const array_fields_ty = try mod.arrayType(.{ 17785 .len = struct_field_vals.len, 17786 .child = struct_field_ty.toIntern(), 17787 }); 17788 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17789 .ty = array_fields_ty.toIntern(), 17790 .storage = .{ .elems = struct_field_vals }, 17791 } }); 17792 const ptr_ty = (try sema.ptrType(.{ 17793 .child = struct_field_ty.toIntern(), 17794 .flags = .{ 17795 .size = .Slice, 17796 .is_const = true, 17797 }, 17798 })).toIntern(); 17799 break :v try mod.intern(.{ .ptr = .{ 17800 .ty = ptr_ty, 17801 .addr = .{ .anon_decl = .{ 17802 .orig_ty = ptr_ty, 17803 .val = new_decl_val, 17804 } }, 17805 .len = (try mod.intValue(Type.usize, struct_field_vals.len)).toIntern(), 17806 } }); 17807 }; 17808 17809 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod)); 17810 17811 const backing_integer_val = try mod.intern(.{ .opt = .{ 17812 .ty = (try mod.optionalType(.type_type)).toIntern(), 17813 .val = if (mod.typeToPackedStruct(ty)) |packed_struct| val: { 17814 assert(Type.fromInterned(packed_struct.backingIntType(ip).*).isInt(mod)); 17815 break :val packed_struct.backingIntType(ip).*; 17816 } else .none, 17817 } }); 17818 17819 const container_layout_ty = t: { 17820 const decl_index = (try sema.namespaceLookup( 17821 block, 17822 src, 17823 (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, 17824 try ip.getOrPutString(gpa, "ContainerLayout"), 17825 )).?; 17826 try sema.ensureDeclAnalyzed(decl_index); 17827 const decl = mod.declPtr(decl_index); 17828 break :t decl.val.toType(); 17829 }; 17830 17831 const layout = ty.containerLayout(mod); 17832 17833 const field_values = [_]InternPool.Index{ 17834 // layout: ContainerLayout, 17835 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17836 // backing_integer: ?type, 17837 backing_integer_val, 17838 // fields: []const StructField, 17839 fields_val, 17840 // decls: []const Declaration, 17841 decls_val, 17842 // is_tuple: bool, 17843 Value.makeBool(ty.isTuple(mod)).toIntern(), 17844 }; 17845 return Air.internedToRef((try mod.intern(.{ .un = .{ 17846 .ty = type_info_ty.toIntern(), 17847 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Struct))).toIntern(), 17848 .val = try mod.intern(.{ .aggregate = .{ 17849 .ty = type_struct_ty.toIntern(), 17850 .storage = .{ .elems = &field_values }, 17851 } }), 17852 } }))); 17853 }, 17854 .Opaque => { 17855 const type_opaque_ty = t: { 17856 const type_opaque_ty_decl_index = (try sema.namespaceLookup( 17857 block, 17858 src, 17859 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17860 try ip.getOrPutString(gpa, "Opaque"), 17861 )).?; 17862 try sema.ensureDeclAnalyzed(type_opaque_ty_decl_index); 17863 const type_opaque_ty_decl = mod.declPtr(type_opaque_ty_decl_index); 17864 break :t type_opaque_ty_decl.val.toType(); 17865 }; 17866 17867 try sema.resolveTypeFields(ty); 17868 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod)); 17869 17870 const field_values = .{ 17871 // decls: []const Declaration, 17872 decls_val, 17873 }; 17874 return Air.internedToRef((try mod.intern(.{ .un = .{ 17875 .ty = type_info_ty.toIntern(), 17876 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Opaque))).toIntern(), 17877 .val = try mod.intern(.{ .aggregate = .{ 17878 .ty = type_opaque_ty.toIntern(), 17879 .storage = .{ .elems = &field_values }, 17880 } }), 17881 } }))); 17882 }, 17883 .Frame => return sema.failWithUseOfAsync(block, src), 17884 .AnyFrame => return sema.failWithUseOfAsync(block, src), 17885 } 17886 } 17887 17888 fn typeInfoDecls( 17889 sema: *Sema, 17890 block: *Block, 17891 src: LazySrcLoc, 17892 type_info_ty: Type, 17893 opt_namespace: InternPool.OptionalNamespaceIndex, 17894 ) CompileError!InternPool.Index { 17895 const mod = sema.mod; 17896 const gpa = sema.gpa; 17897 17898 const declaration_ty = t: { 17899 const declaration_ty_decl_index = (try sema.namespaceLookup( 17900 block, 17901 src, 17902 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17903 try mod.intern_pool.getOrPutString(gpa, "Declaration"), 17904 )).?; 17905 try sema.ensureDeclAnalyzed(declaration_ty_decl_index); 17906 const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index); 17907 break :t declaration_ty_decl.val.toType(); 17908 }; 17909 try sema.queueFullTypeResolution(declaration_ty); 17910 17911 var decl_vals = std.ArrayList(InternPool.Index).init(gpa); 17912 defer decl_vals.deinit(); 17913 17914 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); 17915 defer seen_namespaces.deinit(); 17916 17917 if (opt_namespace.unwrap()) |namespace_index| { 17918 const namespace = mod.namespacePtr(namespace_index); 17919 try sema.typeInfoNamespaceDecls(block, namespace, declaration_ty, &decl_vals, &seen_namespaces); 17920 } 17921 17922 const array_decl_ty = try mod.arrayType(.{ 17923 .len = decl_vals.items.len, 17924 .child = declaration_ty.toIntern(), 17925 }); 17926 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17927 .ty = array_decl_ty.toIntern(), 17928 .storage = .{ .elems = decl_vals.items }, 17929 } }); 17930 const ptr_ty = (try sema.ptrType(.{ 17931 .child = declaration_ty.toIntern(), 17932 .flags = .{ 17933 .size = .Slice, 17934 .is_const = true, 17935 }, 17936 })).toIntern(); 17937 return try mod.intern(.{ .ptr = .{ 17938 .ty = ptr_ty, 17939 .addr = .{ .anon_decl = .{ 17940 .orig_ty = ptr_ty, 17941 .val = new_decl_val, 17942 } }, 17943 .len = (try mod.intValue(Type.usize, decl_vals.items.len)).toIntern(), 17944 } }); 17945 } 17946 17947 fn typeInfoNamespaceDecls( 17948 sema: *Sema, 17949 block: *Block, 17950 namespace: *Namespace, 17951 declaration_ty: Type, 17952 decl_vals: *std.ArrayList(InternPool.Index), 17953 seen_namespaces: *std.AutoHashMap(*Namespace, void), 17954 ) !void { 17955 const mod = sema.mod; 17956 const ip = &mod.intern_pool; 17957 const gop = try seen_namespaces.getOrPut(namespace); 17958 if (gop.found_existing) return; 17959 const decls = namespace.decls.keys(); 17960 for (decls) |decl_index| { 17961 const decl = mod.declPtr(decl_index); 17962 if (decl.kind == .@"usingnamespace") { 17963 if (decl.analysis == .in_progress) continue; 17964 try mod.ensureDeclAnalyzed(decl_index); 17965 const new_ns = decl.val.toType().getNamespace(mod).?; 17966 try sema.typeInfoNamespaceDecls(block, new_ns, declaration_ty, decl_vals, seen_namespaces); 17967 continue; 17968 } 17969 if (decl.kind != .named or !decl.is_pub) continue; 17970 const name_val = v: { 17971 // TODO: write something like getCoercedInts to avoid needing to dupe 17972 const name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name)); 17973 const new_decl_ty = try mod.arrayType(.{ 17974 .len = name.len, 17975 .child = .u8_type, 17976 }); 17977 const new_decl_val = try mod.intern(.{ .aggregate = .{ 17978 .ty = new_decl_ty.toIntern(), 17979 .storage = .{ .bytes = name }, 17980 } }); 17981 break :v try mod.intern(.{ .ptr = .{ 17982 .ty = .slice_const_u8_type, 17983 .addr = .{ .anon_decl = .{ 17984 .orig_ty = .slice_const_u8_type, 17985 .val = new_decl_val, 17986 } }, 17987 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17988 } }); 17989 }; 17990 17991 const fields = .{ 17992 //name: []const u8, 17993 name_val, 17994 }; 17995 try decl_vals.append(try mod.intern(.{ .aggregate = .{ 17996 .ty = declaration_ty.toIntern(), 17997 .storage = .{ .elems = &fields }, 17998 } })); 17999 } 18000 } 18001 18002 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18003 _ = block; 18004 const zir_datas = sema.code.instructions.items(.data); 18005 const inst_data = zir_datas[@intFromEnum(inst)].un_node; 18006 const operand = try sema.resolveInst(inst_data.operand); 18007 const operand_ty = sema.typeOf(operand); 18008 return Air.internedToRef(operand_ty.toIntern()); 18009 } 18010 18011 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18012 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18013 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 18014 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18015 18016 var child_block: Block = .{ 18017 .parent = block, 18018 .sema = sema, 18019 .src_decl = block.src_decl, 18020 .namespace = block.namespace, 18021 .wip_capture_scope = block.wip_capture_scope, 18022 .instructions = .{}, 18023 .inlining = block.inlining, 18024 .is_comptime = false, 18025 .is_typeof = true, 18026 .want_safety = false, 18027 .error_return_trace_index = block.error_return_trace_index, 18028 }; 18029 defer child_block.instructions.deinit(sema.gpa); 18030 18031 const operand = try sema.resolveBody(&child_block, body, inst); 18032 const operand_ty = sema.typeOf(operand); 18033 if (operand_ty.isGenericPoison()) return error.GenericPoison; 18034 return Air.internedToRef(operand_ty.toIntern()); 18035 } 18036 18037 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18038 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18039 const src = inst_data.src(); 18040 const operand = try sema.resolveInst(inst_data.operand); 18041 const operand_ty = sema.typeOf(operand); 18042 const res_ty = try sema.log2IntType(block, operand_ty, src); 18043 return Air.internedToRef(res_ty.toIntern()); 18044 } 18045 18046 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type { 18047 const mod = sema.mod; 18048 switch (operand.zigTypeTag(mod)) { 18049 .ComptimeInt => return Type.comptime_int, 18050 .Int => { 18051 const bits = operand.bitSize(mod); 18052 const count = if (bits == 0) 18053 0 18054 else blk: { 18055 var count: u16 = 0; 18056 var s = bits - 1; 18057 while (s != 0) : (s >>= 1) { 18058 count += 1; 18059 } 18060 break :blk count; 18061 }; 18062 return mod.intType(.unsigned, count); 18063 }, 18064 .Vector => { 18065 const elem_ty = operand.elemType2(mod); 18066 const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); 18067 return mod.vectorType(.{ 18068 .len = operand.vectorLen(mod), 18069 .child = log2_elem_ty.toIntern(), 18070 }); 18071 }, 18072 else => {}, 18073 } 18074 return sema.fail( 18075 block, 18076 src, 18077 "bit shifting operation expected integer type, found '{}'", 18078 .{operand.fmt(mod)}, 18079 ); 18080 } 18081 18082 fn zirTypeofPeer( 18083 sema: *Sema, 18084 block: *Block, 18085 extended: Zir.Inst.Extended.InstData, 18086 ) CompileError!Air.Inst.Ref { 18087 const tracy = trace(@src()); 18088 defer tracy.end(); 18089 18090 const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); 18091 const src = LazySrcLoc.nodeOffset(extra.data.src_node); 18092 const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len); 18093 18094 var child_block: Block = .{ 18095 .parent = block, 18096 .sema = sema, 18097 .src_decl = block.src_decl, 18098 .namespace = block.namespace, 18099 .wip_capture_scope = block.wip_capture_scope, 18100 .instructions = .{}, 18101 .inlining = block.inlining, 18102 .is_comptime = false, 18103 .is_typeof = true, 18104 .runtime_cond = block.runtime_cond, 18105 .runtime_loop = block.runtime_loop, 18106 .runtime_index = block.runtime_index, 18107 }; 18108 defer child_block.instructions.deinit(sema.gpa); 18109 // Ignore the result, we only care about the instructions in `args`. 18110 _ = try sema.analyzeBodyBreak(&child_block, body); 18111 18112 const args = sema.code.refSlice(extra.end, extended.small); 18113 18114 const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len); 18115 defer sema.gpa.free(inst_list); 18116 18117 for (args, 0..) |arg_ref, i| { 18118 inst_list[i] = try sema.resolveInst(arg_ref); 18119 } 18120 18121 const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); 18122 return Air.internedToRef(result_type.toIntern()); 18123 } 18124 18125 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18126 const tracy = trace(@src()); 18127 defer tracy.end(); 18128 18129 const mod = sema.mod; 18130 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18131 const src = inst_data.src(); 18132 const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 18133 const uncasted_operand = try sema.resolveInst(inst_data.operand); 18134 18135 const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src); 18136 if (try sema.resolveValue(operand)) |val| { 18137 return if (val.isUndef(mod)) 18138 mod.undefRef(Type.bool) 18139 else if (val.toBool()) .bool_false else .bool_true; 18140 } 18141 try sema.requireRuntimeBlock(block, src, null); 18142 return block.addTyOp(.not, Type.bool, operand); 18143 } 18144 18145 fn zirBoolBr( 18146 sema: *Sema, 18147 parent_block: *Block, 18148 inst: Zir.Inst.Index, 18149 is_bool_or: bool, 18150 ) CompileError!Air.Inst.Ref { 18151 const tracy = trace(@src()); 18152 defer tracy.end(); 18153 18154 const mod = sema.mod; 18155 const datas = sema.code.instructions.items(.data); 18156 const inst_data = datas[@intFromEnum(inst)].bool_br; 18157 const lhs = try sema.resolveInst(inst_data.lhs); 18158 const lhs_src = sema.src; 18159 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 18160 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18161 const gpa = sema.gpa; 18162 18163 if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| { 18164 if (is_bool_or and lhs_val.toBool()) { 18165 return .bool_true; 18166 } else if (!is_bool_or and !lhs_val.toBool()) { 18167 return .bool_false; 18168 } 18169 // comptime-known left-hand side. No need for a block here; the result 18170 // is simply the rhs expression. Here we rely on there only being 1 18171 // break instruction (`break_inline`). 18172 return sema.resolveBody(parent_block, body, inst); 18173 } 18174 18175 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 18176 try sema.air_instructions.append(gpa, .{ 18177 .tag = .block, 18178 .data = .{ .ty_pl = .{ 18179 .ty = .bool_type, 18180 .payload = undefined, 18181 } }, 18182 }); 18183 18184 var child_block = parent_block.makeSubBlock(); 18185 child_block.runtime_loop = null; 18186 child_block.runtime_cond = lhs_src; 18187 child_block.runtime_index.increment(); 18188 defer child_block.instructions.deinit(gpa); 18189 18190 var then_block = child_block.makeSubBlock(); 18191 defer then_block.instructions.deinit(gpa); 18192 18193 var else_block = child_block.makeSubBlock(); 18194 defer else_block.instructions.deinit(gpa); 18195 18196 const lhs_block = if (is_bool_or) &then_block else &else_block; 18197 const rhs_block = if (is_bool_or) &else_block else &then_block; 18198 18199 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false; 18200 _ = try lhs_block.addBr(block_inst, lhs_result); 18201 18202 const rhs_result = try sema.resolveBody(rhs_block, body, inst); 18203 if (!sema.typeOf(rhs_result).isNoReturn(mod)) { 18204 _ = try rhs_block.addBr(block_inst, rhs_result); 18205 } 18206 18207 const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst); 18208 if (!sema.typeOf(rhs_result).isNoReturn(mod)) { 18209 if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| { 18210 if (is_bool_or and rhs_val.toBool()) { 18211 return .bool_true; 18212 } else if (!is_bool_or and !rhs_val.toBool()) { 18213 return .bool_false; 18214 } 18215 } 18216 } 18217 18218 return result; 18219 } 18220 18221 fn finishCondBr( 18222 sema: *Sema, 18223 parent_block: *Block, 18224 child_block: *Block, 18225 then_block: *Block, 18226 else_block: *Block, 18227 cond: Air.Inst.Ref, 18228 block_inst: Air.Inst.Index, 18229 ) !Air.Inst.Ref { 18230 const gpa = sema.gpa; 18231 18232 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 18233 then_block.instructions.items.len + else_block.instructions.items.len + 18234 @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1); 18235 18236 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18237 .then_body_len = @intCast(then_block.instructions.items.len), 18238 .else_body_len = @intCast(else_block.instructions.items.len), 18239 }); 18240 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 18241 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 18242 18243 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 18244 .operand = cond, 18245 .payload = cond_br_payload, 18246 } } }); 18247 18248 sema.air_instructions.items(.data)[@intFromEnum(block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity( 18249 Air.Block{ .body_len = @intCast(child_block.instructions.items.len) }, 18250 ); 18251 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); 18252 18253 try parent_block.instructions.append(gpa, block_inst); 18254 return block_inst.toRef(); 18255 } 18256 18257 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 18258 const mod = sema.mod; 18259 switch (ty.zigTypeTag(mod)) { 18260 .Optional, .Null, .Undefined => return, 18261 .Pointer => if (ty.isPtrLikeOptional(mod)) return, 18262 else => {}, 18263 } 18264 return sema.failWithExpectedOptionalType(block, src, ty); 18265 } 18266 18267 fn zirIsNonNull( 18268 sema: *Sema, 18269 block: *Block, 18270 inst: Zir.Inst.Index, 18271 ) CompileError!Air.Inst.Ref { 18272 const tracy = trace(@src()); 18273 defer tracy.end(); 18274 18275 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18276 const src = inst_data.src(); 18277 const operand = try sema.resolveInst(inst_data.operand); 18278 try sema.checkNullableType(block, src, sema.typeOf(operand)); 18279 return sema.analyzeIsNull(block, src, operand, true); 18280 } 18281 18282 fn zirIsNonNullPtr( 18283 sema: *Sema, 18284 block: *Block, 18285 inst: Zir.Inst.Index, 18286 ) CompileError!Air.Inst.Ref { 18287 const tracy = trace(@src()); 18288 defer tracy.end(); 18289 18290 const mod = sema.mod; 18291 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18292 const src = inst_data.src(); 18293 const ptr = try sema.resolveInst(inst_data.operand); 18294 try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(mod)); 18295 if ((try sema.resolveValue(ptr)) == null) { 18296 return block.addUnOp(.is_non_null_ptr, ptr); 18297 } 18298 const loaded = try sema.analyzeLoad(block, src, ptr, src); 18299 return sema.analyzeIsNull(block, src, loaded, true); 18300 } 18301 18302 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 18303 const mod = sema.mod; 18304 switch (ty.zigTypeTag(mod)) { 18305 .ErrorSet, .ErrorUnion, .Undefined => return, 18306 else => return sema.fail(block, src, "expected error union type, found '{}'", .{ 18307 ty.fmt(mod), 18308 }), 18309 } 18310 } 18311 18312 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18313 const tracy = trace(@src()); 18314 defer tracy.end(); 18315 18316 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18317 const src = inst_data.src(); 18318 const operand = try sema.resolveInst(inst_data.operand); 18319 try sema.checkErrorType(block, src, sema.typeOf(operand)); 18320 return sema.analyzeIsNonErr(block, src, operand); 18321 } 18322 18323 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18324 const tracy = trace(@src()); 18325 defer tracy.end(); 18326 18327 const mod = sema.mod; 18328 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18329 const src = inst_data.src(); 18330 const ptr = try sema.resolveInst(inst_data.operand); 18331 try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(mod)); 18332 const loaded = try sema.analyzeLoad(block, src, ptr, src); 18333 return sema.analyzeIsNonErr(block, src, loaded); 18334 } 18335 18336 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18337 const tracy = trace(@src()); 18338 defer tracy.end(); 18339 18340 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18341 const src = inst_data.src(); 18342 const operand = try sema.resolveInst(inst_data.operand); 18343 return sema.analyzeIsNonErr(block, src, operand); 18344 } 18345 18346 fn zirCondbr( 18347 sema: *Sema, 18348 parent_block: *Block, 18349 inst: Zir.Inst.Index, 18350 ) CompileError!Zir.Inst.Index { 18351 const tracy = trace(@src()); 18352 defer tracy.end(); 18353 18354 const mod = sema.mod; 18355 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18356 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; 18357 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 18358 18359 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); 18360 const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len); 18361 18362 const uncasted_cond = try sema.resolveInst(extra.data.condition); 18363 const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src); 18364 18365 if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| { 18366 const body = if (cond_val.toBool()) then_body else else_body; 18367 18368 try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src); 18369 // We use `analyzeBodyInner` since we want to propagate any possible 18370 // `error.ComptimeBreak` to the caller. 18371 return sema.analyzeBodyInner(parent_block, body); 18372 } 18373 18374 const gpa = sema.gpa; 18375 18376 // We'll re-use the sub block to save on memory bandwidth, and yank out the 18377 // instructions array in between using it for the then block and else block. 18378 var sub_block = parent_block.makeSubBlock(); 18379 sub_block.runtime_loop = null; 18380 sub_block.runtime_cond = cond_src; 18381 sub_block.runtime_index.increment(); 18382 defer sub_block.instructions.deinit(gpa); 18383 18384 try sema.analyzeBodyRuntimeBreak(&sub_block, then_body); 18385 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); 18386 defer gpa.free(true_instructions); 18387 18388 const err_cond = blk: { 18389 const index = extra.data.condition.toIndex() orelse break :blk null; 18390 if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) break :blk null; 18391 18392 const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node; 18393 const err_operand = try sema.resolveInst(err_inst_data.operand); 18394 const operand_ty = sema.typeOf(err_operand); 18395 assert(operand_ty.zigTypeTag(mod) == .ErrorUnion); 18396 const result_ty = operand_ty.errorUnionSet(mod); 18397 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); 18398 }; 18399 18400 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src)) { 18401 // nothing to do 18402 } else { 18403 try sema.analyzeBodyRuntimeBreak(&sub_block, else_body); 18404 } 18405 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 18406 true_instructions.len + sub_block.instructions.items.len); 18407 _ = try parent_block.addInst(.{ 18408 .tag = .cond_br, 18409 .data = .{ .pl_op = .{ 18410 .operand = cond, 18411 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18412 .then_body_len = @intCast(true_instructions.len), 18413 .else_body_len = @intCast(sub_block.instructions.items.len), 18414 }), 18415 } }, 18416 }); 18417 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); 18418 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18419 return always_noreturn; 18420 } 18421 18422 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18423 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18424 const src = inst_data.src(); 18425 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 18426 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18427 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18428 const err_union = try sema.resolveInst(extra.data.operand); 18429 const err_union_ty = sema.typeOf(err_union); 18430 const mod = sema.mod; 18431 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 18432 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ 18433 err_union_ty.fmt(mod), 18434 }); 18435 } 18436 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18437 if (is_non_err != .none) { 18438 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18439 if (is_non_err_val.toBool()) { 18440 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); 18441 } 18442 // We can analyze the body directly in the parent block because we know there are 18443 // no breaks from the body possible, and that the body is noreturn. 18444 return sema.resolveBody(parent_block, body, inst); 18445 } 18446 18447 var sub_block = parent_block.makeSubBlock(); 18448 defer sub_block.instructions.deinit(sema.gpa); 18449 18450 // This body is guaranteed to end with noreturn and has no breaks. 18451 _ = try sema.analyzeBodyInner(&sub_block, body); 18452 18453 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + 18454 sub_block.instructions.items.len); 18455 const try_inst = try parent_block.addInst(.{ 18456 .tag = .@"try", 18457 .data = .{ .pl_op = .{ 18458 .operand = err_union, 18459 .payload = sema.addExtraAssumeCapacity(Air.Try{ 18460 .body_len = @intCast(sub_block.instructions.items.len), 18461 }), 18462 } }, 18463 }); 18464 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18465 return try_inst; 18466 } 18467 18468 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18469 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 18470 const src = inst_data.src(); 18471 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 18472 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18473 const body = sema.code.bodySlice(extra.end, extra.data.body_len); 18474 const operand = try sema.resolveInst(extra.data.operand); 18475 const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); 18476 const err_union_ty = sema.typeOf(err_union); 18477 const mod = sema.mod; 18478 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 18479 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ 18480 err_union_ty.fmt(mod), 18481 }); 18482 } 18483 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18484 if (is_non_err != .none) { 18485 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18486 if (is_non_err_val.toBool()) { 18487 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); 18488 } 18489 // We can analyze the body directly in the parent block because we know there are 18490 // no breaks from the body possible, and that the body is noreturn. 18491 return sema.resolveBody(parent_block, body, inst); 18492 } 18493 18494 var sub_block = parent_block.makeSubBlock(); 18495 defer sub_block.instructions.deinit(sema.gpa); 18496 18497 // This body is guaranteed to end with noreturn and has no breaks. 18498 _ = try sema.analyzeBodyInner(&sub_block, body); 18499 18500 const operand_ty = sema.typeOf(operand); 18501 const ptr_info = operand_ty.ptrInfo(mod); 18502 const res_ty = try sema.ptrType(.{ 18503 .child = err_union_ty.errorUnionPayload(mod).toIntern(), 18504 .flags = .{ 18505 .is_const = ptr_info.flags.is_const, 18506 .is_volatile = ptr_info.flags.is_volatile, 18507 .is_allowzero = ptr_info.flags.is_allowzero, 18508 .address_space = ptr_info.flags.address_space, 18509 }, 18510 }); 18511 const res_ty_ref = Air.internedToRef(res_ty.toIntern()); 18512 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len + 18513 sub_block.instructions.items.len); 18514 const try_inst = try parent_block.addInst(.{ 18515 .tag = .try_ptr, 18516 .data = .{ .ty_pl = .{ 18517 .ty = res_ty_ref, 18518 .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ 18519 .ptr = operand, 18520 .body_len = @intCast(sub_block.instructions.items.len), 18521 }), 18522 } }, 18523 }); 18524 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); 18525 return try_inst; 18526 } 18527 18528 // A `break` statement is inside a runtime condition, but trying to 18529 // break from an inline loop. In such case we must convert it to 18530 // a runtime break. 18531 fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void { 18532 const gop = sema.inst_map.getOrPutAssumeCapacity(break_data.block_inst); 18533 const labeled_block = if (!gop.found_existing) blk: { 18534 try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); 18535 18536 const new_block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 18537 gop.value_ptr.* = new_block_inst.toRef(); 18538 try sema.air_instructions.append(sema.gpa, .{ 18539 .tag = .block, 18540 .data = undefined, 18541 }); 18542 const labeled_block = try sema.gpa.create(LabeledBlock); 18543 labeled_block.* = .{ 18544 .label = .{ 18545 .zir_block = break_data.block_inst, 18546 .merges = .{ 18547 .src_locs = .{}, 18548 .results = .{}, 18549 .br_list = .{}, 18550 .block_inst = new_block_inst, 18551 }, 18552 }, 18553 .block = .{ 18554 .parent = child_block, 18555 .sema = sema, 18556 .src_decl = child_block.src_decl, 18557 .namespace = child_block.namespace, 18558 .wip_capture_scope = child_block.wip_capture_scope, 18559 .instructions = .{}, 18560 .label = &labeled_block.label, 18561 .inlining = child_block.inlining, 18562 .is_comptime = child_block.is_comptime, 18563 }, 18564 }; 18565 sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); 18566 break :blk labeled_block; 18567 } else blk: { 18568 const new_block_inst = gop.value_ptr.*.toIndex().?; 18569 const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?; 18570 break :blk labeled_block; 18571 }; 18572 18573 const operand = try sema.resolveInst(break_data.operand); 18574 const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); 18575 try labeled_block.label.merges.results.append(sema.gpa, operand); 18576 try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); 18577 labeled_block.block.runtime_index.increment(); 18578 if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { 18579 labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; 18580 labeled_block.block.runtime_loop = child_block.runtime_loop; 18581 } 18582 } 18583 18584 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18585 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable"; 18586 const src = inst_data.src(); 18587 18588 if (block.is_comptime) { 18589 return sema.fail(block, src, "reached unreachable code", .{}); 18590 } 18591 // TODO Add compile error for @optimizeFor occurring too late in a scope. 18592 block.addUnreachable(src, true) catch |err| switch (err) { 18593 error.AnalysisFail => { 18594 const msg = sema.err orelse return err; 18595 if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err; 18596 try sema.errNote(block, src, msg, "the end of a naked function is implicitly unreachable", .{}); 18597 return err; 18598 }, 18599 else => |e| return e, 18600 }; 18601 return always_noreturn; 18602 } 18603 18604 fn zirRetErrValue( 18605 sema: *Sema, 18606 block: *Block, 18607 inst: Zir.Inst.Index, 18608 ) CompileError!Zir.Inst.Index { 18609 const mod = sema.mod; 18610 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; 18611 const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 18612 _ = try mod.getErrorValue(err_name); 18613 const src = inst_data.src(); 18614 // Return the error code from the function. 18615 const error_set_type = try mod.singleErrorSetType(err_name); 18616 const result_inst = Air.internedToRef((try mod.intern(.{ .err = .{ 18617 .ty = error_set_type.toIntern(), 18618 .name = err_name, 18619 } }))); 18620 return sema.analyzeRet(block, result_inst, src); 18621 } 18622 18623 fn zirRetImplicit( 18624 sema: *Sema, 18625 block: *Block, 18626 inst: Zir.Inst.Index, 18627 ) CompileError!Zir.Inst.Index { 18628 const tracy = trace(@src()); 18629 defer tracy.end(); 18630 18631 const mod = sema.mod; 18632 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; 18633 const r_brace_src = inst_data.src(); 18634 if (block.inlining == null and sema.func_is_naked) { 18635 assert(!block.is_comptime); 18636 if (block.wantSafety()) { 18637 // Calling a safety function from a naked function would not be legal. 18638 _ = try block.addNoOp(.trap); 18639 } else { 18640 try block.addUnreachable(r_brace_src, false); 18641 } 18642 return always_noreturn; 18643 } 18644 18645 const operand = try sema.resolveInst(inst_data.operand); 18646 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 18647 const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod); 18648 if (base_tag == .NoReturn) { 18649 const msg = msg: { 18650 const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{ 18651 sema.fn_ret_ty.fmt(mod), 18652 }); 18653 errdefer msg.destroy(sema.gpa); 18654 try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); 18655 break :msg msg; 18656 }; 18657 return sema.failWithOwnedErrorMsg(block, msg); 18658 } else if (base_tag != .Void) { 18659 const msg = msg: { 18660 const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{ 18661 sema.fn_ret_ty.fmt(mod), 18662 }); 18663 errdefer msg.destroy(sema.gpa); 18664 try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); 18665 break :msg msg; 18666 }; 18667 return sema.failWithOwnedErrorMsg(block, msg); 18668 } 18669 18670 return sema.analyzeRet(block, operand, r_brace_src); 18671 } 18672 18673 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18674 const tracy = trace(@src()); 18675 defer tracy.end(); 18676 18677 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18678 const operand = try sema.resolveInst(inst_data.operand); 18679 const src = inst_data.src(); 18680 18681 return sema.analyzeRet(block, operand, src); 18682 } 18683 18684 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18685 const tracy = trace(@src()); 18686 defer tracy.end(); 18687 18688 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 18689 const src = inst_data.src(); 18690 const ret_ptr = try sema.resolveInst(inst_data.operand); 18691 18692 if (block.is_comptime or block.inlining != null or sema.func_is_naked) { 18693 const operand = try sema.analyzeLoad(block, src, ret_ptr, src); 18694 return sema.analyzeRet(block, operand, src); 18695 } 18696 18697 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18698 const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); 18699 return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr); 18700 } 18701 18702 _ = try block.addUnOp(.ret_load, ret_ptr); 18703 return always_noreturn; 18704 } 18705 18706 fn retWithErrTracing( 18707 sema: *Sema, 18708 block: *Block, 18709 src: LazySrcLoc, 18710 is_non_err: Air.Inst.Ref, 18711 ret_tag: Air.Inst.Tag, 18712 operand: Air.Inst.Ref, 18713 ) CompileError!Zir.Inst.Index { 18714 const mod = sema.mod; 18715 const need_check = switch (is_non_err) { 18716 .bool_true => { 18717 _ = try block.addUnOp(ret_tag, operand); 18718 return always_noreturn; 18719 }, 18720 .bool_false => false, 18721 else => true, 18722 }; 18723 const gpa = sema.gpa; 18724 const stack_trace_ty = try sema.getBuiltinType("StackTrace"); 18725 try sema.resolveTypeFields(stack_trace_ty); 18726 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 18727 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); 18728 const return_err_fn = try sema.getBuiltin("returnError"); 18729 const args: [1]Air.Inst.Ref = .{err_return_trace}; 18730 18731 if (!need_check) { 18732 try sema.callBuiltin(block, src, return_err_fn, .never_inline, &args, .@"error return"); 18733 _ = try block.addUnOp(ret_tag, operand); 18734 return always_noreturn; 18735 } 18736 18737 var then_block = block.makeSubBlock(); 18738 defer then_block.instructions.deinit(gpa); 18739 _ = try then_block.addUnOp(ret_tag, operand); 18740 18741 var else_block = block.makeSubBlock(); 18742 defer else_block.instructions.deinit(gpa); 18743 try sema.callBuiltin(&else_block, src, return_err_fn, .never_inline, &args, .@"error return"); 18744 _ = try else_block.addUnOp(ret_tag, operand); 18745 18746 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 18747 then_block.instructions.items.len + else_block.instructions.items.len + 18748 @typeInfo(Air.Block).Struct.fields.len + 1); 18749 18750 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18751 .then_body_len = @intCast(then_block.instructions.items.len), 18752 .else_body_len = @intCast(else_block.instructions.items.len), 18753 }); 18754 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); 18755 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); 18756 18757 _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 18758 .operand = is_non_err, 18759 .payload = cond_br_payload, 18760 } } }); 18761 18762 return always_noreturn; 18763 } 18764 18765 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { 18766 const mod = sema.mod; 18767 return fn_ret_ty.isError(mod) and mod.comp.config.any_error_tracing; 18768 } 18769 18770 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18771 const mod = sema.mod; 18772 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index; 18773 18774 if (!block.ownerModule().error_tracing) return; 18775 18776 // This is only relevant at runtime. 18777 if (block.is_comptime or block.is_typeof) return; 18778 18779 const save_index = inst_data.operand == .none or b: { 18780 const operand = try sema.resolveInst(inst_data.operand); 18781 const operand_ty = sema.typeOf(operand); 18782 break :b operand_ty.isError(mod); 18783 }; 18784 18785 if (save_index) 18786 block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block); 18787 } 18788 18789 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { 18790 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].restore_err_ret_index; 18791 const src = sema.src; // TODO 18792 18793 // This is only relevant at runtime. 18794 if (start_block.is_comptime or start_block.is_typeof) return; 18795 18796 const mod = sema.mod; 18797 const ip = &mod.intern_pool; 18798 18799 if (!ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn) return; 18800 if (!start_block.ownerModule().error_tracing) return; 18801 18802 const tracy = trace(@src()); 18803 defer tracy.end(); 18804 18805 const saved_index = if (inst_data.block.toIndexAllowNone()) |zir_block| b: { 18806 var block = start_block; 18807 while (true) { 18808 if (block.label) |label| { 18809 if (label.zir_block == zir_block) { 18810 const target_trace_index = if (block.parent) |parent_block| tgt: { 18811 break :tgt parent_block.error_return_trace_index; 18812 } else sema.error_return_trace_index_on_fn_entry; 18813 18814 if (start_block.error_return_trace_index != target_trace_index) 18815 break :b target_trace_index; 18816 18817 return; // No need to restore 18818 } 18819 } 18820 block = block.parent.?; 18821 } 18822 } else b: { 18823 if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry) 18824 break :b sema.error_return_trace_index_on_fn_entry; 18825 18826 return; // No need to restore 18827 }; 18828 18829 assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere 18830 18831 const operand = try sema.resolveInstAllowNone(inst_data.operand); 18832 return sema.popErrorReturnTrace(start_block, src, operand, saved_index); 18833 } 18834 18835 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { 18836 const mod = sema.mod; 18837 const ip = &mod.intern_pool; 18838 assert(sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion); 18839 const err_set_ty = sema.fn_ret_ty.errorUnionSet(mod).toIntern(); 18840 switch (err_set_ty) { 18841 .adhoc_inferred_error_set_type => { 18842 const ies = sema.fn_ret_ty_ies.?; 18843 assert(ies.func == .none); 18844 try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand)); 18845 }, 18846 else => if (ip.isInferredErrorSetType(err_set_ty)) { 18847 const ies = sema.fn_ret_ty_ies.?; 18848 assert(ies.func == sema.func_index); 18849 try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand)); 18850 }, 18851 } 18852 } 18853 18854 fn addToInferredErrorSetPtr(sema: *Sema, ies: *InferredErrorSet, op_ty: Type) !void { 18855 const arena = sema.arena; 18856 const mod = sema.mod; 18857 const ip = &mod.intern_pool; 18858 switch (op_ty.zigTypeTag(mod)) { 18859 .ErrorSet => try ies.addErrorSet(op_ty, ip, arena), 18860 .ErrorUnion => try ies.addErrorSet(op_ty.errorUnionSet(mod), ip, arena), 18861 else => {}, 18862 } 18863 } 18864 18865 fn analyzeRet( 18866 sema: *Sema, 18867 block: *Block, 18868 uncasted_operand: Air.Inst.Ref, 18869 src: LazySrcLoc, 18870 ) CompileError!Zir.Inst.Index { 18871 // Special case for returning an error to an inferred error set; we need to 18872 // add the error tag to the inferred error set of the in-scope function, so 18873 // that the coercion below works correctly. 18874 const mod = sema.mod; 18875 if (sema.fn_ret_ty_ies != null and sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) { 18876 try sema.addToInferredErrorSet(uncasted_operand); 18877 } 18878 const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) { 18879 error.NotCoercible => unreachable, 18880 else => |e| return e, 18881 }; 18882 18883 if (block.inlining) |inlining| { 18884 if (block.is_comptime) { 18885 _ = try sema.resolveConstValue(block, src, operand, .{ 18886 .needed_comptime_reason = "value being returned at comptime must be comptime-known", 18887 }); 18888 inlining.comptime_result = operand; 18889 return error.ComptimeReturn; 18890 } 18891 // We are inlining a function call; rewrite the `ret` as a `break`. 18892 try inlining.merges.results.append(sema.gpa, operand); 18893 _ = try block.addBr(inlining.merges.block_inst, operand); 18894 return always_noreturn; 18895 } else if (block.is_comptime) { 18896 return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{}); 18897 } else if (sema.func_is_naked) { 18898 const msg = msg: { 18899 const msg = try sema.errMsg(block, src, "cannot return from naked function", .{}); 18900 errdefer msg.destroy(sema.gpa); 18901 18902 try sema.errNote(block, src, msg, "can only return using assembly", .{}); 18903 break :msg msg; 18904 }; 18905 return sema.failWithOwnedErrorMsg(block, msg); 18906 } 18907 18908 try sema.resolveTypeLayout(sema.fn_ret_ty); 18909 18910 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18911 // Avoid adding a frame to the error return trace in case the value is comptime-known 18912 // to be not an error. 18913 const is_non_err = try sema.analyzeIsNonErr(block, src, operand); 18914 return sema.retWithErrTracing(block, src, is_non_err, .ret, operand); 18915 } 18916 18917 _ = try block.addUnOp(.ret, operand); 18918 18919 return always_noreturn; 18920 } 18921 18922 fn floatOpAllowed(tag: Zir.Inst.Tag) bool { 18923 // extend this swich as additional operators are implemented 18924 return switch (tag) { 18925 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true, 18926 else => false, 18927 }; 18928 } 18929 18930 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18931 const tracy = trace(@src()); 18932 defer tracy.end(); 18933 18934 const mod = sema.mod; 18935 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type; 18936 const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); 18937 const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node }; 18938 const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node }; 18939 const align_src: LazySrcLoc = .{ .node_offset_ptr_align = extra.data.src_node }; 18940 const addrspace_src: LazySrcLoc = .{ .node_offset_ptr_addrspace = extra.data.src_node }; 18941 const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node }; 18942 const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node }; 18943 18944 const elem_ty = blk: { 18945 const air_inst = try sema.resolveInst(extra.data.elem_type); 18946 const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| { 18947 if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(mod)) { 18948 try sema.errNote(block, elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{}); 18949 } 18950 return err; 18951 }; 18952 if (ty.isGenericPoison()) return error.GenericPoison; 18953 break :blk ty; 18954 }; 18955 18956 if (elem_ty.zigTypeTag(mod) == .NoReturn) 18957 return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); 18958 18959 const target = mod.getTarget(); 18960 18961 var extra_i = extra.end; 18962 18963 const sentinel = if (inst_data.flags.has_sentinel) blk: { 18964 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 18965 extra_i += 1; 18966 const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src); 18967 const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ 18968 .needed_comptime_reason = "pointer sentinel value must be comptime-known", 18969 }); 18970 break :blk val.toIntern(); 18971 } else .none; 18972 18973 const abi_align: Alignment = if (inst_data.flags.has_align) blk: { 18974 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 18975 extra_i += 1; 18976 const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src); 18977 const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ 18978 .needed_comptime_reason = "pointer alignment must be comptime-known", 18979 }); 18980 // Check if this happens to be the lazy alignment of our element type, in 18981 // which case we can make this 0 without resolving it. 18982 switch (mod.intern_pool.indexToKey(val.toIntern())) { 18983 .int => |int| switch (int.storage) { 18984 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none, 18985 else => {}, 18986 }, 18987 else => {}, 18988 } 18989 const align_bytes = (try val.getUnsignedIntAdvanced(mod, sema)).?; 18990 break :blk try sema.validateAlignAllowZero(block, align_src, align_bytes); 18991 } else .none; 18992 18993 const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: { 18994 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 18995 extra_i += 1; 18996 break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer); 18997 } else if (elem_ty.zigTypeTag(mod) == .Fn and target.cpu.arch == .avr) .flash else .generic; 18998 18999 const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: { 19000 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 19001 extra_i += 1; 19002 const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16, .{ 19003 .needed_comptime_reason = "pointer bit-offset must be comptime-known", 19004 }); 19005 break :blk @intCast(bit_offset); 19006 } else 0; 19007 19008 const host_size: u16 = if (inst_data.flags.has_bit_range) blk: { 19009 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); 19010 extra_i += 1; 19011 const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16, .{ 19012 .needed_comptime_reason = "pointer host size must be comptime-known", 19013 }); 19014 break :blk @intCast(host_size); 19015 } else 0; 19016 19017 if (host_size != 0 and bit_offset >= host_size * 8) { 19018 return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{}); 19019 } 19020 19021 if (elem_ty.zigTypeTag(mod) == .Fn) { 19022 if (inst_data.size != .One) { 19023 return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); 19024 } 19025 const fn_align = mod.typeToFunc(elem_ty).?.alignment; 19026 if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and 19027 abi_align != fn_align) 19028 { 19029 return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); 19030 } 19031 } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { 19032 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); 19033 } else if (inst_data.size == .C) { 19034 if (!try sema.validateExternType(elem_ty, .other)) { 19035 const msg = msg: { 19036 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)}); 19037 errdefer msg.destroy(sema.gpa); 19038 19039 const src_decl = mod.declPtr(block.src_decl); 19040 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other); 19041 19042 try sema.addDeclaredHereNote(msg, elem_ty); 19043 break :msg msg; 19044 }; 19045 return sema.failWithOwnedErrorMsg(block, msg); 19046 } 19047 if (elem_ty.zigTypeTag(mod) == .Opaque) { 19048 return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{}); 19049 } 19050 } 19051 19052 const ty = try sema.ptrType(.{ 19053 .child = elem_ty.toIntern(), 19054 .sentinel = sentinel, 19055 .flags = .{ 19056 .alignment = abi_align, 19057 .address_space = address_space, 19058 .is_const = !inst_data.flags.is_mutable, 19059 .is_allowzero = inst_data.flags.is_allowzero, 19060 .is_volatile = inst_data.flags.is_volatile, 19061 .size = inst_data.size, 19062 }, 19063 .packed_offset = .{ 19064 .bit_offset = bit_offset, 19065 .host_size = host_size, 19066 }, 19067 }); 19068 return Air.internedToRef(ty.toIntern()); 19069 } 19070 19071 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19072 const tracy = trace(@src()); 19073 defer tracy.end(); 19074 19075 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 19076 const src = inst_data.src(); 19077 const obj_ty = try sema.resolveType(block, src, inst_data.operand); 19078 const mod = sema.mod; 19079 19080 switch (obj_ty.zigTypeTag(mod)) { 19081 .Struct => return sema.structInitEmpty(block, obj_ty, src, src), 19082 .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty), 19083 .Void => return Air.internedToRef(Value.void.toIntern()), 19084 .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}), 19085 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), 19086 } 19087 } 19088 19089 fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref { 19090 const tracy = trace(@src()); 19091 defer tracy.end(); 19092 19093 const mod = sema.mod; 19094 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 19095 const src = inst_data.src(); 19096 const ty_operand = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) { 19097 // Generic poison means this is an untyped anonymous empty struct init 19098 error.GenericPoison => return .empty_struct, 19099 else => |e| return e, 19100 }; 19101 const init_ty = if (is_byref) ty: { 19102 const ptr_ty = ty_operand.optEuBaseType(mod); 19103 assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction 19104 if (!ptr_ty.isSlice(mod)) { 19105 break :ty ptr_ty.childType(mod); 19106 } 19107 // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`. 19108 break :ty try mod.arrayType(.{ 19109 .len = 0, 19110 .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none, 19111 .child = ptr_ty.childType(mod).toIntern(), 19112 }); 19113 } else ty_operand; 19114 const obj_ty = init_ty.optEuBaseType(mod); 19115 19116 const empty_ref = switch (obj_ty.zigTypeTag(mod)) { 19117 .Struct => try sema.structInitEmpty(block, obj_ty, src, src), 19118 .Array, .Vector => try sema.arrayInitEmpty(block, src, obj_ty), 19119 .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}), 19120 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), 19121 }; 19122 const init_ref = try sema.coerce(block, init_ty, empty_ref, src); 19123 19124 if (is_byref) { 19125 const init_val = (try sema.resolveValue(init_ref)).?; 19126 return anonDeclRef(sema, init_val.toIntern()); 19127 } else { 19128 return init_ref; 19129 } 19130 } 19131 19132 fn structInitEmpty( 19133 sema: *Sema, 19134 block: *Block, 19135 struct_ty: Type, 19136 dest_src: LazySrcLoc, 19137 init_src: LazySrcLoc, 19138 ) CompileError!Air.Inst.Ref { 19139 const mod = sema.mod; 19140 const gpa = sema.gpa; 19141 // This logic must be synchronized with that in `zirStructInit`. 19142 try sema.resolveTypeFields(struct_ty); 19143 19144 // The init values to use for the struct instance. 19145 const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(mod)); 19146 defer gpa.free(field_inits); 19147 @memset(field_inits, .none); 19148 19149 return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, struct_ty, false); 19150 } 19151 19152 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { 19153 const mod = sema.mod; 19154 const arr_len = obj_ty.arrayLen(mod); 19155 if (arr_len != 0) { 19156 if (obj_ty.zigTypeTag(mod) == .Array) { 19157 return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len}); 19158 } else { 19159 return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len}); 19160 } 19161 } 19162 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 19163 .ty = obj_ty.toIntern(), 19164 .storage = .{ .elems = &.{} }, 19165 } }))); 19166 } 19167 19168 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19169 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19170 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19171 const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 19172 const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 19173 const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; 19174 const union_ty = try sema.resolveType(block, ty_src, extra.union_type); 19175 if (union_ty.zigTypeTag(sema.mod) != .Union) { 19176 const msg = msg: { 19177 const msg = try sema.errMsg(block, ty_src, "expected union type, found '{}'", .{union_ty.fmt(sema.mod)}); 19178 errdefer msg.destroy(sema.gpa); 19179 try sema.addDeclaredHereNote(msg, union_ty); 19180 break :msg msg; 19181 }; 19182 return sema.failWithOwnedErrorMsg(block, msg); 19183 } 19184 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ 19185 .needed_comptime_reason = "name of field being initialized must be comptime-known", 19186 }); 19187 const init = try sema.resolveInst(extra.init); 19188 return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); 19189 } 19190 19191 fn unionInit( 19192 sema: *Sema, 19193 block: *Block, 19194 uncasted_init: Air.Inst.Ref, 19195 init_src: LazySrcLoc, 19196 union_ty: Type, 19197 union_ty_src: LazySrcLoc, 19198 field_name: InternPool.NullTerminatedString, 19199 field_src: LazySrcLoc, 19200 ) CompileError!Air.Inst.Ref { 19201 const mod = sema.mod; 19202 const ip = &mod.intern_pool; 19203 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); 19204 const field_ty = Type.fromInterned(mod.typeToUnion(union_ty).?.field_types.get(ip)[field_index]); 19205 const init = try sema.coerce(block, field_ty, uncasted_init, init_src); 19206 19207 if (try sema.resolveValue(init)) |init_val| { 19208 const tag_ty = union_ty.unionTagTypeHypothetical(mod); 19209 const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); 19210 return Air.internedToRef((try mod.intern(.{ .un = .{ 19211 .ty = union_ty.toIntern(), 19212 .tag = try tag_val.intern(tag_ty, mod), 19213 .val = try init_val.intern(field_ty, mod), 19214 } }))); 19215 } 19216 19217 try sema.requireRuntimeBlock(block, init_src, null); 19218 _ = union_ty_src; 19219 try sema.queueFullTypeResolution(union_ty); 19220 return block.addUnionInit(union_ty, field_index, init); 19221 } 19222 19223 fn zirStructInit( 19224 sema: *Sema, 19225 block: *Block, 19226 inst: Zir.Inst.Index, 19227 is_ref: bool, 19228 ) CompileError!Air.Inst.Ref { 19229 const gpa = sema.gpa; 19230 const zir_datas = sema.code.instructions.items(.data); 19231 const inst_data = zir_datas[@intFromEnum(inst)].pl_node; 19232 const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); 19233 const src = inst_data.src(); 19234 19235 const mod = sema.mod; 19236 const ip = &mod.intern_pool; 19237 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; 19238 const first_field_type_data = zir_datas[@intFromEnum(first_item.field_type)].pl_node; 19239 const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; 19240 const result_ty = sema.resolveType(block, src, first_field_type_extra.container_type) catch |err| switch (err) { 19241 error.GenericPoison => { 19242 // The type wasn't actually known, so treat this as an anon struct init. 19243 return sema.structInitAnon(block, src, .typed_init, extra.data, extra.end, is_ref); 19244 }, 19245 else => |e| return e, 19246 }; 19247 const resolved_ty = result_ty.optEuBaseType(mod); 19248 try sema.resolveTypeLayout(resolved_ty); 19249 19250 if (resolved_ty.zigTypeTag(mod) == .Struct) { 19251 // This logic must be synchronized with that in `zirStructInitEmpty`. 19252 19253 // Maps field index to field_type index of where it was already initialized. 19254 // For making sure all fields are accounted for and no fields are duplicated. 19255 const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(mod)); 19256 defer gpa.free(found_fields); 19257 19258 // The init values to use for the struct instance. 19259 const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(mod)); 19260 defer gpa.free(field_inits); 19261 @memset(field_inits, .none); 19262 19263 var field_i: u32 = 0; 19264 var extra_index = extra.end; 19265 19266 const is_packed = resolved_ty.containerLayout(mod) == .Packed; 19267 while (field_i < extra.data.fields_len) : (field_i += 1) { 19268 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); 19269 extra_index = item.end; 19270 19271 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19272 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; 19273 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 19274 const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); 19275 const field_index = if (resolved_ty.isTuple(mod)) 19276 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) 19277 else 19278 try sema.structFieldIndex(block, resolved_ty, field_name, field_src); 19279 assert(field_inits[field_index] == .none); 19280 found_fields[field_index] = item.data.field_type; 19281 const uncoerced_init = try sema.resolveInst(item.data.init); 19282 const field_ty = resolved_ty.structFieldType(field_index, mod); 19283 field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src); 19284 if (!is_packed) { 19285 try sema.resolveStructFieldInits(resolved_ty); 19286 if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| { 19287 const init_val = (try sema.resolveValue(field_inits[field_index])) orelse { 19288 return sema.failWithNeededComptime(block, field_src, .{ 19289 .needed_comptime_reason = "value stored in comptime field must be comptime-known", 19290 }); 19291 }; 19292 19293 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) { 19294 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); 19295 } 19296 } 19297 } 19298 } 19299 19300 return sema.finishStructInit(block, src, src, field_inits, resolved_ty, result_ty, is_ref); 19301 } else if (resolved_ty.zigTypeTag(mod) == .Union) { 19302 if (extra.data.fields_len != 1) { 19303 return sema.fail(block, src, "union initialization expects exactly one field", .{}); 19304 } 19305 19306 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end); 19307 19308 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19309 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; 19310 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 19311 const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); 19312 const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); 19313 const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); 19314 const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); 19315 const field_ty = Type.fromInterned(mod.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]); 19316 19317 if (field_ty.zigTypeTag(mod) == .NoReturn) { 19318 return sema.failWithOwnedErrorMsg(block, msg: { 19319 const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{}); 19320 errdefer msg.destroy(sema.gpa); 19321 19322 try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{}' declared here", .{ 19323 field_name.fmt(ip), 19324 }); 19325 try sema.addDeclaredHereNote(msg, resolved_ty); 19326 break :msg msg; 19327 }); 19328 } 19329 19330 const uncoerced_init_inst = try sema.resolveInst(item.data.init); 19331 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src); 19332 19333 if (try sema.resolveValue(init_inst)) |val| { 19334 const struct_val = Value.fromInterned((try mod.intern(.{ .un = .{ 19335 .ty = resolved_ty.toIntern(), 19336 .tag = try tag_val.intern(tag_ty, mod), 19337 .val = try val.intern(field_ty, mod), 19338 } }))); 19339 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src); 19340 const final_val = (try sema.resolveValue(final_val_inst)).?; 19341 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref); 19342 } 19343 19344 if (try sema.typeRequiresComptime(resolved_ty)) { 19345 return sema.failWithNeededComptime(block, field_src, .{ 19346 .needed_comptime_reason = "initializer of comptime only union must be comptime-known", 19347 }); 19348 } 19349 19350 if (is_ref) { 19351 const target = mod.getTarget(); 19352 const alloc_ty = try sema.ptrType(.{ 19353 .child = result_ty.toIntern(), 19354 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19355 }); 19356 const alloc = try block.addTy(.alloc, alloc_ty); 19357 const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); 19358 const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true); 19359 try sema.storePtr(block, src, field_ptr, init_inst); 19360 const new_tag = Air.internedToRef(tag_val.toIntern()); 19361 _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag); 19362 return sema.makePtrConst(block, alloc); 19363 } 19364 19365 try sema.requireRuntimeBlock(block, src, null); 19366 try sema.queueFullTypeResolution(resolved_ty); 19367 const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst); 19368 return sema.coerce(block, result_ty, union_val, src); 19369 } 19370 unreachable; 19371 } 19372 19373 fn finishStructInit( 19374 sema: *Sema, 19375 block: *Block, 19376 init_src: LazySrcLoc, 19377 dest_src: LazySrcLoc, 19378 field_inits: []Air.Inst.Ref, 19379 struct_ty: Type, 19380 result_ty: Type, 19381 is_ref: bool, 19382 ) CompileError!Air.Inst.Ref { 19383 const mod = sema.mod; 19384 const ip = &mod.intern_pool; 19385 19386 var root_msg: ?*Module.ErrorMsg = null; 19387 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 19388 19389 switch (ip.indexToKey(struct_ty.toIntern())) { 19390 .anon_struct_type => |anon_struct| { 19391 // We can't get the slices, as the coercion may invalidate them. 19392 for (0..anon_struct.types.len) |i| { 19393 if (field_inits[i] != .none) { 19394 // Coerce the init value to the field type. 19395 const field_ty = Type.fromInterned(anon_struct.types.get(ip)[i]); 19396 field_inits[i] = sema.coerce(block, field_ty, field_inits[i], .unneeded) catch |err| switch (err) { 19397 error.NeededSourceLocation => { 19398 const decl = mod.declPtr(block.src_decl); 19399 const field_src = mod.initSrc(init_src.node_offset.x, decl, i); 19400 _ = try sema.coerce(block, field_ty, field_inits[i], field_src); 19401 unreachable; 19402 }, 19403 else => |e| return e, 19404 }; 19405 continue; 19406 } 19407 19408 const default_val = anon_struct.values.get(ip)[i]; 19409 19410 if (default_val == .none) { 19411 if (anon_struct.names.len == 0) { 19412 const template = "missing tuple field with index {d}"; 19413 if (root_msg) |msg| { 19414 try sema.errNote(block, init_src, msg, template, .{i}); 19415 } else { 19416 root_msg = try sema.errMsg(block, init_src, template, .{i}); 19417 } 19418 } else { 19419 const field_name = anon_struct.names.get(ip)[i]; 19420 const template = "missing struct field: {}"; 19421 const args = .{field_name.fmt(ip)}; 19422 if (root_msg) |msg| { 19423 try sema.errNote(block, init_src, msg, template, args); 19424 } else { 19425 root_msg = try sema.errMsg(block, init_src, template, args); 19426 } 19427 } 19428 } else { 19429 field_inits[i] = Air.internedToRef(default_val); 19430 } 19431 } 19432 }, 19433 .struct_type => |struct_type| { 19434 for (0..struct_type.field_types.len) |i| { 19435 if (field_inits[i] != .none) { 19436 // Coerce the init value to the field type. 19437 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 19438 field_inits[i] = sema.coerce(block, field_ty, field_inits[i], init_src) catch |err| switch (err) { 19439 error.NeededSourceLocation => { 19440 const decl = mod.declPtr(block.src_decl); 19441 const field_src = mod.initSrc(init_src.node_offset.x, decl, i); 19442 _ = try sema.coerce(block, field_ty, field_inits[i], field_src); 19443 unreachable; 19444 }, 19445 else => |e| return e, 19446 }; 19447 continue; 19448 } 19449 19450 try sema.resolveStructFieldInits(struct_ty); 19451 19452 const field_init = struct_type.fieldInit(ip, i); 19453 if (field_init == .none) { 19454 const field_name = struct_type.field_names.get(ip)[i]; 19455 const template = "missing struct field: {}"; 19456 const args = .{field_name.fmt(ip)}; 19457 if (root_msg) |msg| { 19458 try sema.errNote(block, init_src, msg, template, args); 19459 } else { 19460 root_msg = try sema.errMsg(block, init_src, template, args); 19461 } 19462 } else { 19463 field_inits[i] = Air.internedToRef(field_init); 19464 } 19465 } 19466 }, 19467 else => unreachable, 19468 } 19469 19470 if (root_msg) |msg| { 19471 if (mod.typeToStruct(struct_ty)) |struct_type| { 19472 const decl = mod.declPtr(struct_type.decl.unwrap().?); 19473 const fqn = try decl.getFullyQualifiedName(mod); 19474 try mod.errNoteNonLazy( 19475 decl.srcLoc(mod), 19476 msg, 19477 "struct '{}' declared here", 19478 .{fqn.fmt(ip)}, 19479 ); 19480 } 19481 root_msg = null; 19482 return sema.failWithOwnedErrorMsg(block, msg); 19483 } 19484 19485 // Find which field forces the expression to be runtime, if any. 19486 const opt_runtime_index = for (field_inits, 0..) |field_init, i| { 19487 if (!(try sema.isComptimeKnown(field_init))) { 19488 break i; 19489 } 19490 } else null; 19491 19492 const runtime_index = opt_runtime_index orelse { 19493 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); 19494 for (elems, field_inits) |*elem, field_init| { 19495 elem.* = (sema.resolveValue(field_init) catch unreachable).?.toIntern(); 19496 } 19497 const struct_val = try mod.intern(.{ .aggregate = .{ 19498 .ty = struct_ty.toIntern(), 19499 .storage = .{ .elems = elems }, 19500 } }); 19501 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val), init_src); 19502 const final_val = (try sema.resolveValue(final_val_inst)).?; 19503 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref); 19504 }; 19505 19506 if (try sema.typeRequiresComptime(struct_ty)) { 19507 const decl = mod.declPtr(block.src_decl); 19508 const field_src = mod.initSrc(init_src.node_offset.x, decl, runtime_index); 19509 return sema.failWithNeededComptime(block, field_src, .{ 19510 .needed_comptime_reason = "initializer of comptime only struct must be comptime-known", 19511 }); 19512 } 19513 19514 if (is_ref) { 19515 try sema.resolveStructLayout(struct_ty); 19516 const target = sema.mod.getTarget(); 19517 const alloc_ty = try sema.ptrType(.{ 19518 .child = result_ty.toIntern(), 19519 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19520 }); 19521 const alloc = try block.addTy(.alloc, alloc_ty); 19522 const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src); 19523 for (field_inits, 0..) |field_init, i_usize| { 19524 const i: u32 = @intCast(i_usize); 19525 const field_src = dest_src; 19526 const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, field_src, struct_ty, true); 19527 try sema.storePtr(block, dest_src, field_ptr, field_init); 19528 } 19529 19530 return sema.makePtrConst(block, alloc); 19531 } 19532 19533 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 19534 error.NeededSourceLocation => { 19535 const decl = mod.declPtr(block.src_decl); 19536 const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index); 19537 try sema.requireRuntimeBlock(block, dest_src, field_src); 19538 unreachable; 19539 }, 19540 else => |e| return e, 19541 }; 19542 try sema.queueFullTypeResolution(struct_ty); 19543 const struct_val = try block.addAggregateInit(struct_ty, field_inits); 19544 return sema.coerce(block, result_ty, struct_val, init_src); 19545 } 19546 19547 fn zirStructInitAnon( 19548 sema: *Sema, 19549 block: *Block, 19550 inst: Zir.Inst.Index, 19551 ) CompileError!Air.Inst.Ref { 19552 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19553 const src = inst_data.src(); 19554 const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); 19555 return sema.structInitAnon(block, src, .anon_init, extra.data, extra.end, false); 19556 } 19557 19558 fn structInitAnon( 19559 sema: *Sema, 19560 block: *Block, 19561 src: LazySrcLoc, 19562 /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a 19563 /// generic poison type. In this case, we need to know to interpret the extra data differently. 19564 comptime kind: enum { anon_init, typed_init }, 19565 extra_data: switch (kind) { 19566 .anon_init => Zir.Inst.StructInitAnon, 19567 .typed_init => Zir.Inst.StructInit, 19568 }, 19569 extra_end: usize, 19570 is_ref: bool, 19571 ) CompileError!Air.Inst.Ref { 19572 const mod = sema.mod; 19573 const gpa = sema.gpa; 19574 const ip = &mod.intern_pool; 19575 const zir_datas = sema.code.instructions.items(.data); 19576 19577 const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len); 19578 const values = try sema.arena.alloc(InternPool.Index, types.len); 19579 const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len); 19580 19581 // Find which field forces the expression to be runtime, if any. 19582 const opt_runtime_index = rs: { 19583 var runtime_index: ?usize = null; 19584 var extra_index = extra_end; 19585 for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| { 19586 const item = switch (kind) { 19587 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19588 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19589 }; 19590 extra_index = item.end; 19591 19592 const name = switch (kind) { 19593 .anon_init => sema.code.nullTerminatedString(item.data.field_name), 19594 .typed_init => name: { 19595 // `item.data.field_type` references a `field_type` instruction 19596 const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; 19597 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index); 19598 break :name sema.code.nullTerminatedString(field_type_extra.data.name_start); 19599 }, 19600 }; 19601 19602 const name_ip = try mod.intern_pool.getOrPutString(gpa, name); 19603 field_name.* = name_ip; 19604 19605 const init = try sema.resolveInst(item.data.init); 19606 field_ty.* = sema.typeOf(init).toIntern(); 19607 if (Type.fromInterned(field_ty.*).zigTypeTag(mod) == .Opaque) { 19608 const msg = msg: { 19609 const decl = mod.declPtr(block.src_decl); 19610 const field_src = mod.initSrc(src.node_offset.x, decl, @intCast(i_usize)); 19611 const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19612 errdefer msg.destroy(sema.gpa); 19613 19614 try sema.addDeclaredHereNote(msg, Type.fromInterned(field_ty.*)); 19615 break :msg msg; 19616 }; 19617 return sema.failWithOwnedErrorMsg(block, msg); 19618 } 19619 if (try sema.resolveValue(init)) |init_val| { 19620 field_val.* = try init_val.intern(Type.fromInterned(field_ty.*), mod); 19621 } else { 19622 field_val.* = .none; 19623 runtime_index = @intCast(i_usize); 19624 } 19625 } 19626 break :rs runtime_index; 19627 }; 19628 19629 const tuple_ty = try ip.getAnonStructType(gpa, .{ 19630 .names = names, 19631 .types = types, 19632 .values = values, 19633 }); 19634 19635 const runtime_index = opt_runtime_index orelse { 19636 const tuple_val = try mod.intern(.{ .aggregate = .{ 19637 .ty = tuple_ty, 19638 .storage = .{ .elems = values }, 19639 } }); 19640 return sema.addConstantMaybeRef(tuple_val, is_ref); 19641 }; 19642 19643 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 19644 error.NeededSourceLocation => { 19645 const decl = mod.declPtr(block.src_decl); 19646 const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index); 19647 try sema.requireRuntimeBlock(block, src, field_src); 19648 unreachable; 19649 }, 19650 else => |e| return e, 19651 }; 19652 19653 if (is_ref) { 19654 const target = mod.getTarget(); 19655 const alloc_ty = try sema.ptrType(.{ 19656 .child = tuple_ty, 19657 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19658 }); 19659 const alloc = try block.addTy(.alloc, alloc_ty); 19660 var extra_index = extra_end; 19661 for (types, 0..) |field_ty, i_usize| { 19662 const i: u32 = @intCast(i_usize); 19663 const item = switch (kind) { 19664 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19665 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19666 }; 19667 extra_index = item.end; 19668 19669 const field_ptr_ty = try sema.ptrType(.{ 19670 .child = field_ty, 19671 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19672 }); 19673 if (values[i] == .none) { 19674 const init = try sema.resolveInst(item.data.init); 19675 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19676 _ = try block.addBinOp(.store, field_ptr, init); 19677 } 19678 } 19679 19680 return sema.makePtrConst(block, alloc); 19681 } 19682 19683 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); 19684 var extra_index = extra_end; 19685 for (types, 0..) |_, i| { 19686 const item = switch (kind) { 19687 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), 19688 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), 19689 }; 19690 extra_index = item.end; 19691 element_refs[i] = try sema.resolveInst(item.data.init); 19692 } 19693 19694 return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs); 19695 } 19696 19697 fn zirArrayInit( 19698 sema: *Sema, 19699 block: *Block, 19700 inst: Zir.Inst.Index, 19701 is_ref: bool, 19702 ) CompileError!Air.Inst.Ref { 19703 const mod = sema.mod; 19704 const gpa = sema.gpa; 19705 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19706 const src = inst_data.src(); 19707 19708 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19709 const args = sema.code.refSlice(extra.end, extra.data.operands_len); 19710 assert(args.len >= 2); // array_ty + at least one element 19711 19712 const result_ty = sema.resolveType(block, src, args[0]) catch |err| switch (err) { 19713 error.GenericPoison => { 19714 // The type wasn't actually known, so treat this as an anon array init. 19715 return sema.arrayInitAnon(block, src, args[1..], is_ref); 19716 }, 19717 else => |e| return e, 19718 }; 19719 const array_ty = result_ty.optEuBaseType(mod); 19720 const is_tuple = array_ty.zigTypeTag(mod) == .Struct; 19721 const sentinel_val = array_ty.sentinel(mod); 19722 19723 var root_msg: ?*Module.ErrorMsg = null; 19724 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 19725 19726 const final_len = try sema.usizeCast(block, src, array_ty.arrayLenIncludingSentinel(mod)); 19727 const resolved_args = try gpa.alloc(Air.Inst.Ref, final_len); 19728 defer gpa.free(resolved_args); 19729 for (resolved_args, 0..) |*dest, i| { 19730 // Less inits than needed. 19731 if (i + 2 > args.len) if (is_tuple) { 19732 const default_val = array_ty.structFieldDefaultValue(i, mod).toIntern(); 19733 if (default_val == .unreachable_value) { 19734 const template = "missing tuple field with index {d}"; 19735 if (root_msg) |msg| { 19736 try sema.errNote(block, src, msg, template, .{i}); 19737 } else { 19738 root_msg = try sema.errMsg(block, src, template, .{i}); 19739 } 19740 } else { 19741 dest.* = Air.internedToRef(default_val); 19742 } 19743 continue; 19744 } else { 19745 dest.* = Air.internedToRef(sentinel_val.?.toIntern()); 19746 break; 19747 }; 19748 19749 const arg = args[i + 1]; 19750 const resolved_arg = try sema.resolveInst(arg); 19751 const elem_ty = if (is_tuple) 19752 array_ty.structFieldType(i, mod) 19753 else 19754 array_ty.elemType2(mod); 19755 dest.* = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) { 19756 error.NeededSourceLocation => { 19757 const decl = mod.declPtr(block.src_decl); 19758 const elem_src = mod.initSrc(src.node_offset.x, decl, i); 19759 _ = try sema.coerce(block, elem_ty, resolved_arg, elem_src); 19760 unreachable; 19761 }, 19762 else => return err, 19763 }; 19764 if (is_tuple) { 19765 if (array_ty.structFieldIsComptime(i, mod)) 19766 try sema.resolveStructFieldInits(array_ty); 19767 if (try array_ty.structFieldValueComptime(mod, i)) |field_val| { 19768 const init_val = try sema.resolveValue(dest.*) orelse { 19769 const decl = mod.declPtr(block.src_decl); 19770 const elem_src = mod.initSrc(src.node_offset.x, decl, i); 19771 return sema.failWithNeededComptime(block, elem_src, .{ 19772 .needed_comptime_reason = "value stored in comptime field must be comptime-known", 19773 }); 19774 }; 19775 if (!field_val.eql(init_val, elem_ty, mod)) { 19776 const decl = mod.declPtr(block.src_decl); 19777 const elem_src = mod.initSrc(src.node_offset.x, decl, i); 19778 return sema.failWithInvalidComptimeFieldStore(block, elem_src, array_ty, i); 19779 } 19780 } 19781 } 19782 } 19783 19784 if (root_msg) |msg| { 19785 try sema.addDeclaredHereNote(msg, array_ty); 19786 root_msg = null; 19787 return sema.failWithOwnedErrorMsg(block, msg); 19788 } 19789 19790 const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| { 19791 const comptime_known = try sema.isComptimeKnown(arg); 19792 if (!comptime_known) break @intCast(i); 19793 } else null; 19794 19795 const runtime_index = opt_runtime_index orelse { 19796 const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len); 19797 for (elem_vals, resolved_args, 0..) |*val, arg, i| { 19798 const elem_ty = if (is_tuple) 19799 array_ty.structFieldType(i, mod) 19800 else 19801 array_ty.elemType2(mod); 19802 // We checked that all args are comptime above. 19803 val.* = try ((sema.resolveValue(arg) catch unreachable).?).intern(elem_ty, mod); 19804 } 19805 const arr_val = try mod.intern(.{ .aggregate = .{ 19806 .ty = array_ty.toIntern(), 19807 .storage = .{ .elems = elem_vals }, 19808 } }); 19809 const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val), src); 19810 const result_val = (try sema.resolveValue(result_ref)).?; 19811 return sema.addConstantMaybeRef(result_val.toIntern(), is_ref); 19812 }; 19813 19814 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 19815 error.NeededSourceLocation => { 19816 const decl = mod.declPtr(block.src_decl); 19817 const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index); 19818 try sema.requireRuntimeBlock(block, src, elem_src); 19819 unreachable; 19820 }, 19821 else => return err, 19822 }; 19823 try sema.queueFullTypeResolution(array_ty); 19824 19825 if (is_ref) { 19826 const target = mod.getTarget(); 19827 const alloc_ty = try sema.ptrType(.{ 19828 .child = result_ty.toIntern(), 19829 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19830 }); 19831 const alloc = try block.addTy(.alloc, alloc_ty); 19832 const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); 19833 19834 if (is_tuple) { 19835 for (resolved_args, 0..) |arg, i| { 19836 const elem_ptr_ty = try sema.ptrType(.{ 19837 .child = array_ty.structFieldType(i, mod).toIntern(), 19838 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19839 }); 19840 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 19841 19842 const index = try mod.intRef(Type.usize, i); 19843 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref); 19844 _ = try block.addBinOp(.store, elem_ptr, arg); 19845 } 19846 return sema.makePtrConst(block, alloc); 19847 } 19848 19849 const elem_ptr_ty = try sema.ptrType(.{ 19850 .child = array_ty.elemType2(mod).toIntern(), 19851 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19852 }); 19853 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); 19854 19855 for (resolved_args, 0..) |arg, i| { 19856 const index = try mod.intRef(Type.usize, i); 19857 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref); 19858 _ = try block.addBinOp(.store, elem_ptr, arg); 19859 } 19860 return sema.makePtrConst(block, alloc); 19861 } 19862 19863 const arr_ref = try block.addAggregateInit(array_ty, resolved_args); 19864 return sema.coerce(block, result_ty, arr_ref, src); 19865 } 19866 19867 fn zirArrayInitAnon( 19868 sema: *Sema, 19869 block: *Block, 19870 inst: Zir.Inst.Index, 19871 ) CompileError!Air.Inst.Ref { 19872 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19873 const src = inst_data.src(); 19874 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19875 const operands = sema.code.refSlice(extra.end, extra.data.operands_len); 19876 return sema.arrayInitAnon(block, src, operands, false); 19877 } 19878 19879 fn arrayInitAnon( 19880 sema: *Sema, 19881 block: *Block, 19882 src: LazySrcLoc, 19883 operands: []const Zir.Inst.Ref, 19884 is_ref: bool, 19885 ) CompileError!Air.Inst.Ref { 19886 const mod = sema.mod; 19887 const gpa = sema.gpa; 19888 const ip = &mod.intern_pool; 19889 19890 const types = try sema.arena.alloc(InternPool.Index, operands.len); 19891 const values = try sema.arena.alloc(InternPool.Index, operands.len); 19892 19893 const opt_runtime_src = rs: { 19894 var runtime_src: ?LazySrcLoc = null; 19895 for (operands, 0..) |operand, i| { 19896 const operand_src = src; // TODO better source location 19897 const elem = try sema.resolveInst(operand); 19898 types[i] = sema.typeOf(elem).toIntern(); 19899 if (Type.fromInterned(types[i]).zigTypeTag(mod) == .Opaque) { 19900 const msg = msg: { 19901 const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19902 errdefer msg.destroy(gpa); 19903 19904 try sema.addDeclaredHereNote(msg, Type.fromInterned(types[i])); 19905 break :msg msg; 19906 }; 19907 return sema.failWithOwnedErrorMsg(block, msg); 19908 } 19909 if (try sema.resolveValue(elem)) |val| { 19910 values[i] = val.toIntern(); 19911 } else { 19912 values[i] = .none; 19913 runtime_src = operand_src; 19914 } 19915 } 19916 break :rs runtime_src; 19917 }; 19918 19919 const tuple_ty = try ip.getAnonStructType(gpa, .{ 19920 .types = types, 19921 .values = values, 19922 .names = &.{}, 19923 }); 19924 19925 const runtime_src = opt_runtime_src orelse { 19926 const tuple_val = try mod.intern(.{ .aggregate = .{ 19927 .ty = tuple_ty, 19928 .storage = .{ .elems = values }, 19929 } }); 19930 return sema.addConstantMaybeRef(tuple_val, is_ref); 19931 }; 19932 19933 try sema.requireRuntimeBlock(block, src, runtime_src); 19934 19935 if (is_ref) { 19936 const target = sema.mod.getTarget(); 19937 const alloc_ty = try sema.ptrType(.{ 19938 .child = tuple_ty, 19939 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19940 }); 19941 const alloc = try block.addTy(.alloc, alloc_ty); 19942 for (operands, 0..) |operand, i_usize| { 19943 const i: u32 = @intCast(i_usize); 19944 const field_ptr_ty = try sema.ptrType(.{ 19945 .child = types[i], 19946 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19947 }); 19948 if (values[i] == .none) { 19949 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19950 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); 19951 } 19952 } 19953 19954 return sema.makePtrConst(block, alloc); 19955 } 19956 19957 const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 19958 for (operands, 0..) |operand, i| { 19959 element_refs[i] = try sema.resolveInst(operand); 19960 } 19961 19962 return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs); 19963 } 19964 19965 fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref { 19966 return if (is_ref) anonDeclRef(sema, val) else Air.internedToRef(val); 19967 } 19968 19969 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19970 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19971 const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data; 19972 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19973 const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 19974 const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); 19975 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ 19976 .needed_comptime_reason = "field name must be comptime-known", 19977 }); 19978 return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); 19979 } 19980 19981 fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19982 const mod = sema.mod; 19983 const ip = &mod.intern_pool; 19984 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 19985 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; 19986 const ty_src = inst_data.src(); 19987 const field_name_src: LazySrcLoc = .{ .node_offset_field_name_init = inst_data.src_node }; 19988 const wrapped_aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) { 19989 // Since this is a ZIR instruction that returns a type, encountering 19990 // generic poison should not result in a failed compilation, but the 19991 // generic poison type. This prevents unnecessary failures when 19992 // constructing types at compile-time. 19993 error.GenericPoison => return .generic_poison_type, 19994 else => |e| return e, 19995 }; 19996 const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(mod); 19997 const zir_field_name = sema.code.nullTerminatedString(extra.name_start); 19998 const field_name = try ip.getOrPutString(sema.gpa, zir_field_name); 19999 return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); 20000 } 20001 20002 fn fieldType( 20003 sema: *Sema, 20004 block: *Block, 20005 aggregate_ty: Type, 20006 field_name: InternPool.NullTerminatedString, 20007 field_src: LazySrcLoc, 20008 ty_src: LazySrcLoc, 20009 ) CompileError!Air.Inst.Ref { 20010 const mod = sema.mod; 20011 const ip = &mod.intern_pool; 20012 var cur_ty = aggregate_ty; 20013 while (true) { 20014 try sema.resolveTypeFields(cur_ty); 20015 switch (cur_ty.zigTypeTag(mod)) { 20016 .Struct => switch (ip.indexToKey(cur_ty.toIntern())) { 20017 .anon_struct_type => |anon_struct| { 20018 const field_index = if (anon_struct.names.len == 0) 20019 try sema.tupleFieldIndex(block, cur_ty, field_name, field_src) 20020 else 20021 try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); 20022 return Air.internedToRef(anon_struct.types.get(ip)[field_index]); 20023 }, 20024 .struct_type => |struct_type| { 20025 const field_index = struct_type.nameIndex(ip, field_name) orelse 20026 return sema.failWithBadStructFieldAccess(block, struct_type, field_src, field_name); 20027 const field_ty = struct_type.field_types.get(ip)[field_index]; 20028 return Air.internedToRef(field_ty); 20029 }, 20030 else => unreachable, 20031 }, 20032 .Union => { 20033 const union_obj = mod.typeToUnion(cur_ty).?; 20034 const field_index = union_obj.nameIndex(ip, field_name) orelse 20035 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); 20036 const field_ty = union_obj.field_types.get(ip)[field_index]; 20037 return Air.internedToRef(field_ty); 20038 }, 20039 .Optional => { 20040 // Struct/array init through optional requires the child type to not be a pointer. 20041 // If the child of .optional is a pointer it'll error on the next loop. 20042 cur_ty = Type.fromInterned(ip.indexToKey(cur_ty.toIntern()).opt_type); 20043 continue; 20044 }, 20045 .ErrorUnion => { 20046 cur_ty = cur_ty.errorUnionPayload(mod); 20047 continue; 20048 }, 20049 else => {}, 20050 } 20051 return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{ 20052 cur_ty.fmt(sema.mod), 20053 }); 20054 } 20055 } 20056 20057 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 20058 return sema.getErrorReturnTrace(block); 20059 } 20060 20061 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 20062 const mod = sema.mod; 20063 const ip = &mod.intern_pool; 20064 const stack_trace_ty = try sema.getBuiltinType("StackTrace"); 20065 try sema.resolveTypeFields(stack_trace_ty); 20066 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 20067 const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern()); 20068 20069 if (sema.owner_func_index != .none and 20070 ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn and 20071 block.ownerModule().error_tracing) 20072 { 20073 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); 20074 } 20075 return Air.internedToRef((try mod.intern(.{ .opt = .{ 20076 .ty = opt_ptr_stack_trace_ty.toIntern(), 20077 .val = .none, 20078 } }))); 20079 } 20080 20081 fn zirFrame( 20082 sema: *Sema, 20083 block: *Block, 20084 extended: Zir.Inst.Extended.InstData, 20085 ) CompileError!Air.Inst.Ref { 20086 const src = LazySrcLoc.nodeOffset(@bitCast(extended.operand)); 20087 return sema.failWithUseOfAsync(block, src); 20088 } 20089 20090 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20091 const mod = sema.mod; 20092 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20093 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20094 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 20095 if (ty.isNoReturn(mod)) { 20096 return sema.fail(block, operand_src, "no align available for type '{}'", .{ty.fmt(sema.mod)}); 20097 } 20098 const val = try ty.lazyAbiAlignment(mod); 20099 if (val.isLazyAlign(mod)) { 20100 try sema.queueFullTypeResolution(ty); 20101 } 20102 return Air.internedToRef(val.toIntern()); 20103 } 20104 20105 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20106 const mod = sema.mod; 20107 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20108 const src = inst_data.src(); 20109 const operand = try sema.resolveInst(inst_data.operand); 20110 const operand_ty = sema.typeOf(operand); 20111 const is_vector = operand_ty.zigTypeTag(mod) == .Vector; 20112 const operand_scalar_ty = operand_ty.scalarType(mod); 20113 if (operand_scalar_ty.toIntern() != .bool_type) { 20114 return sema.fail(block, src, "expected 'bool', found '{}'", .{operand_scalar_ty.zigTypeTag(mod)}); 20115 } 20116 if (try sema.resolveValue(operand)) |val| { 20117 if (!is_vector) { 20118 if (val.isUndef(mod)) return mod.undefRef(Type.u1); 20119 if (val.toBool()) return Air.internedToRef((try mod.intValue(Type.u1, 1)).toIntern()); 20120 return Air.internedToRef((try mod.intValue(Type.u1, 0)).toIntern()); 20121 } 20122 const len = operand_ty.vectorLen(mod); 20123 const dest_ty = try mod.vectorType(.{ .child = .u1_type, .len = len }); 20124 if (val.isUndef(mod)) return mod.undefRef(dest_ty); 20125 const new_elems = try sema.arena.alloc(InternPool.Index, len); 20126 for (new_elems, 0..) |*new_elem, i| { 20127 const old_elem = try val.elemValue(mod, i); 20128 const new_val = if (old_elem.isUndef(mod)) 20129 try mod.undefValue(Type.u1) 20130 else if (old_elem.toBool()) 20131 try mod.intValue(Type.u1, 1) 20132 else 20133 try mod.intValue(Type.u1, 0); 20134 new_elem.* = new_val.toIntern(); 20135 } 20136 return Air.internedToRef(try mod.intern(.{ .aggregate = .{ 20137 .ty = dest_ty.toIntern(), 20138 .storage = .{ .elems = new_elems }, 20139 } })); 20140 } 20141 if (!is_vector) { 20142 return block.addUnOp(.int_from_bool, operand); 20143 } 20144 const len = operand_ty.vectorLen(mod); 20145 const dest_ty = try mod.vectorType(.{ .child = .u1_type, .len = len }); 20146 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len); 20147 for (new_elems, 0..) |*new_elem, i| { 20148 const idx_ref = try mod.intRef(Type.usize, i); 20149 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref); 20150 new_elem.* = try block.addUnOp(.int_from_bool, old_elem); 20151 } 20152 return block.addAggregateInit(dest_ty, new_elems); 20153 } 20154 20155 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20156 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20157 const operand = try sema.resolveInst(inst_data.operand); 20158 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20159 20160 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 20161 const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name; 20162 return sema.addStrLit(sema.mod.intern_pool.stringToSlice(err_name)); 20163 } 20164 20165 // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass 20166 // might be able to resolve the result at compile time. 20167 return block.addUnOp(.error_name, operand); 20168 } 20169 20170 fn zirAbs( 20171 sema: *Sema, 20172 block: *Block, 20173 inst: Zir.Inst.Index, 20174 ) CompileError!Air.Inst.Ref { 20175 const mod = sema.mod; 20176 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20177 const operand = try sema.resolveInst(inst_data.operand); 20178 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20179 const operand_ty = sema.typeOf(operand); 20180 const scalar_ty = operand_ty.scalarType(mod); 20181 20182 const result_ty = switch (scalar_ty.zigTypeTag(mod)) { 20183 .ComptimeFloat, .Float, .ComptimeInt => operand_ty, 20184 .Int => if (scalar_ty.isSignedInt(mod)) try operand_ty.toUnsigned(mod) else return operand, 20185 else => return sema.fail( 20186 block, 20187 operand_src, 20188 "expected integer, float, or vector of either integers or floats, found '{}'", 20189 .{operand_ty.fmt(mod)}, 20190 ), 20191 }; 20192 20193 return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse { 20194 try sema.requireRuntimeBlock(block, operand_src, null); 20195 return block.addTyOp(.abs, result_ty, operand); 20196 }; 20197 } 20198 20199 fn maybeConstantUnaryMath( 20200 sema: *Sema, 20201 operand: Air.Inst.Ref, 20202 result_ty: Type, 20203 comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value, 20204 ) CompileError!?Air.Inst.Ref { 20205 const mod = sema.mod; 20206 switch (result_ty.zigTypeTag(mod)) { 20207 .Vector => if (try sema.resolveValue(operand)) |val| { 20208 const scalar_ty = result_ty.scalarType(mod); 20209 const vec_len = result_ty.vectorLen(mod); 20210 if (val.isUndef(mod)) 20211 return try mod.undefRef(result_ty); 20212 20213 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 20214 for (elems, 0..) |*elem, i| { 20215 const elem_val = try val.elemValue(sema.mod, i); 20216 elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod); 20217 } 20218 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 20219 .ty = result_ty.toIntern(), 20220 .storage = .{ .elems = elems }, 20221 } }))); 20222 }, 20223 else => if (try sema.resolveValue(operand)) |operand_val| { 20224 if (operand_val.isUndef(mod)) 20225 return try mod.undefRef(result_ty); 20226 const result_val = try eval(operand_val, result_ty, sema.arena, sema.mod); 20227 return Air.internedToRef(result_val.toIntern()); 20228 }, 20229 } 20230 return null; 20231 } 20232 20233 fn zirUnaryMath( 20234 sema: *Sema, 20235 block: *Block, 20236 inst: Zir.Inst.Index, 20237 air_tag: Air.Inst.Tag, 20238 comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value, 20239 ) CompileError!Air.Inst.Ref { 20240 const tracy = trace(@src()); 20241 defer tracy.end(); 20242 20243 const mod = sema.mod; 20244 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20245 const operand = try sema.resolveInst(inst_data.operand); 20246 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20247 const operand_ty = sema.typeOf(operand); 20248 const scalar_ty = operand_ty.scalarType(mod); 20249 20250 switch (scalar_ty.zigTypeTag(mod)) { 20251 .ComptimeFloat, .Float => {}, 20252 else => return sema.fail( 20253 block, 20254 operand_src, 20255 "expected vector of floats or float type, found '{}'", 20256 .{operand_ty.fmt(sema.mod)}, 20257 ), 20258 } 20259 20260 return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse { 20261 try sema.requireRuntimeBlock(block, operand_src, null); 20262 return block.addUnOp(air_tag, operand); 20263 }; 20264 } 20265 20266 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20267 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 20268 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20269 const src = inst_data.src(); 20270 const operand = try sema.resolveInst(inst_data.operand); 20271 const operand_ty = sema.typeOf(operand); 20272 const mod = sema.mod; 20273 const ip = &mod.intern_pool; 20274 20275 try sema.resolveTypeLayout(operand_ty); 20276 const enum_ty = switch (operand_ty.zigTypeTag(mod)) { 20277 .EnumLiteral => { 20278 const val = try sema.resolveConstDefinedValue(block, .unneeded, operand, undefined); 20279 const tag_name = ip.indexToKey(val.toIntern()).enum_literal; 20280 return sema.addStrLit(ip.stringToSlice(tag_name)); 20281 }, 20282 .Enum => operand_ty, 20283 .Union => operand_ty.unionTagType(mod) orelse { 20284 const msg = msg: { 20285 const msg = try sema.errMsg(block, src, "union '{}' is untagged", .{ 20286 operand_ty.fmt(sema.mod), 20287 }); 20288 errdefer msg.destroy(sema.gpa); 20289 try sema.addDeclaredHereNote(msg, operand_ty); 20290 break :msg msg; 20291 }; 20292 return sema.failWithOwnedErrorMsg(block, msg); 20293 }, 20294 else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{ 20295 operand_ty.fmt(mod), 20296 }), 20297 }; 20298 if (enum_ty.enumFieldCount(mod) == 0) { 20299 // TODO I don't think this is the correct way to handle this but 20300 // it prevents a crash. 20301 return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{}'", .{ 20302 enum_ty.fmt(mod), 20303 }); 20304 } 20305 const enum_decl_index = enum_ty.getOwnerDecl(mod); 20306 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); 20307 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { 20308 const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse { 20309 const enum_decl = mod.declPtr(enum_decl_index); 20310 const msg = msg: { 20311 const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{}'", .{ 20312 val.fmtValue(enum_ty, sema.mod), enum_decl.name.fmt(ip), 20313 }); 20314 errdefer msg.destroy(sema.gpa); 20315 try mod.errNoteNonLazy(enum_decl.srcLoc(mod), msg, "declared here", .{}); 20316 break :msg msg; 20317 }; 20318 return sema.failWithOwnedErrorMsg(block, msg); 20319 }; 20320 // TODO: write something like getCoercedInts to avoid needing to dupe 20321 const field_name = enum_ty.enumFieldName(field_index, mod); 20322 return sema.addStrLit(ip.stringToSlice(field_name)); 20323 } 20324 try sema.requireRuntimeBlock(block, src, operand_src); 20325 if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) { 20326 const ok = try block.addUnOp(.is_named_enum_value, casted_operand); 20327 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); 20328 } 20329 // In case the value is runtime-known, we have an AIR instruction for this instead 20330 // of trying to lower it in Sema because an optimization pass may result in the operand 20331 // being comptime-known, which would let us elide the `tag_name` AIR instruction. 20332 return block.addUnOp(.tag_name, casted_operand); 20333 } 20334 20335 fn zirReify( 20336 sema: *Sema, 20337 block: *Block, 20338 extended: Zir.Inst.Extended.InstData, 20339 inst: Zir.Inst.Index, 20340 ) CompileError!Air.Inst.Ref { 20341 const mod = sema.mod; 20342 const gpa = sema.gpa; 20343 const ip = &mod.intern_pool; 20344 const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small); 20345 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 20346 const src = LazySrcLoc.nodeOffset(extra.node); 20347 const type_info_ty = try sema.getBuiltinType("Type"); 20348 const uncasted_operand = try sema.resolveInst(extra.operand); 20349 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20350 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); 20351 const val = try sema.resolveConstDefinedValue(block, operand_src, type_info, .{ 20352 .needed_comptime_reason = "operand to @Type must be comptime-known", 20353 }); 20354 const union_val = ip.indexToKey(val.toIntern()).un; 20355 const target = mod.getTarget(); 20356 if (try Value.fromInterned(union_val.val).anyUndef(mod)) return sema.failWithUseOfUndef(block, src); 20357 const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), mod).?; 20358 switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { 20359 .Type => return .type_type, 20360 .Void => return .void_type, 20361 .Bool => return .bool_type, 20362 .NoReturn => return .noreturn_type, 20363 .ComptimeFloat => return .comptime_float_type, 20364 .ComptimeInt => return .comptime_int_type, 20365 .Undefined => return .undefined_type, 20366 .Null => return .null_type, 20367 .AnyFrame => return sema.failWithUseOfAsync(block, src), 20368 .EnumLiteral => return .enum_literal_type, 20369 .Int => { 20370 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20371 const signedness_val = try Value.fromInterned(union_val.val).fieldValue( 20372 mod, 20373 struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "signedness")).?, 20374 ); 20375 const bits_val = try Value.fromInterned(union_val.val).fieldValue( 20376 mod, 20377 struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "bits")).?, 20378 ); 20379 20380 const signedness = mod.toEnum(std.builtin.Signedness, signedness_val); 20381 const bits: u16 = @intCast(bits_val.toUnsignedInt(mod)); 20382 const ty = try mod.intType(signedness, bits); 20383 return Air.internedToRef(ty.toIntern()); 20384 }, 20385 .Vector => { 20386 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20387 const len_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20388 ip, 20389 try ip.getOrPutString(gpa, "len"), 20390 ).?); 20391 const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20392 ip, 20393 try ip.getOrPutString(gpa, "child"), 20394 ).?); 20395 20396 const len: u32 = @intCast(len_val.toUnsignedInt(mod)); 20397 const child_ty = child_val.toType(); 20398 20399 try sema.checkVectorElemType(block, src, child_ty); 20400 20401 const ty = try mod.vectorType(.{ 20402 .len = len, 20403 .child = child_ty.toIntern(), 20404 }); 20405 return Air.internedToRef(ty.toIntern()); 20406 }, 20407 .Float => { 20408 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20409 const bits_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20410 ip, 20411 try ip.getOrPutString(gpa, "bits"), 20412 ).?); 20413 20414 const bits: u16 = @intCast(bits_val.toUnsignedInt(mod)); 20415 const ty = switch (bits) { 20416 16 => Type.f16, 20417 32 => Type.f32, 20418 64 => Type.f64, 20419 80 => Type.f80, 20420 128 => Type.f128, 20421 else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}), 20422 }; 20423 return Air.internedToRef(ty.toIntern()); 20424 }, 20425 .Pointer => { 20426 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20427 const size_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20428 ip, 20429 try ip.getOrPutString(gpa, "size"), 20430 ).?); 20431 const is_const_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20432 ip, 20433 try ip.getOrPutString(gpa, "is_const"), 20434 ).?); 20435 const is_volatile_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20436 ip, 20437 try ip.getOrPutString(gpa, "is_volatile"), 20438 ).?); 20439 const alignment_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20440 ip, 20441 try ip.getOrPutString(gpa, "alignment"), 20442 ).?); 20443 const address_space_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20444 ip, 20445 try ip.getOrPutString(gpa, "address_space"), 20446 ).?); 20447 const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20448 ip, 20449 try ip.getOrPutString(gpa, "child"), 20450 ).?); 20451 const is_allowzero_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20452 ip, 20453 try ip.getOrPutString(gpa, "is_allowzero"), 20454 ).?); 20455 const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20456 ip, 20457 try ip.getOrPutString(gpa, "sentinel"), 20458 ).?); 20459 20460 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { 20461 return sema.fail(block, src, "alignment must fit in 'u32'", .{}); 20462 } 20463 20464 const alignment_val_int = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; 20465 if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) { 20466 return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{alignment_val_int}); 20467 } 20468 const abi_align = Alignment.fromByteUnits(alignment_val_int); 20469 20470 const elem_ty = child_val.toType(); 20471 if (abi_align != .none) { 20472 try sema.resolveTypeLayout(elem_ty); 20473 } 20474 20475 const ptr_size = mod.toEnum(std.builtin.Type.Pointer.Size, size_val); 20476 20477 const actual_sentinel: InternPool.Index = s: { 20478 if (!sentinel_val.isNull(mod)) { 20479 if (ptr_size == .One or ptr_size == .C) { 20480 return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); 20481 } 20482 const sentinel_ptr_val = sentinel_val.optionalValue(mod).?; 20483 const ptr_ty = try mod.singleMutPtrType(elem_ty); 20484 const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; 20485 break :s sent_val.toIntern(); 20486 } 20487 break :s .none; 20488 }; 20489 20490 if (elem_ty.zigTypeTag(mod) == .NoReturn) { 20491 return sema.fail(block, src, "pointer to noreturn not allowed", .{}); 20492 } else if (elem_ty.zigTypeTag(mod) == .Fn) { 20493 if (ptr_size != .One) { 20494 return sema.fail(block, src, "function pointers must be single pointers", .{}); 20495 } 20496 const fn_align = mod.typeToFunc(elem_ty).?.alignment; 20497 if (abi_align != .none and fn_align != .none and 20498 abi_align != fn_align) 20499 { 20500 return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{}); 20501 } 20502 } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { 20503 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); 20504 } else if (ptr_size == .C) { 20505 if (!try sema.validateExternType(elem_ty, .other)) { 20506 const msg = msg: { 20507 const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)}); 20508 errdefer msg.destroy(gpa); 20509 20510 const src_decl = mod.declPtr(block.src_decl); 20511 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other); 20512 20513 try sema.addDeclaredHereNote(msg, elem_ty); 20514 break :msg msg; 20515 }; 20516 return sema.failWithOwnedErrorMsg(block, msg); 20517 } 20518 if (elem_ty.zigTypeTag(mod) == .Opaque) { 20519 return sema.fail(block, src, "C pointers cannot point to opaque types", .{}); 20520 } 20521 } 20522 20523 const ty = try sema.ptrType(.{ 20524 .child = elem_ty.toIntern(), 20525 .sentinel = actual_sentinel, 20526 .flags = .{ 20527 .size = ptr_size, 20528 .is_const = is_const_val.toBool(), 20529 .is_volatile = is_volatile_val.toBool(), 20530 .alignment = abi_align, 20531 .address_space = mod.toEnum(std.builtin.AddressSpace, address_space_val), 20532 .is_allowzero = is_allowzero_val.toBool(), 20533 }, 20534 }); 20535 return Air.internedToRef(ty.toIntern()); 20536 }, 20537 .Array => { 20538 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20539 const len_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20540 ip, 20541 try ip.getOrPutString(gpa, "len"), 20542 ).?); 20543 const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20544 ip, 20545 try ip.getOrPutString(gpa, "child"), 20546 ).?); 20547 const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20548 ip, 20549 try ip.getOrPutString(gpa, "sentinel"), 20550 ).?); 20551 20552 const len = len_val.toUnsignedInt(mod); 20553 const child_ty = child_val.toType(); 20554 const sentinel = if (sentinel_val.optionalValue(mod)) |p| blk: { 20555 const ptr_ty = try mod.singleMutPtrType(child_ty); 20556 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?; 20557 } else null; 20558 20559 const ty = try mod.arrayType(.{ 20560 .len = len, 20561 .sentinel = if (sentinel) |s| s.toIntern() else .none, 20562 .child = child_ty.toIntern(), 20563 }); 20564 return Air.internedToRef(ty.toIntern()); 20565 }, 20566 .Optional => { 20567 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20568 const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20569 ip, 20570 try ip.getOrPutString(gpa, "child"), 20571 ).?); 20572 20573 const child_ty = child_val.toType(); 20574 20575 const ty = try mod.optionalType(child_ty.toIntern()); 20576 return Air.internedToRef(ty.toIntern()); 20577 }, 20578 .ErrorUnion => { 20579 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20580 const error_set_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20581 ip, 20582 try ip.getOrPutString(gpa, "error_set"), 20583 ).?); 20584 const payload_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20585 ip, 20586 try ip.getOrPutString(gpa, "payload"), 20587 ).?); 20588 20589 const error_set_ty = error_set_val.toType(); 20590 const payload_ty = payload_val.toType(); 20591 20592 if (error_set_ty.zigTypeTag(mod) != .ErrorSet) { 20593 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{}); 20594 } 20595 20596 const ty = try mod.errorUnionType(error_set_ty, payload_ty); 20597 return Air.internedToRef(ty.toIntern()); 20598 }, 20599 .ErrorSet => { 20600 const payload_val = Value.fromInterned(union_val.val).optionalValue(mod) orelse 20601 return Air.internedToRef(Type.anyerror.toIntern()); 20602 20603 const len = try sema.usizeCast(block, src, payload_val.sliceLen(mod)); 20604 var names: InferredErrorSet.NameMap = .{}; 20605 try names.ensureUnusedCapacity(sema.arena, len); 20606 for (0..len) |i| { 20607 const elem_val = try payload_val.elemValue(mod, i); 20608 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; 20609 const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 20610 ip, 20611 try ip.getOrPutString(gpa, "name"), 20612 ).?); 20613 20614 const name = try name_val.toIpString(Type.slice_const_u8, mod); 20615 _ = try mod.getErrorValue(name); 20616 const gop = names.getOrPutAssumeCapacity(name); 20617 if (gop.found_existing) { 20618 return sema.fail(block, src, "duplicate error '{}'", .{ 20619 name.fmt(ip), 20620 }); 20621 } 20622 } 20623 20624 const ty = try mod.errorSetFromUnsortedNames(names.keys()); 20625 return Air.internedToRef(ty.toIntern()); 20626 }, 20627 .Struct => { 20628 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20629 const layout_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20630 ip, 20631 try ip.getOrPutString(gpa, "layout"), 20632 ).?); 20633 const backing_integer_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20634 ip, 20635 try ip.getOrPutString(gpa, "backing_integer"), 20636 ).?); 20637 const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20638 ip, 20639 try ip.getOrPutString(gpa, "fields"), 20640 ).?); 20641 const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20642 ip, 20643 try ip.getOrPutString(gpa, "decls"), 20644 ).?); 20645 const is_tuple_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20646 ip, 20647 try ip.getOrPutString(gpa, "is_tuple"), 20648 ).?); 20649 20650 const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); 20651 20652 // Decls 20653 if (decls_val.sliceLen(mod) > 0) { 20654 return sema.fail(block, src, "reified structs must have no decls", .{}); 20655 } 20656 20657 if (layout != .Packed and !backing_integer_val.isNull(mod)) { 20658 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); 20659 } 20660 20661 return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool()); 20662 }, 20663 .Enum => { 20664 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20665 const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20666 ip, 20667 try ip.getOrPutString(gpa, "tag_type"), 20668 ).?); 20669 const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20670 ip, 20671 try ip.getOrPutString(gpa, "fields"), 20672 ).?); 20673 const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20674 ip, 20675 try ip.getOrPutString(gpa, "decls"), 20676 ).?); 20677 const is_exhaustive_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20678 ip, 20679 try ip.getOrPutString(gpa, "is_exhaustive"), 20680 ).?); 20681 20682 // Decls 20683 if (decls_val.sliceLen(mod) > 0) { 20684 return sema.fail(block, src, "reified enums must have no decls", .{}); 20685 } 20686 20687 const int_tag_ty = tag_type_val.toType(); 20688 if (int_tag_ty.zigTypeTag(mod) != .Int) { 20689 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); 20690 } 20691 20692 // Because these things each reference each other, `undefined` 20693 // placeholders are used before being set after the enum type gains 20694 // an InternPool index. 20695 20696 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 20697 .ty = Type.noreturn, 20698 .val = Value.@"unreachable", 20699 }, name_strategy, "enum", inst); 20700 const new_decl = mod.declPtr(new_decl_index); 20701 new_decl.owns_tv = true; 20702 errdefer { 20703 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 20704 mod.abortAnonDecl(new_decl_index); 20705 } 20706 20707 // Define our empty enum decl 20708 const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); 20709 const incomplete_enum = try ip.getIncompleteEnum(gpa, .{ 20710 .decl = new_decl_index, 20711 .namespace = .none, 20712 .fields_len = fields_len, 20713 .has_values = true, 20714 .tag_mode = if (!is_exhaustive_val.toBool()) 20715 .nonexhaustive 20716 else 20717 .explicit, 20718 .tag_ty = int_tag_ty.toIntern(), 20719 }); 20720 // TODO: figure out InternPool removals for incremental compilation 20721 //errdefer ip.remove(incomplete_enum.index); 20722 20723 new_decl.ty = Type.type; 20724 new_decl.val = Value.fromInterned(incomplete_enum.index); 20725 20726 for (0..fields_len) |field_i| { 20727 const elem_val = try fields_val.elemValue(mod, field_i); 20728 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; 20729 const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 20730 ip, 20731 try ip.getOrPutString(gpa, "name"), 20732 ).?); 20733 const value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 20734 ip, 20735 try ip.getOrPutString(gpa, "value"), 20736 ).?); 20737 20738 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 20739 20740 if (!try sema.intFitsInType(value_val, int_tag_ty, null)) { 20741 // TODO: better source location 20742 return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{ 20743 field_name.fmt(ip), 20744 value_val.fmtValue(Type.comptime_int, mod), 20745 int_tag_ty.fmt(mod), 20746 }); 20747 } 20748 20749 if (incomplete_enum.addFieldName(ip, field_name)) |other_index| { 20750 const msg = msg: { 20751 const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{ 20752 field_name.fmt(ip), 20753 }); 20754 errdefer msg.destroy(gpa); 20755 _ = other_index; // TODO: this note is incorrect 20756 try sema.errNote(block, src, msg, "other field here", .{}); 20757 break :msg msg; 20758 }; 20759 return sema.failWithOwnedErrorMsg(block, msg); 20760 } 20761 20762 if (incomplete_enum.addFieldValue(ip, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| { 20763 const msg = msg: { 20764 const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)}); 20765 errdefer msg.destroy(gpa); 20766 _ = other; // TODO: this note is incorrect 20767 try sema.errNote(block, src, msg, "other enum tag value here", .{}); 20768 break :msg msg; 20769 }; 20770 return sema.failWithOwnedErrorMsg(block, msg); 20771 } 20772 } 20773 20774 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20775 try mod.finalizeAnonDecl(new_decl_index); 20776 return decl_val; 20777 }, 20778 .Opaque => { 20779 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20780 const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20781 ip, 20782 try ip.getOrPutString(gpa, "decls"), 20783 ).?); 20784 20785 // Decls 20786 if (decls_val.sliceLen(mod) > 0) { 20787 return sema.fail(block, src, "reified opaque must have no decls", .{}); 20788 } 20789 20790 // Because these three things each reference each other, 20791 // `undefined` placeholders are used in two places before being set 20792 // after the opaque type gains an InternPool index. 20793 20794 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 20795 .ty = Type.noreturn, 20796 .val = Value.@"unreachable", 20797 }, name_strategy, "opaque", inst); 20798 const new_decl = mod.declPtr(new_decl_index); 20799 new_decl.owns_tv = true; 20800 errdefer { 20801 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 20802 mod.abortAnonDecl(new_decl_index); 20803 } 20804 20805 const new_namespace_index = try mod.createNamespace(.{ 20806 .parent = block.namespace.toOptional(), 20807 .ty = undefined, 20808 .file_scope = block.getFileScope(mod), 20809 }); 20810 const new_namespace = mod.namespacePtr(new_namespace_index); 20811 errdefer mod.destroyNamespace(new_namespace_index); 20812 20813 const opaque_ty = try mod.intern(.{ .opaque_type = .{ 20814 .decl = new_decl_index, 20815 .namespace = new_namespace_index, 20816 } }); 20817 // TODO: figure out InternPool removals for incremental compilation 20818 //errdefer ip.remove(opaque_ty); 20819 20820 new_decl.ty = Type.type; 20821 new_decl.val = Value.fromInterned(opaque_ty); 20822 new_namespace.ty = Type.fromInterned(opaque_ty); 20823 20824 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20825 try mod.finalizeAnonDecl(new_decl_index); 20826 return decl_val; 20827 }, 20828 .Union => { 20829 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 20830 const layout_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20831 ip, 20832 try ip.getOrPutString(gpa, "layout"), 20833 ).?); 20834 const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20835 ip, 20836 try ip.getOrPutString(gpa, "tag_type"), 20837 ).?); 20838 const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20839 ip, 20840 try ip.getOrPutString(gpa, "fields"), 20841 ).?); 20842 const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 20843 ip, 20844 try ip.getOrPutString(gpa, "decls"), 20845 ).?); 20846 20847 // Decls 20848 if (decls_val.sliceLen(mod) > 0) { 20849 return sema.fail(block, src, "reified unions must have no decls", .{}); 20850 } 20851 const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); 20852 const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); 20853 20854 // Tag type 20855 var explicit_tags_seen: []bool = &.{}; 20856 var enum_field_names: []InternPool.NullTerminatedString = &.{}; 20857 var enum_tag_ty: InternPool.Index = .none; 20858 if (tag_type_val.optionalValue(mod)) |payload_val| { 20859 enum_tag_ty = payload_val.toType().toIntern(); 20860 20861 const enum_type = switch (ip.indexToKey(enum_tag_ty)) { 20862 .enum_type => |x| x, 20863 else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), 20864 }; 20865 20866 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); 20867 @memset(explicit_tags_seen, false); 20868 } else { 20869 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 20870 } 20871 20872 // Fields 20873 var any_aligned_fields: bool = false; 20874 var union_fields: std.MultiArrayList(struct { 20875 type: InternPool.Index, 20876 alignment: InternPool.Alignment, 20877 }) = .{}; 20878 var field_name_table: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; 20879 try field_name_table.ensureTotalCapacity(sema.arena, fields_len); 20880 20881 for (0..fields_len) |i| { 20882 const elem_val = try fields_val.elemValue(mod, i); 20883 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; 20884 const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 20885 ip, 20886 try ip.getOrPutString(gpa, "name"), 20887 ).?); 20888 const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 20889 ip, 20890 try ip.getOrPutString(gpa, "type"), 20891 ).?); 20892 const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 20893 ip, 20894 try ip.getOrPutString(gpa, "alignment"), 20895 ).?); 20896 20897 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 20898 20899 if (enum_field_names.len != 0) { 20900 enum_field_names[i] = field_name; 20901 } 20902 20903 if (explicit_tags_seen.len > 0) { 20904 const tag_info = ip.indexToKey(enum_tag_ty).enum_type; 20905 const enum_index = tag_info.nameIndex(ip, field_name) orelse { 20906 const msg = msg: { 20907 const msg = try sema.errMsg(block, src, "no field named '{}' in enum '{}'", .{ 20908 field_name.fmt(ip), 20909 Type.fromInterned(enum_tag_ty).fmt(mod), 20910 }); 20911 errdefer msg.destroy(gpa); 20912 try sema.addDeclaredHereNote(msg, Type.fromInterned(enum_tag_ty)); 20913 break :msg msg; 20914 }; 20915 return sema.failWithOwnedErrorMsg(block, msg); 20916 }; 20917 // No check for duplicate because the check already happened in order 20918 // to create the enum type in the first place. 20919 assert(!explicit_tags_seen[enum_index]); 20920 explicit_tags_seen[enum_index] = true; 20921 } 20922 20923 const gop = field_name_table.getOrPutAssumeCapacity(field_name); 20924 if (gop.found_existing) { 20925 // TODO: better source location 20926 return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); 20927 } 20928 20929 const field_ty = type_val.toType(); 20930 const alignment_val_int = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; 20931 if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) { 20932 // TODO: better source location 20933 return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{ 20934 alignment_val_int, 20935 }); 20936 } 20937 const field_align = Alignment.fromByteUnits(alignment_val_int); 20938 any_aligned_fields = any_aligned_fields or field_align != .none; 20939 20940 try union_fields.append(sema.arena, .{ 20941 .type = field_ty.toIntern(), 20942 .alignment = field_align, 20943 }); 20944 20945 if (field_ty.zigTypeTag(mod) == .Opaque) { 20946 const msg = msg: { 20947 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 20948 errdefer msg.destroy(gpa); 20949 20950 try sema.addDeclaredHereNote(msg, field_ty); 20951 break :msg msg; 20952 }; 20953 return sema.failWithOwnedErrorMsg(block, msg); 20954 } 20955 if (layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { 20956 const msg = msg: { 20957 const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 20958 errdefer msg.destroy(gpa); 20959 20960 const src_decl = mod.declPtr(block.src_decl); 20961 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field); 20962 20963 try sema.addDeclaredHereNote(msg, field_ty); 20964 break :msg msg; 20965 }; 20966 return sema.failWithOwnedErrorMsg(block, msg); 20967 } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { 20968 const msg = msg: { 20969 const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 20970 errdefer msg.destroy(gpa); 20971 20972 const src_decl = mod.declPtr(block.src_decl); 20973 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); 20974 20975 try sema.addDeclaredHereNote(msg, field_ty); 20976 break :msg msg; 20977 }; 20978 return sema.failWithOwnedErrorMsg(block, msg); 20979 } 20980 } 20981 20982 if (explicit_tags_seen.len > 0) { 20983 const tag_info = ip.indexToKey(enum_tag_ty).enum_type; 20984 if (tag_info.names.len > fields_len) { 20985 const msg = msg: { 20986 const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); 20987 errdefer msg.destroy(gpa); 20988 20989 for (tag_info.names.get(ip), 0..) |field_name, field_index| { 20990 if (explicit_tags_seen[field_index]) continue; 20991 try sema.addFieldErrNote(Type.fromInterned(enum_tag_ty), field_index, msg, "field '{}' missing, declared here", .{ 20992 field_name.fmt(ip), 20993 }); 20994 } 20995 try sema.addDeclaredHereNote(msg, Type.fromInterned(enum_tag_ty)); 20996 break :msg msg; 20997 }; 20998 return sema.failWithOwnedErrorMsg(block, msg); 20999 } 21000 } else { 21001 enum_tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, .none); 21002 } 21003 21004 // Because these three things each reference each other, `undefined` 21005 // placeholders are used before being set after the union type gains an 21006 // InternPool index. 21007 21008 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 21009 .ty = Type.noreturn, 21010 .val = Value.@"unreachable", 21011 }, name_strategy, "union", inst); 21012 const new_decl = mod.declPtr(new_decl_index); 21013 new_decl.owns_tv = true; 21014 errdefer { 21015 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 21016 mod.abortAnonDecl(new_decl_index); 21017 } 21018 21019 const new_namespace_index = try mod.createNamespace(.{ 21020 .parent = block.namespace.toOptional(), 21021 .ty = undefined, 21022 .file_scope = block.getFileScope(mod), 21023 }); 21024 const new_namespace = mod.namespacePtr(new_namespace_index); 21025 errdefer mod.destroyNamespace(new_namespace_index); 21026 21027 const union_ty = try ip.getUnionType(gpa, .{ 21028 .decl = new_decl_index, 21029 .namespace = new_namespace_index, 21030 .enum_tag_ty = enum_tag_ty, 21031 .fields_len = fields_len, 21032 .zir_index = inst, 21033 .flags = .{ 21034 .layout = layout, 21035 .status = .have_field_types, 21036 .runtime_tag = if (!tag_type_val.isNull(mod)) 21037 .tagged 21038 else if (layout != .Auto) 21039 .none 21040 else switch (block.wantSafety()) { 21041 true => .safety, 21042 false => .none, 21043 }, 21044 .any_aligned_fields = any_aligned_fields, 21045 .requires_comptime = .unknown, 21046 .assumed_runtime_bits = false, 21047 .assumed_pointer_aligned = false, 21048 .alignment = .none, 21049 }, 21050 .field_types = union_fields.items(.type), 21051 .field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{}, 21052 }); 21053 21054 new_decl.ty = Type.type; 21055 new_decl.val = Value.fromInterned(union_ty); 21056 new_namespace.ty = Type.fromInterned(union_ty); 21057 21058 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 21059 try mod.finalizeAnonDecl(new_decl_index); 21060 return decl_val; 21061 }, 21062 .Fn => { 21063 const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; 21064 const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 21065 ip, 21066 try ip.getOrPutString(gpa, "calling_convention"), 21067 ).?); 21068 const alignment_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 21069 ip, 21070 try ip.getOrPutString(gpa, "alignment"), 21071 ).?); 21072 const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 21073 ip, 21074 try ip.getOrPutString(gpa, "is_generic"), 21075 ).?); 21076 const is_var_args_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 21077 ip, 21078 try ip.getOrPutString(gpa, "is_var_args"), 21079 ).?); 21080 const return_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 21081 ip, 21082 try ip.getOrPutString(gpa, "return_type"), 21083 ).?); 21084 const params_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( 21085 ip, 21086 try ip.getOrPutString(gpa, "params"), 21087 ).?); 21088 21089 const is_generic = is_generic_val.toBool(); 21090 if (is_generic) { 21091 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{}); 21092 } 21093 21094 const is_var_args = is_var_args_val.toBool(); 21095 const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val); 21096 if (is_var_args) { 21097 try sema.checkCallConvSupportsVarArgs(block, src, cc); 21098 } 21099 21100 const alignment = alignment: { 21101 const alignment = try sema.validateAlignAllowZero(block, src, alignment_val.toUnsignedInt(mod)); 21102 const default = target_util.defaultFunctionAlignment(target); 21103 break :alignment if (alignment == default) .none else alignment; 21104 }; 21105 const return_type = return_type_val.optionalValue(mod) orelse 21106 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); 21107 21108 const args_len = try sema.usizeCast(block, src, params_val.sliceLen(mod)); 21109 const param_types = try sema.arena.alloc(InternPool.Index, args_len); 21110 21111 var noalias_bits: u32 = 0; 21112 for (param_types, 0..) |*param_type, i| { 21113 const elem_val = try params_val.elemValue(mod, i); 21114 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; 21115 const param_is_generic_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 21116 ip, 21117 try ip.getOrPutString(gpa, "is_generic"), 21118 ).?); 21119 const param_is_noalias_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 21120 ip, 21121 try ip.getOrPutString(gpa, "is_noalias"), 21122 ).?); 21123 const opt_param_type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 21124 ip, 21125 try ip.getOrPutString(gpa, "type"), 21126 ).?); 21127 21128 if (param_is_generic_val.toBool()) { 21129 return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{}); 21130 } 21131 21132 const param_type_val = opt_param_type_val.optionalValue(mod) orelse 21133 return sema.fail(block, src, "Type.Fn.Param.type must be non-null for @Type", .{}); 21134 param_type.* = param_type_val.toIntern(); 21135 21136 if (param_is_noalias_val.toBool()) { 21137 if (!Type.fromInterned(param_type.*).isPtrAtRuntime(mod)) { 21138 return sema.fail(block, src, "non-pointer parameter declared noalias", .{}); 21139 } 21140 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse 21141 return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 21142 } 21143 } 21144 21145 const ty = try mod.funcType(.{ 21146 .param_types = param_types, 21147 .noalias_bits = noalias_bits, 21148 .return_type = return_type.toIntern(), 21149 .alignment = alignment, 21150 .cc = cc, 21151 .is_var_args = is_var_args, 21152 }); 21153 return Air.internedToRef(ty.toIntern()); 21154 }, 21155 .Frame => return sema.failWithUseOfAsync(block, src), 21156 } 21157 } 21158 21159 fn reifyStruct( 21160 sema: *Sema, 21161 block: *Block, 21162 inst: Zir.Inst.Index, 21163 src: LazySrcLoc, 21164 layout: std.builtin.Type.ContainerLayout, 21165 backing_int_val: Value, 21166 fields_val: Value, 21167 name_strategy: Zir.Inst.NameStrategy, 21168 is_tuple: bool, 21169 ) CompileError!Air.Inst.Ref { 21170 const mod = sema.mod; 21171 const gpa = sema.gpa; 21172 const ip = &mod.intern_pool; 21173 21174 if (is_tuple) switch (layout) { 21175 .Extern => return sema.fail(block, src, "extern tuples are not supported", .{}), 21176 .Packed => return sema.fail(block, src, "packed tuples are not supported", .{}), 21177 .Auto => {}, 21178 }; 21179 21180 const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); 21181 21182 // Because these three things each reference each other, `undefined` 21183 // placeholders are used before being set after the struct type gains an 21184 // InternPool index. 21185 21186 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 21187 .ty = Type.noreturn, 21188 .val = Value.@"unreachable", 21189 }, name_strategy, "struct", inst); 21190 const new_decl = mod.declPtr(new_decl_index); 21191 new_decl.owns_tv = true; 21192 errdefer { 21193 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 21194 mod.abortAnonDecl(new_decl_index); 21195 } 21196 21197 const ty = try ip.getStructType(gpa, .{ 21198 .decl = new_decl_index, 21199 .namespace = .none, 21200 .zir_index = inst, 21201 .layout = layout, 21202 .known_non_opv = false, 21203 .fields_len = fields_len, 21204 .requires_comptime = .unknown, 21205 .is_tuple = is_tuple, 21206 // So that we don't have to scan ahead, we allocate space in the struct 21207 // type for alignments, comptime fields, and default inits. This might 21208 // result in wasted space, however, this is a permitted encoding of 21209 // struct types. 21210 .any_comptime_fields = true, 21211 .any_default_inits = true, 21212 .inits_resolved = true, 21213 .any_aligned_fields = true, 21214 }); 21215 // TODO: figure out InternPool removals for incremental compilation 21216 //errdefer ip.remove(ty); 21217 const struct_type = ip.indexToKey(ty).struct_type; 21218 21219 new_decl.ty = Type.type; 21220 new_decl.val = Value.fromInterned(ty); 21221 21222 // Fields 21223 for (0..fields_len) |i| { 21224 const elem_val = try fields_val.elemValue(mod, i); 21225 const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; 21226 const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 21227 ip, 21228 try ip.getOrPutString(gpa, "name"), 21229 ).?); 21230 const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 21231 ip, 21232 try ip.getOrPutString(gpa, "type"), 21233 ).?); 21234 const default_value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 21235 ip, 21236 try ip.getOrPutString(gpa, "default_value"), 21237 ).?); 21238 const is_comptime_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 21239 ip, 21240 try ip.getOrPutString(gpa, "is_comptime"), 21241 ).?); 21242 const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( 21243 ip, 21244 try ip.getOrPutString(gpa, "alignment"), 21245 ).?); 21246 21247 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { 21248 return sema.fail(block, src, "alignment must fit in 'u32'", .{}); 21249 } 21250 const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; 21251 21252 if (layout == .Packed) { 21253 if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); 21254 if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}); 21255 } else { 21256 if (abi_align > 0 and !math.isPowerOfTwo(abi_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{abi_align}); 21257 struct_type.field_aligns.get(ip)[i] = Alignment.fromByteUnits(abi_align); 21258 } 21259 if (layout == .Extern and is_comptime_val.toBool()) { 21260 return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}); 21261 } 21262 21263 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 21264 21265 if (is_tuple) { 21266 const field_index = field_name.toUnsigned(ip) orelse return sema.fail( 21267 block, 21268 src, 21269 "tuple cannot have non-numeric field '{}'", 21270 .{field_name.fmt(ip)}, 21271 ); 21272 21273 if (field_index >= fields_len) { 21274 return sema.fail( 21275 block, 21276 src, 21277 "tuple field {} exceeds tuple field count", 21278 .{field_index}, 21279 ); 21280 } 21281 } else if (struct_type.addFieldName(ip, field_name)) |prev_index| { 21282 _ = prev_index; // TODO: better source location 21283 return sema.fail(block, src, "duplicate struct field {}", .{field_name.fmt(ip)}); 21284 } 21285 21286 const field_ty = type_val.toType(); 21287 const default_val = if (default_value_val.optionalValue(mod)) |opt_val| 21288 (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse 21289 return sema.failWithNeededComptime(block, src, .{ 21290 .needed_comptime_reason = "struct field default value must be comptime-known", 21291 })).toIntern() 21292 else 21293 .none; 21294 if (is_comptime_val.toBool() and default_val == .none) { 21295 return sema.fail(block, src, "comptime field without default initialization value", .{}); 21296 } 21297 21298 struct_type.field_types.get(ip)[i] = field_ty.toIntern(); 21299 struct_type.field_inits.get(ip)[i] = default_val; 21300 if (is_comptime_val.toBool()) 21301 struct_type.setFieldComptime(ip, i); 21302 21303 if (field_ty.zigTypeTag(mod) == .Opaque) { 21304 const msg = msg: { 21305 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 21306 errdefer msg.destroy(gpa); 21307 21308 try sema.addDeclaredHereNote(msg, field_ty); 21309 break :msg msg; 21310 }; 21311 return sema.failWithOwnedErrorMsg(block, msg); 21312 } 21313 if (field_ty.zigTypeTag(mod) == .NoReturn) { 21314 const msg = msg: { 21315 const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{}); 21316 errdefer msg.destroy(gpa); 21317 21318 try sema.addDeclaredHereNote(msg, field_ty); 21319 break :msg msg; 21320 }; 21321 return sema.failWithOwnedErrorMsg(block, msg); 21322 } 21323 if (layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) { 21324 const msg = msg: { 21325 const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); 21326 errdefer msg.destroy(gpa); 21327 21328 const src_decl = sema.mod.declPtr(block.src_decl); 21329 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field); 21330 21331 try sema.addDeclaredHereNote(msg, field_ty); 21332 break :msg msg; 21333 }; 21334 return sema.failWithOwnedErrorMsg(block, msg); 21335 } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { 21336 const msg = msg: { 21337 const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); 21338 errdefer msg.destroy(gpa); 21339 21340 const src_decl = sema.mod.declPtr(block.src_decl); 21341 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); 21342 21343 try sema.addDeclaredHereNote(msg, field_ty); 21344 break :msg msg; 21345 }; 21346 return sema.failWithOwnedErrorMsg(block, msg); 21347 } 21348 } 21349 21350 if (layout == .Packed) { 21351 for (0..struct_type.field_types.len) |index| { 21352 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]); 21353 sema.resolveTypeLayout(field_ty) catch |err| switch (err) { 21354 error.AnalysisFail => { 21355 const msg = sema.err orelse return err; 21356 try sema.addFieldErrNote(Type.fromInterned(ty), index, msg, "while checking this field", .{}); 21357 return err; 21358 }, 21359 else => return err, 21360 }; 21361 } 21362 21363 var fields_bit_sum: u64 = 0; 21364 for (0..struct_type.field_types.len) |i| { 21365 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 21366 fields_bit_sum += field_ty.bitSize(mod); 21367 } 21368 21369 if (backing_int_val.optionalValue(mod)) |backing_int_ty_val| { 21370 const backing_int_ty = backing_int_ty_val.toType(); 21371 try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); 21372 struct_type.backingIntType(ip).* = backing_int_ty.toIntern(); 21373 } else { 21374 const backing_int_ty = try mod.intType(.unsigned, @intCast(fields_bit_sum)); 21375 struct_type.backingIntType(ip).* = backing_int_ty.toIntern(); 21376 } 21377 } 21378 21379 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 21380 try mod.finalizeAnonDecl(new_decl_index); 21381 return decl_val; 21382 } 21383 21384 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { 21385 const va_list_ty = try sema.getBuiltinType("VaList"); 21386 const va_list_ptr = try sema.mod.singleMutPtrType(va_list_ty); 21387 21388 const inst = try sema.resolveInst(zir_ref); 21389 return sema.coerce(block, va_list_ptr, inst, src); 21390 } 21391 21392 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21393 const mod = sema.mod; 21394 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 21395 const src = LazySrcLoc.nodeOffset(extra.node); 21396 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 21397 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 21398 21399 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs); 21400 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs); 21401 21402 if (!try sema.validateExternType(arg_ty, .param_ty)) { 21403 const msg = msg: { 21404 const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)}); 21405 errdefer msg.destroy(sema.gpa); 21406 21407 const src_decl = sema.mod.declPtr(block.src_decl); 21408 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty); 21409 21410 try sema.addDeclaredHereNote(msg, arg_ty); 21411 break :msg msg; 21412 }; 21413 return sema.failWithOwnedErrorMsg(block, msg); 21414 } 21415 21416 try sema.requireRuntimeBlock(block, src, null); 21417 return block.addTyOp(.c_va_arg, arg_ty, va_list_ref); 21418 } 21419 21420 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21421 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 21422 const src = LazySrcLoc.nodeOffset(extra.node); 21423 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 21424 21425 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 21426 const va_list_ty = try sema.getBuiltinType("VaList"); 21427 21428 try sema.requireRuntimeBlock(block, src, null); 21429 return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref); 21430 } 21431 21432 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21433 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 21434 const src = LazySrcLoc.nodeOffset(extra.node); 21435 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 21436 21437 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 21438 21439 try sema.requireRuntimeBlock(block, src, null); 21440 return block.addUnOp(.c_va_end, va_list_ref); 21441 } 21442 21443 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21444 const src = LazySrcLoc.nodeOffset(@bitCast(extended.operand)); 21445 21446 const va_list_ty = try sema.getBuiltinType("VaList"); 21447 try sema.requireRuntimeBlock(block, src, null); 21448 return block.addInst(.{ 21449 .tag = .c_va_start, 21450 .data = .{ .ty = va_list_ty }, 21451 }); 21452 } 21453 21454 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21455 const mod = sema.mod; 21456 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 21457 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21458 const ty = try sema.resolveType(block, ty_src, inst_data.operand); 21459 21460 var bytes = std.ArrayList(u8).init(sema.arena); 21461 try ty.print(bytes.writer(), mod); 21462 return addStrLitNoAlias(sema, bytes.items); 21463 } 21464 21465 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21466 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 21467 const src = inst_data.src(); 21468 return sema.failWithUseOfAsync(block, src); 21469 } 21470 21471 fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21472 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 21473 const src = inst_data.src(); 21474 return sema.failWithUseOfAsync(block, src); 21475 } 21476 21477 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21478 const mod = sema.mod; 21479 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21480 const src = inst_data.src(); 21481 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21482 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21483 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat"); 21484 const operand = try sema.resolveInst(extra.rhs); 21485 const operand_ty = sema.typeOf(operand); 21486 21487 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 21488 const is_vector = dest_ty.zigTypeTag(mod) == .Vector; 21489 21490 const dest_scalar_ty = dest_ty.scalarType(mod); 21491 const operand_scalar_ty = operand_ty.scalarType(mod); 21492 21493 _ = try sema.checkIntType(block, src, dest_scalar_ty); 21494 try sema.checkFloatType(block, operand_src, operand_scalar_ty); 21495 21496 if (try sema.resolveValue(operand)) |operand_val| { 21497 const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate); 21498 return Air.internedToRef(result_val.toIntern()); 21499 } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 21500 return sema.failWithNeededComptime(block, operand_src, .{ 21501 .needed_comptime_reason = "value being casted to 'comptime_int' must be comptime-known", 21502 }); 21503 } 21504 21505 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 21506 if (dest_scalar_ty.intInfo(mod).bits == 0) { 21507 if (!is_vector) { 21508 if (block.wantSafety()) { 21509 const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, Air.internedToRef((try mod.floatValue(operand_ty, 0.0)).toIntern())); 21510 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds); 21511 } 21512 return Air.internedToRef((try mod.intValue(dest_ty, 0)).toIntern()); 21513 } 21514 if (block.wantSafety()) { 21515 const len = dest_ty.vectorLen(mod); 21516 for (0..len) |i| { 21517 const idx_ref = try mod.intRef(Type.usize, i); 21518 const elem_ref = try block.addBinOp(.array_elem_val, operand, idx_ref); 21519 const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, elem_ref, Air.internedToRef((try mod.floatValue(operand_scalar_ty, 0.0)).toIntern())); 21520 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds); 21521 } 21522 } 21523 return Air.internedToRef(try mod.intern(.{ .aggregate = .{ 21524 .ty = dest_ty.toIntern(), 21525 .storage = .{ .repeated_elem = (try mod.intValue(dest_scalar_ty, 0)).toIntern() }, 21526 } })); 21527 } 21528 if (!is_vector) { 21529 const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_ty, operand); 21530 if (block.wantSafety()) { 21531 const back = try block.addTyOp(.float_from_int, operand_ty, result); 21532 const diff = try block.addBinOp(.sub, operand, back); 21533 const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, Air.internedToRef((try mod.floatValue(operand_ty, 1.0)).toIntern())); 21534 const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, Air.internedToRef((try mod.floatValue(operand_ty, -1.0)).toIntern())); 21535 const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg); 21536 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds); 21537 } 21538 return result; 21539 } 21540 const len = dest_ty.vectorLen(mod); 21541 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len); 21542 for (new_elems, 0..) |*new_elem, i| { 21543 const idx_ref = try mod.intRef(Type.usize, i); 21544 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref); 21545 const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_scalar_ty, old_elem); 21546 if (block.wantSafety()) { 21547 const back = try block.addTyOp(.float_from_int, operand_scalar_ty, result); 21548 const diff = try block.addBinOp(.sub, old_elem, back); 21549 const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, Air.internedToRef((try mod.floatValue(operand_scalar_ty, 1.0)).toIntern())); 21550 const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, Air.internedToRef((try mod.floatValue(operand_scalar_ty, -1.0)).toIntern())); 21551 const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg); 21552 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds); 21553 } 21554 new_elem.* = result; 21555 } 21556 return block.addAggregateInit(dest_ty, new_elems); 21557 } 21558 21559 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21560 const mod = sema.mod; 21561 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21562 const src = inst_data.src(); 21563 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21564 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21565 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt"); 21566 const operand = try sema.resolveInst(extra.rhs); 21567 const operand_ty = sema.typeOf(operand); 21568 21569 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); 21570 const is_vector = dest_ty.zigTypeTag(mod) == .Vector; 21571 21572 const dest_scalar_ty = dest_ty.scalarType(mod); 21573 const operand_scalar_ty = operand_ty.scalarType(mod); 21574 21575 try sema.checkFloatType(block, src, dest_scalar_ty); 21576 _ = try sema.checkIntType(block, operand_src, operand_scalar_ty); 21577 21578 if (try sema.resolveValue(operand)) |operand_val| { 21579 const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, mod, sema); 21580 return Air.internedToRef(result_val.toIntern()); 21581 } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeFloat) { 21582 return sema.failWithNeededComptime(block, operand_src, .{ 21583 .needed_comptime_reason = "value being casted to 'comptime_float' must be comptime-known", 21584 }); 21585 } 21586 21587 try sema.requireRuntimeBlock(block, src, operand_src); 21588 if (!is_vector) { 21589 return block.addTyOp(.float_from_int, dest_ty, operand); 21590 } 21591 const len = operand_ty.vectorLen(mod); 21592 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len); 21593 for (new_elems, 0..) |*new_elem, i| { 21594 const idx_ref = try mod.intRef(Type.usize, i); 21595 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref); 21596 new_elem.* = try block.addTyOp(.float_from_int, dest_scalar_ty, old_elem); 21597 } 21598 return block.addAggregateInit(dest_ty, new_elems); 21599 } 21600 21601 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21602 const mod = sema.mod; 21603 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21604 const src = inst_data.src(); 21605 21606 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21607 21608 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21609 const operand_res = try sema.resolveInst(extra.rhs); 21610 21611 const uncoerced_operand_ty = sema.typeOf(operand_res); 21612 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt"); 21613 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src); 21614 21615 const is_vector = dest_ty.zigTypeTag(mod) == .Vector; 21616 const operand_ty = if (is_vector) operand_ty: { 21617 const len = dest_ty.vectorLen(mod); 21618 break :operand_ty try mod.vectorType(.{ .child = .usize_type, .len = len }); 21619 } else Type.usize; 21620 21621 const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src); 21622 21623 const ptr_ty = dest_ty.scalarType(mod); 21624 try sema.checkPtrType(block, src, ptr_ty, true); 21625 21626 const elem_ty = ptr_ty.elemType2(mod); 21627 const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema); 21628 21629 if (ptr_ty.isSlice(mod)) { 21630 const msg = msg: { 21631 const msg = try sema.errMsg(block, src, "integer cannot be converted to slice type '{}'", .{ptr_ty.fmt(sema.mod)}); 21632 errdefer msg.destroy(sema.gpa); 21633 try sema.errNote(block, src, msg, "slice length cannot be inferred from address", .{}); 21634 break :msg msg; 21635 }; 21636 return sema.failWithOwnedErrorMsg(block, msg); 21637 } 21638 21639 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { 21640 if (!is_vector) { 21641 const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align); 21642 return Air.internedToRef(ptr_val.toIntern()); 21643 } 21644 const len = dest_ty.vectorLen(mod); 21645 const new_elems = try sema.arena.alloc(InternPool.Index, len); 21646 for (new_elems, 0..) |*new_elem, i| { 21647 const elem = try val.elemValue(mod, i); 21648 const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align); 21649 new_elem.* = ptr_val.toIntern(); 21650 } 21651 return Air.internedToRef(try mod.intern(.{ .aggregate = .{ 21652 .ty = dest_ty.toIntern(), 21653 .storage = .{ .elems = new_elems }, 21654 } })); 21655 } 21656 21657 try sema.requireRuntimeBlock(block, src, operand_src); 21658 if (!is_vector) { 21659 if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) { 21660 if (!ptr_ty.isAllowzeroPtr(mod)) { 21661 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); 21662 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null); 21663 } 21664 if (ptr_align.compare(.gt, .@"1")) { 21665 const align_bytes_minus_1 = ptr_align.toByteUnitsOptional().? - 1; 21666 const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, align_bytes_minus_1)).toIntern()); 21667 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1); 21668 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 21669 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment); 21670 } 21671 } 21672 return block.addBitCast(dest_ty, operand_coerced); 21673 } 21674 21675 const len = dest_ty.vectorLen(mod); 21676 if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) { 21677 for (0..len) |i| { 21678 const idx_ref = try mod.intRef(Type.usize, i); 21679 const elem_coerced = try block.addBinOp(.array_elem_val, operand_coerced, idx_ref); 21680 if (!ptr_ty.isAllowzeroPtr(mod)) { 21681 const is_non_zero = try block.addBinOp(.cmp_neq, elem_coerced, .zero_usize); 21682 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null); 21683 } 21684 if (ptr_align.compare(.gt, .@"1")) { 21685 const align_bytes_minus_1 = ptr_align.toByteUnitsOptional().? - 1; 21686 const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, align_bytes_minus_1)).toIntern()); 21687 const remainder = try block.addBinOp(.bit_and, elem_coerced, align_minus_1); 21688 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 21689 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment); 21690 } 21691 } 21692 } 21693 21694 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len); 21695 for (new_elems, 0..) |*new_elem, i| { 21696 const idx_ref = try mod.intRef(Type.usize, i); 21697 const old_elem = try block.addBinOp(.array_elem_val, operand_coerced, idx_ref); 21698 new_elem.* = try block.addBitCast(ptr_ty, old_elem); 21699 } 21700 return block.addAggregateInit(dest_ty, new_elems); 21701 } 21702 21703 fn ptrFromIntVal( 21704 sema: *Sema, 21705 block: *Block, 21706 operand_src: LazySrcLoc, 21707 operand_val: Value, 21708 ptr_ty: Type, 21709 ptr_align: Alignment, 21710 ) !Value { 21711 const mod = sema.mod; 21712 const addr = operand_val.toUnsignedInt(mod); 21713 if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0) 21714 return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)}); 21715 if (addr != 0 and ptr_align != .none and !ptr_align.check(addr)) 21716 return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)}); 21717 21718 return switch (ptr_ty.zigTypeTag(mod)) { 21719 .Optional => Value.fromInterned((try mod.intern(.{ .opt = .{ 21720 .ty = ptr_ty.toIntern(), 21721 .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(), 21722 } }))), 21723 .Pointer => try mod.ptrIntValue(ptr_ty, addr), 21724 else => unreachable, 21725 }; 21726 } 21727 21728 fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21729 const mod = sema.mod; 21730 const ip = &mod.intern_pool; 21731 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 21732 const src = LazySrcLoc.nodeOffset(extra.node); 21733 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 21734 const base_dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); 21735 const operand = try sema.resolveInst(extra.rhs); 21736 const base_operand_ty = sema.typeOf(operand); 21737 const dest_tag = base_dest_ty.zigTypeTag(mod); 21738 const operand_tag = base_operand_ty.zigTypeTag(mod); 21739 if (dest_tag != operand_tag) { 21740 return sema.fail(block, src, "expected source and destination types to match, found '{s}' and '{s}'", .{ 21741 @tagName(operand_tag), @tagName(dest_tag), 21742 }); 21743 } else if (dest_tag != .ErrorSet and dest_tag != .ErrorUnion) { 21744 return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)}); 21745 } 21746 const dest_ty, const operand_ty = if (dest_tag == .ErrorUnion) .{ 21747 base_dest_ty.errorUnionSet(mod), 21748 base_operand_ty.errorUnionSet(mod), 21749 } else .{ 21750 base_dest_ty, 21751 base_operand_ty, 21752 }; 21753 21754 // operand must be defined since it can be an invalid error value 21755 const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand); 21756 21757 const disjoint = disjoint: { 21758 // Try avoiding resolving inferred error sets if we can 21759 if (!dest_ty.isAnyError(mod) and dest_ty.errorSetIsEmpty(mod)) break :disjoint true; 21760 if (!operand_ty.isAnyError(mod) and operand_ty.errorSetIsEmpty(mod)) break :disjoint true; 21761 if (dest_ty.isAnyError(mod)) break :disjoint false; 21762 if (operand_ty.isAnyError(mod)) break :disjoint false; 21763 for (dest_ty.errorSetNames(mod)) |dest_err_name| { 21764 if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) 21765 break :disjoint false; 21766 } 21767 21768 if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and 21769 !ip.isInferredErrorSetType(operand_ty.toIntern())) 21770 { 21771 break :disjoint true; 21772 } 21773 21774 _ = try sema.resolveInferredErrorSetTy(block, src, dest_ty.toIntern()); 21775 _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty.toIntern()); 21776 for (dest_ty.errorSetNames(mod)) |dest_err_name| { 21777 if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) 21778 break :disjoint false; 21779 } 21780 21781 break :disjoint true; 21782 }; 21783 if (disjoint and dest_tag != .ErrorUnion) { 21784 const msg = msg: { 21785 const msg = try sema.errMsg( 21786 block, 21787 src, 21788 "error sets '{}' and '{}' have no common errors", 21789 .{ operand_ty.fmt(sema.mod), dest_ty.fmt(sema.mod) }, 21790 ); 21791 errdefer msg.destroy(sema.gpa); 21792 try sema.addDeclaredHereNote(msg, operand_ty); 21793 try sema.addDeclaredHereNote(msg, dest_ty); 21794 break :msg msg; 21795 }; 21796 return sema.failWithOwnedErrorMsg(block, msg); 21797 } 21798 21799 if (maybe_operand_val) |val| { 21800 if (!dest_ty.isAnyError(mod)) check: { 21801 const operand_val = mod.intern_pool.indexToKey(val.toIntern()); 21802 var error_name: InternPool.NullTerminatedString = undefined; 21803 if (dest_tag == .ErrorUnion) { 21804 if (operand_val.error_union.val != .err_name) break :check; 21805 error_name = operand_val.error_union.val.err_name; 21806 } else { 21807 error_name = operand_val.err.name; 21808 } 21809 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) { 21810 const msg = msg: { 21811 const msg = try sema.errMsg( 21812 block, 21813 src, 21814 "'error.{}' not a member of error set '{}'", 21815 .{ error_name.fmt(ip), dest_ty.fmt(sema.mod) }, 21816 ); 21817 errdefer msg.destroy(sema.gpa); 21818 try sema.addDeclaredHereNote(msg, dest_ty); 21819 break :msg msg; 21820 }; 21821 return sema.failWithOwnedErrorMsg(block, msg); 21822 } 21823 } 21824 21825 return Air.internedToRef((try mod.getCoerced(val, base_dest_ty)).toIntern()); 21826 } 21827 21828 try sema.requireRuntimeBlock(block, src, operand_src); 21829 const err_int_ty = try mod.errorIntType(); 21830 if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) { 21831 if (dest_tag == .ErrorUnion) { 21832 const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand); 21833 const err_int = try block.addBitCast(err_int_ty, err_code); 21834 const zero_err = try mod.intRef(try mod.errorIntType(), 0); 21835 21836 const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_err); 21837 if (disjoint) { 21838 // Error must be zero. 21839 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code); 21840 } else { 21841 // Error must be in destination set or zero. 21842 const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code); 21843 const ok = try block.addBinOp(.bool_or, has_value, is_zero); 21844 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 21845 } 21846 } else { 21847 const err_int_inst = try block.addBitCast(err_int_ty, operand); 21848 const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); 21849 try sema.addSafetyCheck(block, src, ok, .invalid_error_code); 21850 } 21851 } 21852 return block.addBitCast(base_dest_ty, operand); 21853 } 21854 21855 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21856 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as( 21857 @typeInfo(Zir.Inst.FullPtrCastFlags).Struct.backing_integer.?, 21858 @truncate(extended.small), 21859 )); 21860 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 21861 const src = LazySrcLoc.nodeOffset(extra.node); 21862 const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node }; 21863 const operand = try sema.resolveInst(extra.rhs); 21864 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName()); 21865 return sema.ptrCastFull( 21866 block, 21867 flags, 21868 src, 21869 operand, 21870 operand_src, 21871 dest_ty, 21872 ); 21873 } 21874 21875 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21876 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 21877 const src = inst_data.src(); 21878 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21879 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21880 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast"); 21881 const operand = try sema.resolveInst(extra.rhs); 21882 21883 return sema.ptrCastFull( 21884 block, 21885 .{ .ptr_cast = true }, 21886 src, 21887 operand, 21888 operand_src, 21889 dest_ty, 21890 ); 21891 } 21892 21893 fn ptrCastFull( 21894 sema: *Sema, 21895 block: *Block, 21896 flags: Zir.Inst.FullPtrCastFlags, 21897 src: LazySrcLoc, 21898 operand: Air.Inst.Ref, 21899 operand_src: LazySrcLoc, 21900 dest_ty: Type, 21901 ) CompileError!Air.Inst.Ref { 21902 const mod = sema.mod; 21903 const operand_ty = sema.typeOf(operand); 21904 21905 try sema.checkPtrType(block, src, dest_ty, true); 21906 try sema.checkPtrOperand(block, operand_src, operand_ty); 21907 21908 const src_info = operand_ty.ptrInfo(mod); 21909 const dest_info = dest_ty.ptrInfo(mod); 21910 21911 try sema.resolveTypeLayout(Type.fromInterned(src_info.child)); 21912 try sema.resolveTypeLayout(Type.fromInterned(dest_info.child)); 21913 21914 const src_slice_like = src_info.flags.size == .Slice or 21915 (src_info.flags.size == .One and Type.fromInterned(src_info.child).zigTypeTag(mod) == .Array); 21916 21917 const dest_slice_like = dest_info.flags.size == .Slice or 21918 (dest_info.flags.size == .One and Type.fromInterned(dest_info.child).zigTypeTag(mod) == .Array); 21919 21920 if (dest_info.flags.size == .Slice and !src_slice_like) { 21921 return sema.fail(block, src, "illegal pointer cast to slice", .{}); 21922 } 21923 21924 if (dest_info.flags.size == .Slice) { 21925 const src_elem_size = switch (src_info.flags.size) { 21926 .Slice => Type.fromInterned(src_info.child).abiSize(mod), 21927 // pointer to array 21928 .One => Type.fromInterned(src_info.child).childType(mod).abiSize(mod), 21929 else => unreachable, 21930 }; 21931 const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(mod); 21932 if (src_elem_size != dest_elem_size) { 21933 return sema.fail(block, src, "TODO: implement @ptrCast between slices changing the length", .{}); 21934 } 21935 } 21936 21937 // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs 21938 21939 if (!flags.ptr_cast) { 21940 check_size: { 21941 if (src_info.flags.size == dest_info.flags.size) break :check_size; 21942 if (src_slice_like and dest_slice_like) break :check_size; 21943 if (src_info.flags.size == .C) break :check_size; 21944 if (dest_info.flags.size == .C) break :check_size; 21945 return sema.failWithOwnedErrorMsg(block, msg: { 21946 const msg = try sema.errMsg(block, src, "cannot implicitly convert {s} pointer to {s} pointer", .{ 21947 pointerSizeString(src_info.flags.size), 21948 pointerSizeString(dest_info.flags.size), 21949 }); 21950 errdefer msg.destroy(sema.gpa); 21951 if (dest_info.flags.size == .Many and 21952 (src_info.flags.size == .Slice or 21953 (src_info.flags.size == .One and Type.fromInterned(src_info.child).zigTypeTag(mod) == .Array))) 21954 { 21955 try sema.errNote(block, src, msg, "use 'ptr' field to convert slice to many pointer", .{}); 21956 } else { 21957 try sema.errNote(block, src, msg, "use @ptrCast to change pointer size", .{}); 21958 } 21959 break :msg msg; 21960 }); 21961 } 21962 21963 check_child: { 21964 const src_child = if (dest_info.flags.size == .Slice and src_info.flags.size == .One) blk: { 21965 // *[n]T -> []T 21966 break :blk Type.fromInterned(src_info.child).childType(mod); 21967 } else Type.fromInterned(src_info.child); 21968 21969 const dest_child = Type.fromInterned(dest_info.child); 21970 21971 const imc_res = try sema.coerceInMemoryAllowed( 21972 block, 21973 dest_child, 21974 src_child, 21975 !dest_info.flags.is_const, 21976 mod.getTarget(), 21977 src, 21978 operand_src, 21979 ); 21980 if (imc_res == .ok) break :check_child; 21981 return sema.failWithOwnedErrorMsg(block, msg: { 21982 const msg = try sema.errMsg(block, src, "pointer element type '{}' cannot coerce into element type '{}'", .{ 21983 src_child.fmt(mod), 21984 dest_child.fmt(mod), 21985 }); 21986 errdefer msg.destroy(sema.gpa); 21987 try imc_res.report(sema, block, src, msg); 21988 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer element type", .{}); 21989 break :msg msg; 21990 }); 21991 } 21992 21993 check_sent: { 21994 if (dest_info.sentinel == .none) break :check_sent; 21995 if (src_info.flags.size == .C) break :check_sent; 21996 if (src_info.sentinel != .none) { 21997 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child); 21998 if (dest_info.sentinel == coerced_sent) break :check_sent; 21999 } 22000 if (src_slice_like and src_info.flags.size == .One and dest_info.flags.size == .Slice) { 22001 // [*]nT -> []T 22002 const arr_ty = Type.fromInterned(src_info.child); 22003 if (arr_ty.sentinel(mod)) |src_sentinel| { 22004 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_sentinel.toIntern(), dest_info.child); 22005 if (dest_info.sentinel == coerced_sent) break :check_sent; 22006 } 22007 } 22008 return sema.failWithOwnedErrorMsg(block, msg: { 22009 const msg = if (src_info.sentinel == .none) blk: { 22010 break :blk try sema.errMsg(block, src, "destination pointer requires '{}' sentinel", .{ 22011 Value.fromInterned(dest_info.sentinel).fmtValue(Type.fromInterned(dest_info.child), mod), 22012 }); 22013 } else blk: { 22014 break :blk try sema.errMsg(block, src, "pointer sentinel '{}' cannot coerce into pointer sentinel '{}'", .{ 22015 Value.fromInterned(src_info.sentinel).fmtValue(Type.fromInterned(src_info.child), mod), 22016 Value.fromInterned(dest_info.sentinel).fmtValue(Type.fromInterned(dest_info.child), mod), 22017 }); 22018 }; 22019 errdefer msg.destroy(sema.gpa); 22020 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer sentinel", .{}); 22021 break :msg msg; 22022 }); 22023 } 22024 22025 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) { 22026 return sema.failWithOwnedErrorMsg(block, msg: { 22027 const msg = try sema.errMsg(block, src, "pointer host size '{}' cannot coerce into pointer host size '{}'", .{ 22028 src_info.packed_offset.host_size, 22029 dest_info.packed_offset.host_size, 22030 }); 22031 errdefer msg.destroy(sema.gpa); 22032 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer host size", .{}); 22033 break :msg msg; 22034 }); 22035 } 22036 22037 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) { 22038 return sema.failWithOwnedErrorMsg(block, msg: { 22039 const msg = try sema.errMsg(block, src, "pointer bit offset '{}' cannot coerce into pointer bit offset '{}'", .{ 22040 src_info.packed_offset.bit_offset, 22041 dest_info.packed_offset.bit_offset, 22042 }); 22043 errdefer msg.destroy(sema.gpa); 22044 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer bit offset", .{}); 22045 break :msg msg; 22046 }); 22047 } 22048 22049 check_allowzero: { 22050 const src_allows_zero = operand_ty.ptrAllowsZero(mod); 22051 const dest_allows_zero = dest_ty.ptrAllowsZero(mod); 22052 if (!src_allows_zero) break :check_allowzero; 22053 if (dest_allows_zero) break :check_allowzero; 22054 22055 return sema.failWithOwnedErrorMsg(block, msg: { 22056 const msg = try sema.errMsg(block, src, "'{}' could have null values which are illegal in type '{}'", .{ 22057 operand_ty.fmt(mod), 22058 dest_ty.fmt(mod), 22059 }); 22060 errdefer msg.destroy(sema.gpa); 22061 try sema.errNote(block, src, msg, "use @ptrCast to assert the pointer is not null", .{}); 22062 break :msg msg; 22063 }); 22064 } 22065 22066 // TODO: vector index? 22067 } 22068 22069 const src_align = if (src_info.flags.alignment != .none) 22070 src_info.flags.alignment 22071 else 22072 Type.fromInterned(src_info.child).abiAlignment(mod); 22073 22074 const dest_align = if (dest_info.flags.alignment != .none) 22075 dest_info.flags.alignment 22076 else 22077 Type.fromInterned(dest_info.child).abiAlignment(mod); 22078 22079 if (!flags.align_cast) { 22080 if (dest_align.compare(.gt, src_align)) { 22081 return sema.failWithOwnedErrorMsg(block, msg: { 22082 const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{}); 22083 errdefer msg.destroy(sema.gpa); 22084 try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{ 22085 operand_ty.fmt(mod), src_align.toByteUnits(0), 22086 }); 22087 try sema.errNote(block, src, msg, "'{}' has alignment '{d}'", .{ 22088 dest_ty.fmt(mod), dest_align.toByteUnits(0), 22089 }); 22090 try sema.errNote(block, src, msg, "use @alignCast to assert pointer alignment", .{}); 22091 break :msg msg; 22092 }); 22093 } 22094 } 22095 22096 if (!flags.addrspace_cast) { 22097 if (src_info.flags.address_space != dest_info.flags.address_space) { 22098 return sema.failWithOwnedErrorMsg(block, msg: { 22099 const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{}); 22100 errdefer msg.destroy(sema.gpa); 22101 try sema.errNote(block, operand_src, msg, "'{}' has address space '{s}'", .{ 22102 operand_ty.fmt(mod), @tagName(src_info.flags.address_space), 22103 }); 22104 try sema.errNote(block, src, msg, "'{}' has address space '{s}'", .{ 22105 dest_ty.fmt(mod), @tagName(dest_info.flags.address_space), 22106 }); 22107 try sema.errNote(block, src, msg, "use @addrSpaceCast to cast pointer address space", .{}); 22108 break :msg msg; 22109 }); 22110 } 22111 } else { 22112 // Some address space casts are always disallowed 22113 if (!target_util.addrSpaceCastIsValid(mod.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) { 22114 return sema.failWithOwnedErrorMsg(block, msg: { 22115 const msg = try sema.errMsg(block, src, "invalid address space cast", .{}); 22116 errdefer msg.destroy(sema.gpa); 22117 try sema.errNote(block, operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{ 22118 @tagName(src_info.flags.address_space), 22119 @tagName(dest_info.flags.address_space), 22120 }); 22121 break :msg msg; 22122 }); 22123 } 22124 } 22125 22126 if (!flags.const_cast) { 22127 if (src_info.flags.is_const and !dest_info.flags.is_const) { 22128 return sema.failWithOwnedErrorMsg(block, msg: { 22129 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{}); 22130 errdefer msg.destroy(sema.gpa); 22131 try sema.errNote(block, src, msg, "use @constCast to discard const qualifier", .{}); 22132 break :msg msg; 22133 }); 22134 } 22135 } 22136 22137 if (!flags.volatile_cast) { 22138 if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) { 22139 return sema.failWithOwnedErrorMsg(block, msg: { 22140 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{}); 22141 errdefer msg.destroy(sema.gpa); 22142 try sema.errNote(block, src, msg, "use @volatileCast to discard volatile qualifier", .{}); 22143 break :msg msg; 22144 }); 22145 } 22146 } 22147 22148 const ptr = if (src_info.flags.size == .Slice and dest_info.flags.size != .Slice) ptr: { 22149 break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); 22150 } else operand; 22151 22152 const dest_ptr_ty = if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) blk: { 22153 // Only convert to a many-pointer at first 22154 var info = dest_info; 22155 info.flags.size = .Many; 22156 const ty = try sema.ptrType(info); 22157 if (dest_ty.zigTypeTag(mod) == .Optional) { 22158 break :blk try mod.optionalType(ty.toIntern()); 22159 } else { 22160 break :blk ty; 22161 } 22162 } else dest_ty; 22163 22164 // Cannot do @addrSpaceCast at comptime 22165 if (!flags.addrspace_cast) { 22166 if (try sema.resolveValue(ptr)) |ptr_val| { 22167 if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isUndef(mod)) { 22168 return sema.failWithUseOfUndef(block, operand_src); 22169 } 22170 if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isNull(mod)) { 22171 return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)}); 22172 } 22173 if (dest_align.compare(.gt, src_align)) { 22174 if (try ptr_val.getUnsignedIntAdvanced(mod, null)) |addr| { 22175 if (!dest_align.check(addr)) { 22176 return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ 22177 addr, 22178 dest_align.toByteUnitsOptional().?, 22179 }); 22180 } 22181 } 22182 } 22183 if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) { 22184 if (ptr_val.isUndef(mod)) return mod.undefRef(dest_ty); 22185 const arr_len = try mod.intValue(Type.usize, Type.fromInterned(src_info.child).arrayLen(mod)); 22186 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 22187 .ty = dest_ty.toIntern(), 22188 .addr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr, 22189 .len = arr_len.toIntern(), 22190 } }))); 22191 } else { 22192 assert(dest_ptr_ty.eql(dest_ty, mod)); 22193 return Air.internedToRef((try mod.getCoerced(ptr_val, dest_ty)).toIntern()); 22194 } 22195 } 22196 } 22197 22198 try sema.requireRuntimeBlock(block, src, null); 22199 22200 if (block.wantSafety() and operand_ty.ptrAllowsZero(mod) and !dest_ty.ptrAllowsZero(mod) and 22201 (try sema.typeHasRuntimeBits(Type.fromInterned(dest_info.child)) or Type.fromInterned(dest_info.child).zigTypeTag(mod) == .Fn)) 22202 { 22203 const ptr_int = try block.addUnOp(.int_from_ptr, ptr); 22204 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); 22205 const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: { 22206 const len = try sema.analyzeSliceLen(block, operand_src, ptr); 22207 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 22208 break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero); 22209 } else is_non_zero; 22210 try sema.addSafetyCheck(block, src, ok, .cast_to_null); 22211 } 22212 22213 if (block.wantSafety() and 22214 dest_align.compare(.gt, src_align) and 22215 try sema.typeHasRuntimeBits(Type.fromInterned(dest_info.child))) 22216 { 22217 const align_bytes_minus_1 = dest_align.toByteUnitsOptional().? - 1; 22218 const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, align_bytes_minus_1)).toIntern()); 22219 const ptr_int = try block.addUnOp(.int_from_ptr, ptr); 22220 const remainder = try block.addBinOp(.bit_and, ptr_int, align_minus_1); 22221 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 22222 const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: { 22223 const len = try sema.analyzeSliceLen(block, operand_src, ptr); 22224 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 22225 break :ok try block.addBinOp(.bool_or, len_zero, is_aligned); 22226 } else is_aligned; 22227 try sema.addSafetyCheck(block, src, ok, .incorrect_alignment); 22228 } 22229 22230 // If we're going from an array pointer to a slice, this will only be the pointer part! 22231 const result_ptr = if (flags.addrspace_cast) ptr: { 22232 // We can't change address spaces with a bitcast, so this requires two instructions 22233 var intermediate_info = src_info; 22234 intermediate_info.flags.address_space = dest_info.flags.address_space; 22235 const intermediate_ptr_ty = try sema.ptrType(intermediate_info); 22236 const intermediate_ty = if (dest_ptr_ty.zigTypeTag(mod) == .Optional) blk: { 22237 break :blk try mod.optionalType(intermediate_ptr_ty.toIntern()); 22238 } else intermediate_ptr_ty; 22239 const intermediate = try block.addInst(.{ 22240 .tag = .addrspace_cast, 22241 .data = .{ .ty_op = .{ 22242 .ty = Air.internedToRef(intermediate_ty.toIntern()), 22243 .operand = ptr, 22244 } }, 22245 }); 22246 if (intermediate_ty.eql(dest_ptr_ty, mod)) { 22247 // We only changed the address space, so no need for a bitcast 22248 break :ptr intermediate; 22249 } 22250 break :ptr try block.addBitCast(dest_ptr_ty, intermediate); 22251 } else ptr: { 22252 break :ptr try block.addBitCast(dest_ptr_ty, ptr); 22253 }; 22254 22255 if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) { 22256 // We have to construct a slice using the operand's child's array length 22257 // Note that we know from the check at the start of the function that operand_ty is slice-like 22258 const arr_len = Air.internedToRef((try mod.intValue(Type.usize, Type.fromInterned(src_info.child).arrayLen(mod))).toIntern()); 22259 return block.addInst(.{ 22260 .tag = .slice, 22261 .data = .{ .ty_pl = .{ 22262 .ty = Air.internedToRef(dest_ty.toIntern()), 22263 .payload = try sema.addExtra(Air.Bin{ 22264 .lhs = result_ptr, 22265 .rhs = arr_len, 22266 }), 22267 } }, 22268 }); 22269 } else { 22270 assert(dest_ptr_ty.eql(dest_ty, mod)); 22271 try sema.checkKnownAllocPtr(operand, result_ptr); 22272 return result_ptr; 22273 } 22274 } 22275 22276 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 22277 const mod = sema.mod; 22278 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as( 22279 @typeInfo(Zir.Inst.FullPtrCastFlags).Struct.backing_integer.?, 22280 @truncate(extended.small), 22281 )); 22282 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 22283 const src = LazySrcLoc.nodeOffset(extra.node); 22284 const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node }; 22285 const operand = try sema.resolveInst(extra.operand); 22286 const operand_ty = sema.typeOf(operand); 22287 try sema.checkPtrOperand(block, operand_src, operand_ty); 22288 22289 var ptr_info = operand_ty.ptrInfo(mod); 22290 if (flags.const_cast) ptr_info.flags.is_const = false; 22291 if (flags.volatile_cast) ptr_info.flags.is_volatile = false; 22292 const dest_ty = try sema.ptrType(ptr_info); 22293 22294 if (try sema.resolveValue(operand)) |operand_val| { 22295 return Air.internedToRef((try mod.getCoerced(operand_val, dest_ty)).toIntern()); 22296 } 22297 22298 try sema.requireRuntimeBlock(block, src, null); 22299 const new_ptr = try block.addBitCast(dest_ty, operand); 22300 try sema.checkKnownAllocPtr(operand, new_ptr); 22301 return new_ptr; 22302 } 22303 22304 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22305 const mod = sema.mod; 22306 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 22307 const src = inst_data.src(); 22308 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22309 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22310 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate"); 22311 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src); 22312 const operand = try sema.resolveInst(extra.rhs); 22313 const operand_ty = sema.typeOf(operand); 22314 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 22315 22316 const operand_is_vector = operand_ty.zigTypeTag(mod) == .Vector; 22317 const dest_is_vector = dest_ty.zigTypeTag(mod) == .Vector; 22318 if (operand_is_vector != dest_is_vector) { 22319 return sema.fail(block, operand_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), operand_ty.fmt(mod) }); 22320 } 22321 22322 if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 22323 return sema.coerce(block, dest_ty, operand, operand_src); 22324 } 22325 22326 const dest_info = dest_scalar_ty.intInfo(mod); 22327 22328 if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { 22329 return Air.internedToRef(val.toIntern()); 22330 } 22331 22332 if (operand_scalar_ty.zigTypeTag(mod) != .ComptimeInt) { 22333 const operand_info = operand_ty.intInfo(mod); 22334 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22335 return Air.internedToRef(val.toIntern()); 22336 } 22337 22338 if (operand_info.signedness != dest_info.signedness) { 22339 return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{ 22340 @tagName(dest_info.signedness), operand_ty.fmt(mod), 22341 }); 22342 } 22343 if (operand_info.bits < dest_info.bits) { 22344 const msg = msg: { 22345 const msg = try sema.errMsg( 22346 block, 22347 src, 22348 "destination type '{}' has more bits than source type '{}'", 22349 .{ dest_ty.fmt(mod), operand_ty.fmt(mod) }, 22350 ); 22351 errdefer msg.destroy(sema.gpa); 22352 try sema.errNote(block, src, msg, "destination type has {d} bits", .{ 22353 dest_info.bits, 22354 }); 22355 try sema.errNote(block, operand_src, msg, "operand type has {d} bits", .{ 22356 operand_info.bits, 22357 }); 22358 break :msg msg; 22359 }; 22360 return sema.failWithOwnedErrorMsg(block, msg); 22361 } 22362 } 22363 22364 if (try sema.resolveValueIntable(operand)) |val| { 22365 if (val.isUndef(mod)) return mod.undefRef(dest_ty); 22366 if (!dest_is_vector) { 22367 return Air.internedToRef((try mod.getCoerced( 22368 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, mod), 22369 dest_ty, 22370 )).toIntern()); 22371 } 22372 const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(mod)); 22373 for (elems, 0..) |*elem, i| { 22374 const elem_val = try val.elemValue(mod, i); 22375 elem.* = try (try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, mod)).intern(dest_scalar_ty, mod); 22376 } 22377 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 22378 .ty = dest_ty.toIntern(), 22379 .storage = .{ .elems = elems }, 22380 } }))); 22381 } 22382 22383 try sema.requireRuntimeBlock(block, src, operand_src); 22384 return block.addTyOp(.trunc, dest_ty, operand); 22385 } 22386 22387 fn zirBitCount( 22388 sema: *Sema, 22389 block: *Block, 22390 inst: Zir.Inst.Index, 22391 air_tag: Air.Inst.Tag, 22392 comptime comptimeOp: fn (val: Value, ty: Type, mod: *Module) u64, 22393 ) CompileError!Air.Inst.Ref { 22394 const mod = sema.mod; 22395 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22396 const src = inst_data.src(); 22397 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22398 const operand = try sema.resolveInst(inst_data.operand); 22399 const operand_ty = sema.typeOf(operand); 22400 _ = try sema.checkIntOrVector(block, operand, operand_src); 22401 const bits = operand_ty.intInfo(mod).bits; 22402 22403 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22404 return Air.internedToRef(val.toIntern()); 22405 } 22406 22407 const result_scalar_ty = try mod.smallestUnsignedInt(bits); 22408 switch (operand_ty.zigTypeTag(mod)) { 22409 .Vector => { 22410 const vec_len = operand_ty.vectorLen(mod); 22411 const result_ty = try mod.vectorType(.{ 22412 .len = vec_len, 22413 .child = result_scalar_ty.toIntern(), 22414 }); 22415 if (try sema.resolveValue(operand)) |val| { 22416 if (val.isUndef(mod)) return mod.undefRef(result_ty); 22417 22418 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 22419 const scalar_ty = operand_ty.scalarType(mod); 22420 for (elems, 0..) |*elem, i| { 22421 const elem_val = try val.elemValue(mod, i); 22422 const count = comptimeOp(elem_val, scalar_ty, mod); 22423 elem.* = (try mod.intValue(result_scalar_ty, count)).toIntern(); 22424 } 22425 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 22426 .ty = result_ty.toIntern(), 22427 .storage = .{ .elems = elems }, 22428 } }))); 22429 } else { 22430 try sema.requireRuntimeBlock(block, src, operand_src); 22431 return block.addTyOp(air_tag, result_ty, operand); 22432 } 22433 }, 22434 .Int => { 22435 if (try sema.resolveValueResolveLazy(operand)) |val| { 22436 if (val.isUndef(mod)) return mod.undefRef(result_scalar_ty); 22437 return mod.intRef(result_scalar_ty, comptimeOp(val, operand_ty, mod)); 22438 } else { 22439 try sema.requireRuntimeBlock(block, src, operand_src); 22440 return block.addTyOp(air_tag, result_scalar_ty, operand); 22441 } 22442 }, 22443 else => unreachable, 22444 } 22445 } 22446 22447 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22448 const mod = sema.mod; 22449 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22450 const src = inst_data.src(); 22451 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22452 const operand = try sema.resolveInst(inst_data.operand); 22453 const operand_ty = sema.typeOf(operand); 22454 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 22455 const bits = scalar_ty.intInfo(mod).bits; 22456 if (bits % 8 != 0) { 22457 return sema.fail( 22458 block, 22459 operand_src, 22460 "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", 22461 .{ scalar_ty.fmt(mod), bits }, 22462 ); 22463 } 22464 22465 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22466 return Air.internedToRef(val.toIntern()); 22467 } 22468 22469 switch (operand_ty.zigTypeTag(mod)) { 22470 .Int => { 22471 const runtime_src = if (try sema.resolveValue(operand)) |val| { 22472 if (val.isUndef(mod)) return mod.undefRef(operand_ty); 22473 const result_val = try val.byteSwap(operand_ty, mod, sema.arena); 22474 return Air.internedToRef(result_val.toIntern()); 22475 } else operand_src; 22476 22477 try sema.requireRuntimeBlock(block, src, runtime_src); 22478 return block.addTyOp(.byte_swap, operand_ty, operand); 22479 }, 22480 .Vector => { 22481 const runtime_src = if (try sema.resolveValue(operand)) |val| { 22482 if (val.isUndef(mod)) 22483 return mod.undefRef(operand_ty); 22484 22485 const vec_len = operand_ty.vectorLen(mod); 22486 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 22487 for (elems, 0..) |*elem, i| { 22488 const elem_val = try val.elemValue(mod, i); 22489 elem.* = try (try elem_val.byteSwap(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod); 22490 } 22491 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 22492 .ty = operand_ty.toIntern(), 22493 .storage = .{ .elems = elems }, 22494 } }))); 22495 } else operand_src; 22496 22497 try sema.requireRuntimeBlock(block, src, runtime_src); 22498 return block.addTyOp(.byte_swap, operand_ty, operand); 22499 }, 22500 else => unreachable, 22501 } 22502 } 22503 22504 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22505 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 22506 const src = inst_data.src(); 22507 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22508 const operand = try sema.resolveInst(inst_data.operand); 22509 const operand_ty = sema.typeOf(operand); 22510 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 22511 22512 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 22513 return Air.internedToRef(val.toIntern()); 22514 } 22515 22516 const mod = sema.mod; 22517 switch (operand_ty.zigTypeTag(mod)) { 22518 .Int => { 22519 const runtime_src = if (try sema.resolveValue(operand)) |val| { 22520 if (val.isUndef(mod)) return mod.undefRef(operand_ty); 22521 const result_val = try val.bitReverse(operand_ty, mod, sema.arena); 22522 return Air.internedToRef(result_val.toIntern()); 22523 } else operand_src; 22524 22525 try sema.requireRuntimeBlock(block, src, runtime_src); 22526 return block.addTyOp(.bit_reverse, operand_ty, operand); 22527 }, 22528 .Vector => { 22529 const runtime_src = if (try sema.resolveValue(operand)) |val| { 22530 if (val.isUndef(mod)) 22531 return mod.undefRef(operand_ty); 22532 22533 const vec_len = operand_ty.vectorLen(mod); 22534 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 22535 for (elems, 0..) |*elem, i| { 22536 const elem_val = try val.elemValue(mod, i); 22537 elem.* = try (try elem_val.bitReverse(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod); 22538 } 22539 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 22540 .ty = operand_ty.toIntern(), 22541 .storage = .{ .elems = elems }, 22542 } }))); 22543 } else operand_src; 22544 22545 try sema.requireRuntimeBlock(block, src, runtime_src); 22546 return block.addTyOp(.bit_reverse, operand_ty, operand); 22547 }, 22548 else => unreachable, 22549 } 22550 } 22551 22552 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22553 const offset = try sema.bitOffsetOf(block, inst); 22554 return sema.mod.intRef(Type.comptime_int, offset); 22555 } 22556 22557 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22558 const offset = try sema.bitOffsetOf(block, inst); 22559 // TODO reminder to make this a compile error for packed structs 22560 return sema.mod.intRef(Type.comptime_int, offset / 8); 22561 } 22562 22563 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 { 22564 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 22565 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 22566 sema.src = src; 22567 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 22568 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 22569 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22570 22571 const ty = try sema.resolveType(block, lhs_src, extra.lhs); 22572 const field_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{ 22573 .needed_comptime_reason = "name of field must be comptime-known", 22574 }); 22575 22576 const mod = sema.mod; 22577 const ip = &mod.intern_pool; 22578 try sema.resolveTypeLayout(ty); 22579 switch (ty.zigTypeTag(mod)) { 22580 .Struct => {}, 22581 else => { 22582 const msg = msg: { 22583 const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(mod)}); 22584 errdefer msg.destroy(sema.gpa); 22585 try sema.addDeclaredHereNote(msg, ty); 22586 break :msg msg; 22587 }; 22588 return sema.failWithOwnedErrorMsg(block, msg); 22589 }, 22590 } 22591 22592 const field_index = if (ty.isTuple(mod)) blk: { 22593 if (ip.stringEqlSlice(field_name, "len")) { 22594 return sema.fail(block, src, "no offset available for 'len' field of tuple", .{}); 22595 } 22596 break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src); 22597 } else try sema.structFieldIndex(block, ty, field_name, rhs_src); 22598 22599 if (ty.structFieldIsComptime(field_index, mod)) { 22600 return sema.fail(block, src, "no offset available for comptime field", .{}); 22601 } 22602 22603 switch (ty.containerLayout(mod)) { 22604 .Packed => { 22605 var bit_sum: u64 = 0; 22606 const struct_type = ip.indexToKey(ty.toIntern()).struct_type; 22607 for (0..struct_type.field_types.len) |i| { 22608 if (i == field_index) { 22609 return bit_sum; 22610 } 22611 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 22612 bit_sum += field_ty.bitSize(mod); 22613 } else unreachable; 22614 }, 22615 else => return ty.structFieldOffset(field_index, mod) * 8, 22616 } 22617 } 22618 22619 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 22620 const mod = sema.mod; 22621 switch (ty.zigTypeTag(mod)) { 22622 .Struct, .Enum, .Union, .Opaque => return, 22623 else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(mod)}), 22624 } 22625 } 22626 22627 /// Returns `true` if the type was a comptime_int. 22628 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { 22629 const mod = sema.mod; 22630 switch (try ty.zigTypeTagOrPoison(mod)) { 22631 .ComptimeInt => return true, 22632 .Int => return false, 22633 else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(mod)}), 22634 } 22635 } 22636 22637 fn checkInvalidPtrArithmetic( 22638 sema: *Sema, 22639 block: *Block, 22640 src: LazySrcLoc, 22641 ty: Type, 22642 ) CompileError!void { 22643 const mod = sema.mod; 22644 switch (try ty.zigTypeTagOrPoison(mod)) { 22645 .Pointer => switch (ty.ptrSize(mod)) { 22646 .One, .Slice => return, 22647 .Many, .C => return sema.fail( 22648 block, 22649 src, 22650 "invalid pointer arithmetic operator", 22651 .{}, 22652 ), 22653 }, 22654 else => return, 22655 } 22656 } 22657 22658 fn checkArithmeticOp( 22659 sema: *Sema, 22660 block: *Block, 22661 src: LazySrcLoc, 22662 scalar_tag: std.builtin.TypeId, 22663 lhs_zig_ty_tag: std.builtin.TypeId, 22664 rhs_zig_ty_tag: std.builtin.TypeId, 22665 zir_tag: Zir.Inst.Tag, 22666 ) CompileError!void { 22667 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 22668 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; 22669 22670 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { 22671 return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ 22672 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), 22673 }); 22674 } 22675 } 22676 22677 fn checkPtrOperand( 22678 sema: *Sema, 22679 block: *Block, 22680 ty_src: LazySrcLoc, 22681 ty: Type, 22682 ) CompileError!void { 22683 const mod = sema.mod; 22684 switch (ty.zigTypeTag(mod)) { 22685 .Pointer => return, 22686 .Fn => { 22687 const msg = msg: { 22688 const msg = try sema.errMsg( 22689 block, 22690 ty_src, 22691 "expected pointer, found '{}'", 22692 .{ty.fmt(mod)}, 22693 ); 22694 errdefer msg.destroy(sema.gpa); 22695 22696 try sema.errNote(block, ty_src, msg, "use '&' to obtain a function pointer", .{}); 22697 22698 break :msg msg; 22699 }; 22700 return sema.failWithOwnedErrorMsg(block, msg); 22701 }, 22702 .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return, 22703 else => {}, 22704 } 22705 return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)}); 22706 } 22707 22708 fn checkPtrType( 22709 sema: *Sema, 22710 block: *Block, 22711 ty_src: LazySrcLoc, 22712 ty: Type, 22713 allow_slice: bool, 22714 ) CompileError!void { 22715 const mod = sema.mod; 22716 switch (ty.zigTypeTag(mod)) { 22717 .Pointer => if (allow_slice or !ty.isSlice(mod)) return, 22718 .Fn => { 22719 const msg = msg: { 22720 const msg = try sema.errMsg( 22721 block, 22722 ty_src, 22723 "expected pointer type, found '{}'", 22724 .{ty.fmt(mod)}, 22725 ); 22726 errdefer msg.destroy(sema.gpa); 22727 22728 try sema.errNote(block, ty_src, msg, "use '*const ' to make a function pointer type", .{}); 22729 22730 break :msg msg; 22731 }; 22732 return sema.failWithOwnedErrorMsg(block, msg); 22733 }, 22734 .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return, 22735 else => {}, 22736 } 22737 return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)}); 22738 } 22739 22740 fn checkVectorElemType( 22741 sema: *Sema, 22742 block: *Block, 22743 ty_src: LazySrcLoc, 22744 ty: Type, 22745 ) CompileError!void { 22746 const mod = sema.mod; 22747 switch (ty.zigTypeTag(mod)) { 22748 .Int, .Float, .Bool => return, 22749 else => if (ty.isPtrAtRuntime(mod)) return, 22750 } 22751 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)}); 22752 } 22753 22754 fn checkFloatType( 22755 sema: *Sema, 22756 block: *Block, 22757 ty_src: LazySrcLoc, 22758 ty: Type, 22759 ) CompileError!void { 22760 const mod = sema.mod; 22761 switch (ty.zigTypeTag(mod)) { 22762 .ComptimeInt, .ComptimeFloat, .Float => {}, 22763 else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(mod)}), 22764 } 22765 } 22766 22767 fn checkNumericType( 22768 sema: *Sema, 22769 block: *Block, 22770 ty_src: LazySrcLoc, 22771 ty: Type, 22772 ) CompileError!void { 22773 const mod = sema.mod; 22774 switch (ty.zigTypeTag(mod)) { 22775 .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, 22776 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) { 22777 .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, 22778 else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}), 22779 }, 22780 else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(mod)}), 22781 } 22782 } 22783 22784 /// Returns the casted pointer. 22785 fn checkAtomicPtrOperand( 22786 sema: *Sema, 22787 block: *Block, 22788 elem_ty: Type, 22789 elem_ty_src: LazySrcLoc, 22790 ptr: Air.Inst.Ref, 22791 ptr_src: LazySrcLoc, 22792 ptr_const: bool, 22793 ) CompileError!Air.Inst.Ref { 22794 const mod = sema.mod; 22795 var diag: Module.AtomicPtrAlignmentDiagnostics = .{}; 22796 const alignment = mod.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) { 22797 error.OutOfMemory => return error.OutOfMemory, 22798 error.FloatTooBig => return sema.fail( 22799 block, 22800 elem_ty_src, 22801 "expected {d}-bit float type or smaller; found {d}-bit float type", 22802 .{ diag.max_bits, diag.bits }, 22803 ), 22804 error.IntTooBig => return sema.fail( 22805 block, 22806 elem_ty_src, 22807 "expected {d}-bit integer type or smaller; found {d}-bit integer type", 22808 .{ diag.max_bits, diag.bits }, 22809 ), 22810 error.BadType => return sema.fail( 22811 block, 22812 elem_ty_src, 22813 "expected bool, integer, float, enum, or pointer type; found '{}'", 22814 .{elem_ty.fmt(mod)}, 22815 ), 22816 }; 22817 22818 var wanted_ptr_data: InternPool.Key.PtrType = .{ 22819 .child = elem_ty.toIntern(), 22820 .flags = .{ 22821 .alignment = alignment, 22822 .is_const = ptr_const, 22823 }, 22824 }; 22825 22826 const ptr_ty = sema.typeOf(ptr); 22827 const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison(mod)) { 22828 .Pointer => ptr_ty.ptrInfo(mod), 22829 else => { 22830 const wanted_ptr_ty = try sema.ptrType(wanted_ptr_data); 22831 _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 22832 unreachable; 22833 }, 22834 }; 22835 22836 wanted_ptr_data.flags.address_space = ptr_data.flags.address_space; 22837 wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero; 22838 wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile; 22839 22840 const wanted_ptr_ty = try sema.ptrType(wanted_ptr_data); 22841 const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 22842 22843 return casted_ptr; 22844 } 22845 22846 fn checkPtrIsNotComptimeMutable( 22847 sema: *Sema, 22848 block: *Block, 22849 ptr_val: Value, 22850 ptr_src: LazySrcLoc, 22851 operand_src: LazySrcLoc, 22852 ) CompileError!void { 22853 _ = operand_src; 22854 if (ptr_val.isComptimeMutablePtr(sema.mod)) { 22855 return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); 22856 } 22857 } 22858 22859 fn checkComptimeVarStore( 22860 sema: *Sema, 22861 block: *Block, 22862 src: LazySrcLoc, 22863 decl_ref_mut: InternPool.Key.Ptr.Addr.MutDecl, 22864 ) CompileError!void { 22865 if (@intFromEnum(decl_ref_mut.runtime_index) < @intFromEnum(block.runtime_index)) { 22866 if (block.runtime_cond) |cond_src| { 22867 const msg = msg: { 22868 const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{}); 22869 errdefer msg.destroy(sema.gpa); 22870 try sema.errNote(block, cond_src, msg, "runtime condition here", .{}); 22871 break :msg msg; 22872 }; 22873 return sema.failWithOwnedErrorMsg(block, msg); 22874 } 22875 if (block.runtime_loop) |loop_src| { 22876 const msg = msg: { 22877 const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{}); 22878 errdefer msg.destroy(sema.gpa); 22879 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{}); 22880 break :msg msg; 22881 }; 22882 return sema.failWithOwnedErrorMsg(block, msg); 22883 } 22884 unreachable; 22885 } 22886 } 22887 22888 fn checkIntOrVector( 22889 sema: *Sema, 22890 block: *Block, 22891 operand: Air.Inst.Ref, 22892 operand_src: LazySrcLoc, 22893 ) CompileError!Type { 22894 const mod = sema.mod; 22895 const operand_ty = sema.typeOf(operand); 22896 switch (try operand_ty.zigTypeTagOrPoison(mod)) { 22897 .Int => return operand_ty, 22898 .Vector => { 22899 const elem_ty = operand_ty.childType(mod); 22900 switch (try elem_ty.zigTypeTagOrPoison(mod)) { 22901 .Int => return elem_ty, 22902 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ 22903 elem_ty.fmt(mod), 22904 }), 22905 } 22906 }, 22907 else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ 22908 operand_ty.fmt(mod), 22909 }), 22910 } 22911 } 22912 22913 fn checkIntOrVectorAllowComptime( 22914 sema: *Sema, 22915 block: *Block, 22916 operand_ty: Type, 22917 operand_src: LazySrcLoc, 22918 ) CompileError!Type { 22919 const mod = sema.mod; 22920 switch (try operand_ty.zigTypeTagOrPoison(mod)) { 22921 .Int, .ComptimeInt => return operand_ty, 22922 .Vector => { 22923 const elem_ty = operand_ty.childType(mod); 22924 switch (try elem_ty.zigTypeTagOrPoison(mod)) { 22925 .Int, .ComptimeInt => return elem_ty, 22926 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ 22927 elem_ty.fmt(mod), 22928 }), 22929 } 22930 }, 22931 else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ 22932 operand_ty.fmt(mod), 22933 }), 22934 } 22935 } 22936 22937 const SimdBinOp = struct { 22938 len: ?usize, 22939 /// Coerced to `result_ty`. 22940 lhs: Air.Inst.Ref, 22941 /// Coerced to `result_ty`. 22942 rhs: Air.Inst.Ref, 22943 lhs_val: ?Value, 22944 rhs_val: ?Value, 22945 /// Only different than `scalar_ty` when it is a vector operation. 22946 result_ty: Type, 22947 scalar_ty: Type, 22948 }; 22949 22950 fn checkSimdBinOp( 22951 sema: *Sema, 22952 block: *Block, 22953 src: LazySrcLoc, 22954 uncasted_lhs: Air.Inst.Ref, 22955 uncasted_rhs: Air.Inst.Ref, 22956 lhs_src: LazySrcLoc, 22957 rhs_src: LazySrcLoc, 22958 ) CompileError!SimdBinOp { 22959 const mod = sema.mod; 22960 const lhs_ty = sema.typeOf(uncasted_lhs); 22961 const rhs_ty = sema.typeOf(uncasted_rhs); 22962 22963 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 22964 const vec_len: ?usize = if (lhs_ty.zigTypeTag(mod) == .Vector) lhs_ty.vectorLen(mod) else null; 22965 const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ 22966 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 22967 }); 22968 const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); 22969 const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); 22970 22971 return SimdBinOp{ 22972 .len = vec_len, 22973 .lhs = lhs, 22974 .rhs = rhs, 22975 .lhs_val = try sema.resolveValue(lhs), 22976 .rhs_val = try sema.resolveValue(rhs), 22977 .result_ty = result_ty, 22978 .scalar_ty = result_ty.scalarType(mod), 22979 }; 22980 } 22981 22982 fn checkVectorizableBinaryOperands( 22983 sema: *Sema, 22984 block: *Block, 22985 src: LazySrcLoc, 22986 lhs_ty: Type, 22987 rhs_ty: Type, 22988 lhs_src: LazySrcLoc, 22989 rhs_src: LazySrcLoc, 22990 ) CompileError!void { 22991 const mod = sema.mod; 22992 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 22993 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 22994 if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return; 22995 22996 const lhs_is_vector = switch (lhs_zig_ty_tag) { 22997 .Vector, .Array => true, 22998 else => false, 22999 }; 23000 const rhs_is_vector = switch (rhs_zig_ty_tag) { 23001 .Vector, .Array => true, 23002 else => false, 23003 }; 23004 23005 if (lhs_is_vector and rhs_is_vector) { 23006 const lhs_len = lhs_ty.arrayLen(mod); 23007 const rhs_len = rhs_ty.arrayLen(mod); 23008 if (lhs_len != rhs_len) { 23009 const msg = msg: { 23010 const msg = try sema.errMsg(block, src, "vector length mismatch", .{}); 23011 errdefer msg.destroy(sema.gpa); 23012 try sema.errNote(block, lhs_src, msg, "length {d} here", .{lhs_len}); 23013 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len}); 23014 break :msg msg; 23015 }; 23016 return sema.failWithOwnedErrorMsg(block, msg); 23017 } 23018 } else { 23019 const msg = msg: { 23020 const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{ 23021 lhs_ty.fmt(mod), rhs_ty.fmt(mod), 23022 }); 23023 errdefer msg.destroy(sema.gpa); 23024 if (lhs_is_vector) { 23025 try sema.errNote(block, lhs_src, msg, "vector here", .{}); 23026 try sema.errNote(block, rhs_src, msg, "scalar here", .{}); 23027 } else { 23028 try sema.errNote(block, lhs_src, msg, "scalar here", .{}); 23029 try sema.errNote(block, rhs_src, msg, "vector here", .{}); 23030 } 23031 break :msg msg; 23032 }; 23033 return sema.failWithOwnedErrorMsg(block, msg); 23034 } 23035 } 23036 23037 fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc { 23038 if (base_src == .unneeded) return .unneeded; 23039 const mod = sema.mod; 23040 return mod.optionsSrc(mod.declPtr(block.src_decl), base_src, wanted); 23041 } 23042 23043 fn resolveExportOptions( 23044 sema: *Sema, 23045 block: *Block, 23046 src: LazySrcLoc, 23047 zir_ref: Zir.Inst.Ref, 23048 ) CompileError!Module.Export.Options { 23049 const mod = sema.mod; 23050 const gpa = sema.gpa; 23051 const ip = &mod.intern_pool; 23052 const export_options_ty = try sema.getBuiltinType("ExportOptions"); 23053 const air_ref = try sema.resolveInst(zir_ref); 23054 const options = try sema.coerce(block, export_options_ty, air_ref, src); 23055 23056 const name_src = sema.maybeOptionsSrc(block, src, "name"); 23057 const linkage_src = sema.maybeOptionsSrc(block, src, "linkage"); 23058 const section_src = sema.maybeOptionsSrc(block, src, "section"); 23059 const visibility_src = sema.maybeOptionsSrc(block, src, "visibility"); 23060 23061 const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); 23062 const name_val = try sema.resolveConstDefinedValue(block, name_src, name_operand, .{ 23063 .needed_comptime_reason = "name of exported value must be comptime-known", 23064 }); 23065 const name_ty = Type.slice_const_u8; 23066 const name = try name_val.toAllocatedBytes(name_ty, sema.arena, mod); 23067 23068 const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); 23069 const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ 23070 .needed_comptime_reason = "linkage of exported value must be comptime-known", 23071 }); 23072 const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); 23073 23074 const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section"), section_src); 23075 const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ 23076 .needed_comptime_reason = "linksection of exported value must be comptime-known", 23077 }); 23078 const section_ty = Type.slice_const_u8; 23079 const section = if (section_opt_val.optionalValue(mod)) |section_val| 23080 try section_val.toAllocatedBytes(section_ty, sema.arena, mod) 23081 else 23082 null; 23083 23084 const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility"), visibility_src); 23085 const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ 23086 .needed_comptime_reason = "visibility of exported value must be comptime-known", 23087 }); 23088 const visibility = mod.toEnum(std.builtin.SymbolVisibility, visibility_val); 23089 23090 if (name.len < 1) { 23091 return sema.fail(block, name_src, "exported symbol name cannot be empty", .{}); 23092 } 23093 23094 if (visibility != .default and linkage == .Internal) { 23095 return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{ 23096 name, @tagName(visibility), 23097 }); 23098 } 23099 23100 return .{ 23101 .name = try ip.getOrPutString(gpa, name), 23102 .linkage = linkage, 23103 .section = try ip.getOrPutStringOpt(gpa, section), 23104 .visibility = visibility, 23105 }; 23106 } 23107 23108 fn resolveBuiltinEnum( 23109 sema: *Sema, 23110 block: *Block, 23111 src: LazySrcLoc, 23112 zir_ref: Zir.Inst.Ref, 23113 comptime name: []const u8, 23114 reason: NeededComptimeReason, 23115 ) CompileError!@field(std.builtin, name) { 23116 const mod = sema.mod; 23117 const ty = try sema.getBuiltinType(name); 23118 const air_ref = try sema.resolveInst(zir_ref); 23119 const coerced = try sema.coerce(block, ty, air_ref, src); 23120 const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); 23121 return mod.toEnum(@field(std.builtin, name), val); 23122 } 23123 23124 fn resolveAtomicOrder( 23125 sema: *Sema, 23126 block: *Block, 23127 src: LazySrcLoc, 23128 zir_ref: Zir.Inst.Ref, 23129 reason: NeededComptimeReason, 23130 ) CompileError!std.builtin.AtomicOrder { 23131 return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicOrder", reason); 23132 } 23133 23134 fn resolveAtomicRmwOp( 23135 sema: *Sema, 23136 block: *Block, 23137 src: LazySrcLoc, 23138 zir_ref: Zir.Inst.Ref, 23139 ) CompileError!std.builtin.AtomicRmwOp { 23140 return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicRmwOp", .{ 23141 .needed_comptime_reason = "@atomicRmW operation must be comptime-known", 23142 }); 23143 } 23144 23145 fn zirCmpxchg( 23146 sema: *Sema, 23147 block: *Block, 23148 extended: Zir.Inst.Extended.InstData, 23149 ) CompileError!Air.Inst.Ref { 23150 const mod = sema.mod; 23151 const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data; 23152 const air_tag: Air.Inst.Tag = switch (extended.small) { 23153 0 => .cmpxchg_weak, 23154 1 => .cmpxchg_strong, 23155 else => unreachable, 23156 }; 23157 const src = LazySrcLoc.nodeOffset(extra.node); 23158 // zig fmt: off 23159 const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 23160 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 23161 const expected_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; 23162 const new_value_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node }; 23163 const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = extra.node }; 23164 const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = extra.node }; 23165 // zig fmt: on 23166 const expected_value = try sema.resolveInst(extra.expected_value); 23167 const elem_ty = sema.typeOf(expected_value); 23168 if (elem_ty.zigTypeTag(mod) == .Float) { 23169 return sema.fail( 23170 block, 23171 elem_ty_src, 23172 "expected bool, integer, enum, or pointer type; found '{}'", 23173 .{elem_ty.fmt(mod)}, 23174 ); 23175 } 23176 const uncasted_ptr = try sema.resolveInst(extra.ptr); 23177 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 23178 const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); 23179 const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{ 23180 .needed_comptime_reason = "atomic order of cmpxchg success must be comptime-known", 23181 }); 23182 const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{ 23183 .needed_comptime_reason = "atomic order of cmpxchg failure must be comptime-known", 23184 }); 23185 23186 if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) { 23187 return sema.fail(block, success_order_src, "success atomic ordering must be Monotonic or stricter", .{}); 23188 } 23189 if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) { 23190 return sema.fail(block, failure_order_src, "failure atomic ordering must be Monotonic or stricter", .{}); 23191 } 23192 if (@intFromEnum(failure_order) > @intFromEnum(success_order)) { 23193 return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{}); 23194 } 23195 if (failure_order == .Release or failure_order == .AcqRel) { 23196 return sema.fail(block, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{}); 23197 } 23198 23199 const result_ty = try mod.optionalType(elem_ty.toIntern()); 23200 23201 // special case zero bit types 23202 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { 23203 return Air.internedToRef((try mod.intern(.{ .opt = .{ 23204 .ty = result_ty.toIntern(), 23205 .val = .none, 23206 } }))); 23207 } 23208 23209 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 23210 if (try sema.resolveValue(expected_value)) |expected_val| { 23211 if (try sema.resolveValue(new_value)) |new_val| { 23212 if (expected_val.isUndef(mod) or new_val.isUndef(mod)) { 23213 // TODO: this should probably cause the memory stored at the pointer 23214 // to become undef as well 23215 return mod.undefRef(result_ty); 23216 } 23217 const ptr_ty = sema.typeOf(ptr); 23218 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 23219 const result_val = try mod.intern(.{ .opt = .{ 23220 .ty = result_ty.toIntern(), 23221 .val = if (stored_val.eql(expected_val, elem_ty, mod)) blk: { 23222 try sema.storePtr(block, src, ptr, new_value); 23223 break :blk .none; 23224 } else stored_val.toIntern(), 23225 } }); 23226 return Air.internedToRef(result_val); 23227 } else break :rs new_value_src; 23228 } else break :rs expected_src; 23229 } else ptr_src; 23230 23231 const flags: u32 = @as(u32, @intFromEnum(success_order)) | 23232 (@as(u32, @intFromEnum(failure_order)) << 3); 23233 23234 try sema.requireRuntimeBlock(block, src, runtime_src); 23235 return block.addInst(.{ 23236 .tag = air_tag, 23237 .data = .{ .ty_pl = .{ 23238 .ty = Air.internedToRef(result_ty.toIntern()), 23239 .payload = try sema.addExtra(Air.Cmpxchg{ 23240 .ptr = ptr, 23241 .expected_value = expected_value, 23242 .new_value = new_value, 23243 .flags = flags, 23244 }), 23245 } }, 23246 }); 23247 } 23248 23249 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23250 const mod = sema.mod; 23251 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23252 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23253 const src = inst_data.src(); 23254 const scalar_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23255 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat"); 23256 23257 if (!dest_ty.isVector(mod)) return sema.fail(block, src, "expected vector type, found '{}'", .{dest_ty.fmt(mod)}); 23258 23259 const operand = try sema.resolveInst(extra.rhs); 23260 const scalar_ty = dest_ty.childType(mod); 23261 const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src); 23262 if (try sema.resolveValue(scalar)) |scalar_val| { 23263 if (scalar_val.isUndef(mod)) return mod.undefRef(dest_ty); 23264 return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern()); 23265 } 23266 23267 try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src); 23268 return block.addTyOp(.splat, dest_ty, scalar); 23269 } 23270 23271 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23272 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23273 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23274 const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23275 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23276 const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", .{ 23277 .needed_comptime_reason = "@reduce operation must be comptime-known", 23278 }); 23279 const operand = try sema.resolveInst(extra.rhs); 23280 const operand_ty = sema.typeOf(operand); 23281 const mod = sema.mod; 23282 23283 if (operand_ty.zigTypeTag(mod) != .Vector) { 23284 return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(mod)}); 23285 } 23286 23287 const scalar_ty = operand_ty.childType(mod); 23288 23289 // Type-check depending on operation. 23290 switch (operation) { 23291 .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(mod)) { 23292 .Int, .Bool => {}, 23293 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{ 23294 @tagName(operation), operand_ty.fmt(mod), 23295 }), 23296 }, 23297 .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(mod)) { 23298 .Int, .Float => {}, 23299 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{ 23300 @tagName(operation), operand_ty.fmt(mod), 23301 }), 23302 }, 23303 } 23304 23305 const vec_len = operand_ty.vectorLen(mod); 23306 if (vec_len == 0) { 23307 // TODO re-evaluate if we should introduce a "neutral value" for some operations, 23308 // e.g. zero for add and one for mul. 23309 return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{}); 23310 } 23311 23312 if (try sema.resolveValue(operand)) |operand_val| { 23313 if (operand_val.isUndef(mod)) return mod.undefRef(scalar_ty); 23314 23315 var accum: Value = try operand_val.elemValue(mod, 0); 23316 var i: u32 = 1; 23317 while (i < vec_len) : (i += 1) { 23318 const elem_val = try operand_val.elemValue(mod, i); 23319 switch (operation) { 23320 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, mod), 23321 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, mod), 23322 .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, mod), 23323 .Min => accum = accum.numberMin(elem_val, mod), 23324 .Max => accum = accum.numberMax(elem_val, mod), 23325 .Add => accum = try sema.numberAddWrapScalar(accum, elem_val, scalar_ty), 23326 .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, mod), 23327 } 23328 } 23329 return Air.internedToRef(accum.toIntern()); 23330 } 23331 23332 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 23333 return block.addInst(.{ 23334 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 23335 .data = .{ .reduce = .{ 23336 .operand = operand, 23337 .operation = operation, 23338 } }, 23339 }); 23340 } 23341 23342 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23343 const mod = sema.mod; 23344 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23345 const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; 23346 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23347 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 23348 23349 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 23350 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 23351 const a = try sema.resolveInst(extra.a); 23352 const b = try sema.resolveInst(extra.b); 23353 var mask = try sema.resolveInst(extra.mask); 23354 var mask_ty = sema.typeOf(mask); 23355 23356 const mask_len = switch (sema.typeOf(mask).zigTypeTag(mod)) { 23357 .Array, .Vector => sema.typeOf(mask).arrayLen(mod), 23358 else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}), 23359 }; 23360 mask_ty = try mod.vectorType(.{ 23361 .len = @intCast(mask_len), 23362 .child = .i32_type, 23363 }); 23364 mask = try sema.coerce(block, mask_ty, mask, mask_src); 23365 const mask_val = try sema.resolveConstValue(block, mask_src, mask, .{ 23366 .needed_comptime_reason = "shuffle mask must be comptime-known", 23367 }); 23368 return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(mask_len)); 23369 } 23370 23371 fn analyzeShuffle( 23372 sema: *Sema, 23373 block: *Block, 23374 src_node: i32, 23375 elem_ty: Type, 23376 a_arg: Air.Inst.Ref, 23377 b_arg: Air.Inst.Ref, 23378 mask: Value, 23379 mask_len: u32, 23380 ) CompileError!Air.Inst.Ref { 23381 const mod = sema.mod; 23382 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node }; 23383 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node }; 23384 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node }; 23385 var a = a_arg; 23386 var b = b_arg; 23387 23388 const res_ty = try mod.vectorType(.{ 23389 .len = mask_len, 23390 .child = elem_ty.toIntern(), 23391 }); 23392 23393 const maybe_a_len = switch (sema.typeOf(a).zigTypeTag(mod)) { 23394 .Array, .Vector => sema.typeOf(a).arrayLen(mod), 23395 .Undefined => null, 23396 else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{ 23397 elem_ty.fmt(sema.mod), 23398 sema.typeOf(a).fmt(sema.mod), 23399 }), 23400 }; 23401 const maybe_b_len = switch (sema.typeOf(b).zigTypeTag(mod)) { 23402 .Array, .Vector => sema.typeOf(b).arrayLen(mod), 23403 .Undefined => null, 23404 else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{ 23405 elem_ty.fmt(sema.mod), 23406 sema.typeOf(b).fmt(sema.mod), 23407 }), 23408 }; 23409 if (maybe_a_len == null and maybe_b_len == null) { 23410 return mod.undefRef(res_ty); 23411 } 23412 const a_len: u32 = @intCast(maybe_a_len orelse maybe_b_len.?); 23413 const b_len: u32 = @intCast(maybe_b_len orelse a_len); 23414 23415 const a_ty = try mod.vectorType(.{ 23416 .len = a_len, 23417 .child = elem_ty.toIntern(), 23418 }); 23419 const b_ty = try mod.vectorType(.{ 23420 .len = b_len, 23421 .child = elem_ty.toIntern(), 23422 }); 23423 23424 if (maybe_a_len == null) a = try mod.undefRef(a_ty) else a = try sema.coerce(block, a_ty, a, a_src); 23425 if (maybe_b_len == null) b = try mod.undefRef(b_ty) else b = try sema.coerce(block, b_ty, b, b_src); 23426 23427 const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){ 23428 .{ a_len, a_src, a_ty }, 23429 .{ b_len, b_src, b_ty }, 23430 }; 23431 23432 for (0..@intCast(mask_len)) |i| { 23433 const elem = try mask.elemValue(sema.mod, i); 23434 if (elem.isUndef(mod)) continue; 23435 const int = elem.toSignedInt(mod); 23436 var unsigned: u32 = undefined; 23437 var chosen: u32 = undefined; 23438 if (int >= 0) { 23439 unsigned = @intCast(int); 23440 chosen = 0; 23441 } else { 23442 unsigned = @intCast(~int); 23443 chosen = 1; 23444 } 23445 if (unsigned >= operand_info[chosen][0]) { 23446 const msg = msg: { 23447 const msg = try sema.errMsg(block, mask_src, "mask index '{d}' has out-of-bounds selection", .{i}); 23448 errdefer msg.destroy(sema.gpa); 23449 23450 try sema.errNote(block, operand_info[chosen][1], msg, "selected index '{d}' out of bounds of '{}'", .{ 23451 unsigned, 23452 operand_info[chosen][2].fmt(sema.mod), 23453 }); 23454 23455 if (chosen == 0) { 23456 try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{}); 23457 } 23458 23459 break :msg msg; 23460 }; 23461 return sema.failWithOwnedErrorMsg(block, msg); 23462 } 23463 } 23464 23465 if (try sema.resolveValue(a)) |a_val| { 23466 if (try sema.resolveValue(b)) |b_val| { 23467 const values = try sema.arena.alloc(InternPool.Index, mask_len); 23468 for (values, 0..) |*value, i| { 23469 const mask_elem_val = try mask.elemValue(sema.mod, i); 23470 if (mask_elem_val.isUndef(mod)) { 23471 value.* = try mod.intern(.{ .undef = elem_ty.toIntern() }); 23472 continue; 23473 } 23474 const int = mask_elem_val.toSignedInt(mod); 23475 const unsigned: u32 = @intCast(if (int >= 0) int else ~int); 23476 values[i] = try (try (if (int >= 0) a_val else b_val).elemValue(mod, unsigned)).intern(elem_ty, mod); 23477 } 23478 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 23479 .ty = res_ty.toIntern(), 23480 .storage = .{ .elems = values }, 23481 } }))); 23482 } 23483 } 23484 23485 // All static analysis passed, and not comptime. 23486 // For runtime codegen, vectors a and b must be the same length. Here we 23487 // recursively @shuffle the smaller vector to append undefined elements 23488 // to it up to the length of the longer vector. This recursion terminates 23489 // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len. 23490 if (a_len != b_len) { 23491 const min_len = @min(a_len, b_len); 23492 const max_src = if (a_len > b_len) a_src else b_src; 23493 const max_len = try sema.usizeCast(block, max_src, @max(a_len, b_len)); 23494 23495 const expand_mask_values = try sema.arena.alloc(InternPool.Index, max_len); 23496 for (@intCast(0)..@intCast(min_len)) |i| { 23497 expand_mask_values[i] = (try mod.intValue(Type.comptime_int, i)).toIntern(); 23498 } 23499 for (@intCast(min_len)..@intCast(max_len)) |i| { 23500 expand_mask_values[i] = (try mod.intValue(Type.comptime_int, -1)).toIntern(); 23501 } 23502 const expand_mask = try mod.intern(.{ .aggregate = .{ 23503 .ty = (try mod.vectorType(.{ .len = @intCast(max_len), .child = .comptime_int_type })).toIntern(), 23504 .storage = .{ .elems = expand_mask_values }, 23505 } }); 23506 23507 if (a_len < b_len) { 23508 const undef = try mod.undefRef(a_ty); 23509 a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, Value.fromInterned(expand_mask), @intCast(max_len)); 23510 } else { 23511 const undef = try mod.undefRef(b_ty); 23512 b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, Value.fromInterned(expand_mask), @intCast(max_len)); 23513 } 23514 } 23515 23516 return block.addInst(.{ 23517 .tag = .shuffle, 23518 .data = .{ .ty_pl = .{ 23519 .ty = Air.internedToRef(res_ty.toIntern()), 23520 .payload = try block.sema.addExtra(Air.Shuffle{ 23521 .a = a, 23522 .b = b, 23523 .mask = mask.toIntern(), 23524 .mask_len = mask_len, 23525 }), 23526 } }, 23527 }); 23528 } 23529 23530 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 23531 const mod = sema.mod; 23532 const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; 23533 23534 const src = LazySrcLoc.nodeOffset(extra.node); 23535 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 23536 const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 23537 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; 23538 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node }; 23539 23540 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 23541 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 23542 const pred_uncoerced = try sema.resolveInst(extra.pred); 23543 const pred_ty = sema.typeOf(pred_uncoerced); 23544 23545 const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison(mod)) { 23546 .Vector, .Array => pred_ty.arrayLen(mod), 23547 else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(mod)}), 23548 }; 23549 const vec_len: u32 = @intCast(try sema.usizeCast(block, pred_src, vec_len_u64)); 23550 23551 const bool_vec_ty = try mod.vectorType(.{ 23552 .len = vec_len, 23553 .child = .bool_type, 23554 }); 23555 const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); 23556 23557 const vec_ty = try mod.vectorType(.{ 23558 .len = vec_len, 23559 .child = elem_ty.toIntern(), 23560 }); 23561 const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src); 23562 const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src); 23563 23564 const maybe_pred = try sema.resolveValue(pred); 23565 const maybe_a = try sema.resolveValue(a); 23566 const maybe_b = try sema.resolveValue(b); 23567 23568 const runtime_src = if (maybe_pred) |pred_val| rs: { 23569 if (pred_val.isUndef(mod)) return mod.undefRef(vec_ty); 23570 23571 if (maybe_a) |a_val| { 23572 if (a_val.isUndef(mod)) return mod.undefRef(vec_ty); 23573 23574 if (maybe_b) |b_val| { 23575 if (b_val.isUndef(mod)) return mod.undefRef(vec_ty); 23576 23577 const elems = try sema.gpa.alloc(InternPool.Index, vec_len); 23578 defer sema.gpa.free(elems); 23579 for (elems, 0..) |*elem, i| { 23580 const pred_elem_val = try pred_val.elemValue(mod, i); 23581 const should_choose_a = pred_elem_val.toBool(); 23582 elem.* = try (try (if (should_choose_a) a_val else b_val).elemValue(mod, i)).intern(elem_ty, mod); 23583 } 23584 23585 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 23586 .ty = vec_ty.toIntern(), 23587 .storage = .{ .elems = elems }, 23588 } }))); 23589 } else { 23590 break :rs b_src; 23591 } 23592 } else { 23593 if (maybe_b) |b_val| { 23594 if (b_val.isUndef(mod)) return mod.undefRef(vec_ty); 23595 } 23596 break :rs a_src; 23597 } 23598 } else rs: { 23599 if (maybe_a) |a_val| { 23600 if (a_val.isUndef(mod)) return mod.undefRef(vec_ty); 23601 } 23602 if (maybe_b) |b_val| { 23603 if (b_val.isUndef(mod)) return mod.undefRef(vec_ty); 23604 } 23605 break :rs pred_src; 23606 }; 23607 23608 try sema.requireRuntimeBlock(block, src, runtime_src); 23609 return block.addInst(.{ 23610 .tag = .select, 23611 .data = .{ .pl_op = .{ 23612 .operand = pred, 23613 .payload = try block.sema.addExtra(Air.Bin{ 23614 .lhs = a, 23615 .rhs = b, 23616 }), 23617 } }, 23618 }); 23619 } 23620 23621 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23622 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23623 const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; 23624 // zig fmt: off 23625 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23626 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23627 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 23628 // zig fmt: on 23629 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 23630 const uncasted_ptr = try sema.resolveInst(extra.ptr); 23631 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); 23632 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ 23633 .needed_comptime_reason = "atomic order of @atomicLoad must be comptime-known", 23634 }); 23635 23636 switch (order) { 23637 .Release, .AcqRel => { 23638 return sema.fail( 23639 block, 23640 order_src, 23641 "@atomicLoad atomic ordering must not be Release or AcqRel", 23642 .{}, 23643 ); 23644 }, 23645 else => {}, 23646 } 23647 23648 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 23649 return Air.internedToRef(val.toIntern()); 23650 } 23651 23652 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 23653 if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { 23654 return Air.internedToRef(elem_val.toIntern()); 23655 } 23656 } 23657 23658 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); 23659 return block.addInst(.{ 23660 .tag = .atomic_load, 23661 .data = .{ .atomic_load = .{ 23662 .ptr = ptr, 23663 .order = order, 23664 } }, 23665 }); 23666 } 23667 23668 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23669 const mod = sema.mod; 23670 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23671 const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; 23672 const src = inst_data.src(); 23673 // zig fmt: off 23674 const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23675 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23676 const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 23677 const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 23678 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; 23679 // zig fmt: on 23680 const operand = try sema.resolveInst(extra.operand); 23681 const elem_ty = sema.typeOf(operand); 23682 const uncasted_ptr = try sema.resolveInst(extra.ptr); 23683 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 23684 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); 23685 23686 switch (elem_ty.zigTypeTag(mod)) { 23687 .Enum => if (op != .Xchg) { 23688 return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); 23689 }, 23690 .Bool => if (op != .Xchg) { 23691 return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{}); 23692 }, 23693 .Float => switch (op) { 23694 .Xchg, .Add, .Sub, .Max, .Min => {}, 23695 else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}), 23696 }, 23697 else => {}, 23698 } 23699 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ 23700 .needed_comptime_reason = "atomic order of @atomicRmW must be comptime-known", 23701 }); 23702 23703 if (order == .Unordered) { 23704 return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be Unordered", .{}); 23705 } 23706 23707 // special case zero bit types 23708 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 23709 return Air.internedToRef(val.toIntern()); 23710 } 23711 23712 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 23713 const maybe_operand_val = try sema.resolveValue(operand); 23714 const operand_val = maybe_operand_val orelse { 23715 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); 23716 break :rs operand_src; 23717 }; 23718 if (ptr_val.isComptimeMutablePtr(mod)) { 23719 const ptr_ty = sema.typeOf(ptr); 23720 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 23721 const new_val = switch (op) { 23722 // zig fmt: off 23723 .Xchg => operand_val, 23724 .Add => try sema.numberAddWrapScalar(stored_val, operand_val, elem_ty), 23725 .Sub => try sema.numberSubWrapScalar(stored_val, operand_val, elem_ty), 23726 .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, mod), 23727 .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, mod), 23728 .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, mod), 23729 .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, mod), 23730 .Max => stored_val.numberMax (operand_val, mod), 23731 .Min => stored_val.numberMin (operand_val, mod), 23732 // zig fmt: on 23733 }; 23734 try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); 23735 return Air.internedToRef(stored_val.toIntern()); 23736 } else break :rs ptr_src; 23737 } else ptr_src; 23738 23739 const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3); 23740 23741 try sema.requireRuntimeBlock(block, src, runtime_src); 23742 return block.addInst(.{ 23743 .tag = .atomic_rmw, 23744 .data = .{ .pl_op = .{ 23745 .operand = ptr, 23746 .payload = try sema.addExtra(Air.AtomicRmw{ 23747 .operand = operand, 23748 .flags = flags, 23749 }), 23750 } }, 23751 }); 23752 } 23753 23754 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 23755 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23756 const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; 23757 const src = inst_data.src(); 23758 // zig fmt: off 23759 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23760 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23761 const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 23762 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 23763 // zig fmt: on 23764 const operand = try sema.resolveInst(extra.operand); 23765 const elem_ty = sema.typeOf(operand); 23766 const uncasted_ptr = try sema.resolveInst(extra.ptr); 23767 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 23768 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ 23769 .needed_comptime_reason = "atomic order of @atomicStore must be comptime-known", 23770 }); 23771 23772 const air_tag: Air.Inst.Tag = switch (order) { 23773 .Acquire, .AcqRel => { 23774 return sema.fail( 23775 block, 23776 order_src, 23777 "@atomicStore atomic ordering must not be Acquire or AcqRel", 23778 .{}, 23779 ); 23780 }, 23781 .Unordered => .atomic_store_unordered, 23782 .Monotonic => .atomic_store_monotonic, 23783 .Release => .atomic_store_release, 23784 .SeqCst => .atomic_store_seq_cst, 23785 }; 23786 23787 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 23788 } 23789 23790 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23791 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23792 const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data; 23793 const src = inst_data.src(); 23794 23795 const mulend1_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23796 const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 23797 const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 23798 23799 const addend = try sema.resolveInst(extra.addend); 23800 const ty = sema.typeOf(addend); 23801 const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src); 23802 const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src); 23803 23804 const maybe_mulend1 = try sema.resolveValue(mulend1); 23805 const maybe_mulend2 = try sema.resolveValue(mulend2); 23806 const maybe_addend = try sema.resolveValue(addend); 23807 const mod = sema.mod; 23808 23809 switch (ty.scalarType(mod).zigTypeTag(mod)) { 23810 .ComptimeFloat, .Float => {}, 23811 else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}), 23812 } 23813 23814 const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { 23815 if (maybe_mulend2) |mulend2_val| { 23816 if (mulend2_val.isUndef(mod)) return mod.undefRef(ty); 23817 23818 if (maybe_addend) |addend_val| { 23819 if (addend_val.isUndef(mod)) return mod.undefRef(ty); 23820 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, sema.mod); 23821 return Air.internedToRef(result_val.toIntern()); 23822 } else { 23823 break :rs addend_src; 23824 } 23825 } else { 23826 if (maybe_addend) |addend_val| { 23827 if (addend_val.isUndef(mod)) return mod.undefRef(ty); 23828 } 23829 break :rs mulend2_src; 23830 } 23831 } else rs: { 23832 if (maybe_mulend2) |mulend2_val| { 23833 if (mulend2_val.isUndef(mod)) return mod.undefRef(ty); 23834 } 23835 if (maybe_addend) |addend_val| { 23836 if (addend_val.isUndef(mod)) return mod.undefRef(ty); 23837 } 23838 break :rs mulend1_src; 23839 }; 23840 23841 try sema.requireRuntimeBlock(block, src, runtime_src); 23842 return block.addInst(.{ 23843 .tag = .mul_add, 23844 .data = .{ .pl_op = .{ 23845 .operand = addend, 23846 .payload = try sema.addExtra(Air.Bin{ 23847 .lhs = mulend1, 23848 .rhs = mulend2, 23849 }), 23850 } }, 23851 }); 23852 } 23853 23854 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23855 const tracy = trace(@src()); 23856 defer tracy.end(); 23857 23858 const mod = sema.mod; 23859 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23860 const modifier_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23861 const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23862 const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 23863 const call_src = inst_data.src(); 23864 23865 const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; 23866 const func = try sema.resolveInst(extra.callee); 23867 23868 const modifier_ty = try sema.getBuiltinType("CallModifier"); 23869 const air_ref = try sema.resolveInst(extra.modifier); 23870 const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); 23871 const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ 23872 .needed_comptime_reason = "call modifier must be comptime-known", 23873 }); 23874 var modifier = mod.toEnum(std.builtin.CallModifier, modifier_val); 23875 switch (modifier) { 23876 // These can be upgraded to comptime or nosuspend calls. 23877 .auto, .never_tail, .no_async => { 23878 if (block.is_comptime) { 23879 if (modifier == .never_tail) { 23880 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{}); 23881 } 23882 modifier = .compile_time; 23883 } else if (extra.flags.is_nosuspend) { 23884 modifier = .no_async; 23885 } 23886 }, 23887 // These can be upgraded to comptime. nosuspend bit can be safely ignored. 23888 .always_inline, .compile_time => { 23889 _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { 23890 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)}); 23891 }; 23892 23893 if (block.is_comptime) { 23894 modifier = .compile_time; 23895 } 23896 }, 23897 .always_tail => { 23898 if (block.is_comptime) { 23899 modifier = .compile_time; 23900 } 23901 }, 23902 .async_kw => { 23903 if (extra.flags.is_nosuspend) { 23904 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{}); 23905 } 23906 if (block.is_comptime) { 23907 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{}); 23908 } 23909 }, 23910 .never_inline => { 23911 if (block.is_comptime) { 23912 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{}); 23913 } 23914 }, 23915 } 23916 23917 const args = try sema.resolveInst(extra.args); 23918 23919 const args_ty = sema.typeOf(args); 23920 if (!args_ty.isTuple(mod) and args_ty.toIntern() != .empty_struct_type) { 23921 return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)}); 23922 } 23923 23924 const resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(mod)); 23925 for (resolved_args, 0..) |*resolved, i| { 23926 resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(i), args_ty); 23927 } 23928 23929 const callee_ty = sema.typeOf(func); 23930 const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false); 23931 const ensure_result_used = extra.flags.ensure_result_used; 23932 return sema.analyzeCall( 23933 block, 23934 func, 23935 func_ty, 23936 func_src, 23937 call_src, 23938 modifier, 23939 ensure_result_used, 23940 .{ .call_builtin = .{ 23941 .call_node_offset = inst_data.src_node, 23942 .args = resolved_args, 23943 } }, 23944 null, 23945 .@"@call", 23946 ); 23947 } 23948 23949 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23950 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 23951 const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data; 23952 const src = inst_data.src(); 23953 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23954 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23955 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 23956 23957 const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type); 23958 const field_name = try sema.resolveConstStringIntern(block, name_src, extra.field_name, .{ 23959 .needed_comptime_reason = "field name must be comptime-known", 23960 }); 23961 const field_ptr = try sema.resolveInst(extra.field_ptr); 23962 const field_ptr_ty = sema.typeOf(field_ptr); 23963 const mod = sema.mod; 23964 const ip = &mod.intern_pool; 23965 23966 if (parent_ty.zigTypeTag(mod) != .Struct and parent_ty.zigTypeTag(mod) != .Union) { 23967 return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)}); 23968 } 23969 try sema.resolveTypeLayout(parent_ty); 23970 23971 const field_index = switch (parent_ty.zigTypeTag(mod)) { 23972 .Struct => blk: { 23973 if (parent_ty.isTuple(mod)) { 23974 if (ip.stringEqlSlice(field_name, "len")) { 23975 return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); 23976 } 23977 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src); 23978 } else { 23979 break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src); 23980 } 23981 }, 23982 .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src), 23983 else => unreachable, 23984 }; 23985 23986 if (parent_ty.zigTypeTag(mod) == .Struct and parent_ty.structFieldIsComptime(field_index, mod)) { 23987 return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{}); 23988 } 23989 23990 try sema.checkPtrOperand(block, ptr_src, field_ptr_ty); 23991 const field_ptr_ty_info = field_ptr_ty.ptrInfo(mod); 23992 23993 var ptr_ty_data: InternPool.Key.PtrType = .{ 23994 .child = parent_ty.structFieldType(field_index, mod).toIntern(), 23995 .flags = .{ 23996 .address_space = field_ptr_ty_info.flags.address_space, 23997 .is_const = field_ptr_ty_info.flags.is_const, 23998 }, 23999 }; 24000 24001 if (parent_ty.containerLayout(mod) == .Packed) { 24002 return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{}); 24003 } else { 24004 ptr_ty_data.flags.alignment = blk: { 24005 if (mod.typeToStruct(parent_ty)) |struct_type| { 24006 break :blk struct_type.fieldAlign(ip, field_index); 24007 } else if (mod.typeToUnion(parent_ty)) |union_obj| { 24008 break :blk union_obj.fieldAlign(ip, field_index); 24009 } else { 24010 break :blk .none; 24011 } 24012 }; 24013 } 24014 24015 const actual_field_ptr_ty = try sema.ptrType(ptr_ty_data); 24016 const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src); 24017 24018 ptr_ty_data.child = parent_ty.toIntern(); 24019 const result_ptr = try sema.ptrType(ptr_ty_data); 24020 24021 if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { 24022 const field = switch (ip.indexToKey(field_ptr_val.toIntern())) { 24023 .ptr => |ptr| switch (ptr.addr) { 24024 .field => |field| field, 24025 else => null, 24026 }, 24027 else => null, 24028 } orelse return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{}); 24029 24030 if (field.index != field_index) { 24031 const msg = msg: { 24032 const msg = try sema.errMsg( 24033 block, 24034 src, 24035 "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'", 24036 .{ 24037 field_name.fmt(ip), 24038 field_index, 24039 field.index, 24040 parent_ty.fmt(sema.mod), 24041 }, 24042 ); 24043 errdefer msg.destroy(sema.gpa); 24044 try sema.addDeclaredHereNote(msg, parent_ty); 24045 break :msg msg; 24046 }; 24047 return sema.failWithOwnedErrorMsg(block, msg); 24048 } 24049 return Air.internedToRef(field.base); 24050 } 24051 24052 try sema.requireRuntimeBlock(block, src, ptr_src); 24053 try sema.queueFullTypeResolution(result_ptr); 24054 return block.addInst(.{ 24055 .tag = .field_parent_ptr, 24056 .data = .{ .ty_pl = .{ 24057 .ty = Air.internedToRef(result_ptr.toIntern()), 24058 .payload = try block.sema.addExtra(Air.FieldParentPtr{ 24059 .field_ptr = casted_field_ptr, 24060 .field_index = @intCast(field_index), 24061 }), 24062 } }, 24063 }); 24064 } 24065 24066 fn zirMinMax( 24067 sema: *Sema, 24068 block: *Block, 24069 inst: Zir.Inst.Index, 24070 comptime air_tag: Air.Inst.Tag, 24071 ) CompileError!Air.Inst.Ref { 24072 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24073 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 24074 const src = inst_data.src(); 24075 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 24076 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 24077 const lhs = try sema.resolveInst(extra.lhs); 24078 const rhs = try sema.resolveInst(extra.rhs); 24079 try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs)); 24080 try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs)); 24081 return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src }); 24082 } 24083 24084 fn zirMinMaxMulti( 24085 sema: *Sema, 24086 block: *Block, 24087 extended: Zir.Inst.Extended.InstData, 24088 comptime air_tag: Air.Inst.Tag, 24089 ) CompileError!Air.Inst.Ref { 24090 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 24091 const src_node = extra.data.src_node; 24092 const src = LazySrcLoc.nodeOffset(src_node); 24093 const operands = sema.code.refSlice(extra.end, extended.small); 24094 24095 const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 24096 const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len); 24097 24098 for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| { 24099 op_src.* = switch (i) { 24100 0 => .{ .node_offset_builtin_call_arg0 = src_node }, 24101 1 => .{ .node_offset_builtin_call_arg1 = src_node }, 24102 2 => .{ .node_offset_builtin_call_arg2 = src_node }, 24103 3 => .{ .node_offset_builtin_call_arg3 = src_node }, 24104 4 => .{ .node_offset_builtin_call_arg4 = src_node }, 24105 5 => .{ .node_offset_builtin_call_arg5 = src_node }, 24106 else => src, // TODO: better source location 24107 }; 24108 air_ref.* = try sema.resolveInst(zir_ref); 24109 try sema.checkNumericType(block, op_src.*, sema.typeOf(air_ref.*)); 24110 } 24111 24112 return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs); 24113 } 24114 24115 fn analyzeMinMax( 24116 sema: *Sema, 24117 block: *Block, 24118 src: LazySrcLoc, 24119 comptime air_tag: Air.Inst.Tag, 24120 operands: []const Air.Inst.Ref, 24121 operand_srcs: []const LazySrcLoc, 24122 ) CompileError!Air.Inst.Ref { 24123 assert(operands.len == operand_srcs.len); 24124 assert(operands.len > 0); 24125 const mod = sema.mod; 24126 24127 if (operands.len == 1) return operands[0]; 24128 24129 const opFunc = switch (air_tag) { 24130 .min => Value.numberMin, 24131 .max => Value.numberMax, 24132 else => @compileError("unreachable"), 24133 }; 24134 24135 // The set of runtime-known operands. Set up in the loop below. 24136 var runtime_known = try std.DynamicBitSet.initFull(sema.arena, operands.len); 24137 // The current minmax value - initially this will always be comptime-known, then we'll add 24138 // runtime values into the mix later. 24139 var cur_minmax: ?Air.Inst.Ref = null; 24140 var cur_minmax_src: LazySrcLoc = undefined; // defined if cur_minmax not null 24141 // The current known scalar bounds of the value. 24142 var bounds_status: enum { 24143 unknown, // We've only seen undef comptime_ints so far, so do not know the bounds. 24144 defined, // We've seen only integers, so the bounds are defined. 24145 non_integral, // There are floats in the mix, so the bounds aren't defined. 24146 } = .unknown; 24147 var cur_min_scalar: Value = undefined; 24148 var cur_max_scalar: Value = undefined; 24149 24150 // First, find all comptime-known arguments, and get their min/max 24151 24152 for (operands, operand_srcs, 0..) |operand, operand_src, operand_idx| { 24153 // Resolve the value now to avoid redundant calls to `checkSimdBinOp` - we'll have to call 24154 // it in the runtime path anyway since the result type may have been refined 24155 const unresolved_uncoerced_val = try sema.resolveValue(operand) orelse continue; 24156 const uncoerced_val = try sema.resolveLazyValue(unresolved_uncoerced_val); 24157 24158 runtime_known.unset(operand_idx); 24159 24160 switch (bounds_status) { 24161 .unknown, .defined => refine_bounds: { 24162 const ty = sema.typeOf(operand); 24163 if (!ty.scalarType(mod).isInt(mod) and !ty.scalarType(mod).eql(Type.comptime_int, mod)) { 24164 bounds_status = .non_integral; 24165 break :refine_bounds; 24166 } 24167 const scalar_bounds: ?[2]Value = bounds: { 24168 if (!ty.isVector(mod)) break :bounds try uncoerced_val.intValueBounds(mod); 24169 var cur_bounds: [2]Value = try Value.intValueBounds(try uncoerced_val.elemValue(mod, 0), mod) orelse break :bounds null; 24170 const len = try sema.usizeCast(block, src, ty.vectorLen(mod)); 24171 for (1..len) |i| { 24172 const elem = try uncoerced_val.elemValue(mod, i); 24173 const elem_bounds = try elem.intValueBounds(mod) orelse break :bounds null; 24174 cur_bounds = .{ 24175 Value.numberMin(elem_bounds[0], cur_bounds[0], mod), 24176 Value.numberMax(elem_bounds[1], cur_bounds[1], mod), 24177 }; 24178 } 24179 break :bounds cur_bounds; 24180 }; 24181 if (scalar_bounds) |bounds| { 24182 if (bounds_status == .unknown) { 24183 cur_min_scalar = bounds[0]; 24184 cur_max_scalar = bounds[1]; 24185 bounds_status = .defined; 24186 } else { 24187 cur_min_scalar = opFunc(cur_min_scalar, bounds[0], mod); 24188 cur_max_scalar = opFunc(cur_max_scalar, bounds[1], mod); 24189 } 24190 } 24191 }, 24192 .non_integral => {}, 24193 } 24194 24195 const cur = cur_minmax orelse { 24196 cur_minmax = operand; 24197 cur_minmax_src = operand_src; 24198 continue; 24199 }; 24200 24201 const simd_op = try sema.checkSimdBinOp(block, src, cur, operand, cur_minmax_src, operand_src); 24202 const cur_val = try sema.resolveLazyValue(simd_op.lhs_val.?); // cur_minmax is comptime-known 24203 const operand_val = try sema.resolveLazyValue(simd_op.rhs_val.?); // we checked the operand was resolvable above 24204 24205 const vec_len = simd_op.len orelse { 24206 const result_val = opFunc(cur_val, operand_val, mod); 24207 cur_minmax = Air.internedToRef(result_val.toIntern()); 24208 continue; 24209 }; 24210 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 24211 for (elems, 0..) |*elem, i| { 24212 const lhs_elem_val = try cur_val.elemValue(mod, i); 24213 const rhs_elem_val = try operand_val.elemValue(mod, i); 24214 const uncoerced_elem = opFunc(lhs_elem_val, rhs_elem_val, mod); 24215 elem.* = (try mod.getCoerced(uncoerced_elem, simd_op.scalar_ty)).toIntern(); 24216 } 24217 cur_minmax = Air.internedToRef((try mod.intern(.{ .aggregate = .{ 24218 .ty = simd_op.result_ty.toIntern(), 24219 .storage = .{ .elems = elems }, 24220 } }))); 24221 } 24222 24223 const opt_runtime_idx = runtime_known.findFirstSet(); 24224 24225 if (cur_minmax) |ct_minmax_ref| refine: { 24226 // Refine the comptime-known result type based on the bounds. This isn't strictly necessary 24227 // in the runtime case, since we'll refine the type again later, but keeping things as small 24228 // as possible will allow us to emit more optimal AIR (if all the runtime operands have 24229 // smaller types than the non-refined comptime type). 24230 24231 const val = (try sema.resolveValue(ct_minmax_ref)).?; 24232 const orig_ty = sema.typeOf(ct_minmax_ref); 24233 24234 if (opt_runtime_idx == null and orig_ty.scalarType(mod).eql(Type.comptime_int, mod)) { 24235 // If all arguments were `comptime_int`, and there are no runtime args, we'll preserve that type 24236 break :refine; 24237 } 24238 24239 // We can't refine float types 24240 if (orig_ty.scalarType(mod).isAnyFloat()) break :refine; 24241 24242 assert(bounds_status == .defined); // there was a non-comptime-int integral comptime-known arg 24243 24244 const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar); 24245 const refined_ty = if (orig_ty.isVector(mod)) try mod.vectorType(.{ 24246 .len = orig_ty.vectorLen(mod), 24247 .child = refined_scalar_ty.toIntern(), 24248 }) else refined_scalar_ty; 24249 24250 // Apply the refined type to the current value 24251 if (std.debug.runtime_safety) { 24252 assert(try sema.intFitsInType(val, refined_ty, null)); 24253 } 24254 cur_minmax = try sema.coerceInMemory(val, refined_ty); 24255 } 24256 24257 const runtime_idx = opt_runtime_idx orelse return cur_minmax.?; 24258 const runtime_src = operand_srcs[runtime_idx]; 24259 try sema.requireRuntimeBlock(block, src, runtime_src); 24260 24261 // Now, iterate over runtime operands, emitting a min/max instruction for each. We'll refine the 24262 // type again at the end, based on the comptime-known bound. 24263 24264 // If the comptime-known part is undef we can avoid emitting actual instructions later 24265 const known_undef = if (cur_minmax) |operand| blk: { 24266 const val = (try sema.resolveValue(operand)).?; 24267 break :blk val.isUndef(mod); 24268 } else false; 24269 24270 if (cur_minmax == null) { 24271 // No comptime operands - use the first operand as the starting value 24272 assert(bounds_status == .unknown); 24273 assert(runtime_idx == 0); 24274 cur_minmax = operands[0]; 24275 cur_minmax_src = runtime_src; 24276 runtime_known.unset(0); // don't look at this operand in the loop below 24277 const scalar_ty = sema.typeOf(cur_minmax.?).scalarType(mod); 24278 if (scalar_ty.isInt(mod)) { 24279 cur_min_scalar = try scalar_ty.minInt(mod, scalar_ty); 24280 cur_max_scalar = try scalar_ty.maxInt(mod, scalar_ty); 24281 bounds_status = .defined; 24282 } else { 24283 bounds_status = .non_integral; 24284 } 24285 } 24286 24287 var it = runtime_known.iterator(.{}); 24288 while (it.next()) |idx| { 24289 const lhs = cur_minmax.?; 24290 const lhs_src = cur_minmax_src; 24291 const rhs = operands[idx]; 24292 const rhs_src = operand_srcs[idx]; 24293 const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src); 24294 if (known_undef) { 24295 cur_minmax = try mod.undefRef(simd_op.result_ty); 24296 } else { 24297 cur_minmax = try block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); 24298 } 24299 // Compute the bounds of this type 24300 switch (bounds_status) { 24301 .unknown, .defined => refine_bounds: { 24302 const scalar_ty = sema.typeOf(rhs).scalarType(mod); 24303 if (scalar_ty.isAnyFloat()) { 24304 bounds_status = .non_integral; 24305 break :refine_bounds; 24306 } 24307 const scalar_min = try scalar_ty.minInt(mod, scalar_ty); 24308 const scalar_max = try scalar_ty.maxInt(mod, scalar_ty); 24309 if (bounds_status == .unknown) { 24310 cur_min_scalar = scalar_min; 24311 cur_max_scalar = scalar_max; 24312 bounds_status = .defined; 24313 } else { 24314 cur_min_scalar = opFunc(cur_min_scalar, scalar_min, mod); 24315 cur_max_scalar = opFunc(cur_max_scalar, scalar_max, mod); 24316 } 24317 }, 24318 .non_integral => {}, 24319 } 24320 } 24321 24322 // Finally, refine the type based on the known bounds. 24323 const unrefined_ty = sema.typeOf(cur_minmax.?); 24324 if (unrefined_ty.scalarType(mod).isAnyFloat()) { 24325 // We can't refine floats, so we're done. 24326 return cur_minmax.?; 24327 } 24328 assert(bounds_status == .defined); // there were integral runtime operands 24329 const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar); 24330 const refined_ty = if (unrefined_ty.isVector(mod)) try mod.vectorType(.{ 24331 .len = unrefined_ty.vectorLen(mod), 24332 .child = refined_scalar_ty.toIntern(), 24333 }) else refined_scalar_ty; 24334 24335 if (!refined_ty.eql(unrefined_ty, mod)) { 24336 // We've reduced the type - cast the result down 24337 return block.addTyOp(.intcast, refined_ty, cur_minmax.?); 24338 } 24339 24340 return cur_minmax.?; 24341 } 24342 24343 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { 24344 const mod = sema.mod; 24345 const ptr_ty = sema.typeOf(ptr); 24346 const info = ptr_ty.ptrInfo(mod); 24347 if (info.flags.size == .One) { 24348 // Already an array pointer. 24349 return ptr; 24350 } 24351 const new_ty = try sema.ptrType(.{ 24352 .child = (try mod.arrayType(.{ 24353 .len = len, 24354 .sentinel = info.sentinel, 24355 .child = info.child, 24356 })).toIntern(), 24357 .flags = .{ 24358 .alignment = info.flags.alignment, 24359 .is_const = info.flags.is_const, 24360 .is_volatile = info.flags.is_volatile, 24361 .is_allowzero = info.flags.is_allowzero, 24362 .address_space = info.flags.address_space, 24363 }, 24364 }); 24365 const non_slice_ptr = if (info.flags.size == .Slice) 24366 try block.addTyOp(.slice_ptr, ptr_ty.slicePtrFieldType(mod), ptr) 24367 else 24368 ptr; 24369 return block.addBitCast(new_ty, non_slice_ptr); 24370 } 24371 24372 fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 24373 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24374 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 24375 const src = inst_data.src(); 24376 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 24377 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 24378 const dest_ptr = try sema.resolveInst(extra.lhs); 24379 const src_ptr = try sema.resolveInst(extra.rhs); 24380 const dest_ty = sema.typeOf(dest_ptr); 24381 const src_ty = sema.typeOf(src_ptr); 24382 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); 24383 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); 24384 const target = sema.mod.getTarget(); 24385 const mod = sema.mod; 24386 24387 if (dest_ty.isConstPtr(mod)) { 24388 return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{}); 24389 } 24390 24391 if (dest_len == .none and src_len == .none) { 24392 const msg = msg: { 24393 const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{}); 24394 errdefer msg.destroy(sema.gpa); 24395 try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{ 24396 dest_ty.fmt(sema.mod), 24397 }); 24398 try sema.errNote(block, src_src, msg, "source type '{}' provides no length", .{ 24399 src_ty.fmt(sema.mod), 24400 }); 24401 break :msg msg; 24402 }; 24403 return sema.failWithOwnedErrorMsg(block, msg); 24404 } 24405 24406 var len_val: ?Value = null; 24407 24408 if (dest_len != .none and src_len != .none) check: { 24409 // If we can check at compile-time, no need for runtime safety. 24410 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 24411 len_val = dest_len_val; 24412 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 24413 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { 24414 const msg = msg: { 24415 const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{}); 24416 errdefer msg.destroy(sema.gpa); 24417 try sema.errNote(block, dest_src, msg, "length {} here", .{ 24418 dest_len_val.fmtValue(Type.usize, sema.mod), 24419 }); 24420 try sema.errNote(block, src_src, msg, "length {} here", .{ 24421 src_len_val.fmtValue(Type.usize, sema.mod), 24422 }); 24423 break :msg msg; 24424 }; 24425 return sema.failWithOwnedErrorMsg(block, msg); 24426 } 24427 break :check; 24428 } 24429 } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 24430 len_val = src_len_val; 24431 } 24432 24433 if (block.wantSafety()) { 24434 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); 24435 try sema.addSafetyCheck(block, src, ok, .memcpy_len_mismatch); 24436 } 24437 } else if (dest_len != .none) { 24438 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 24439 len_val = dest_len_val; 24440 } 24441 } else if (src_len != .none) { 24442 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 24443 len_val = src_len_val; 24444 } 24445 } 24446 24447 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { 24448 if (!dest_ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src; 24449 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| { 24450 const len_u64 = (try len_val.?.getUnsignedIntAdvanced(mod, sema)).?; 24451 const len = try sema.usizeCast(block, dest_src, len_u64); 24452 for (0..len) |i| { 24453 const elem_index = try mod.intRef(Type.usize, i); 24454 const dest_elem_ptr = try sema.elemPtrOneLayerOnly( 24455 block, 24456 src, 24457 dest_ptr, 24458 elem_index, 24459 src, 24460 true, // init 24461 false, // oob_safety 24462 ); 24463 const src_elem_ptr = try sema.elemPtrOneLayerOnly( 24464 block, 24465 src, 24466 src_ptr, 24467 elem_index, 24468 src, 24469 false, // init 24470 false, // oob_safety 24471 ); 24472 const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src); 24473 try sema.storePtr2( 24474 block, 24475 src, 24476 dest_elem_ptr, 24477 dest_src, 24478 uncoerced_elem, 24479 src_src, 24480 .store, 24481 ); 24482 } 24483 return; 24484 } else break :rs src_src; 24485 } else dest_src; 24486 24487 // If in-memory coercion is not allowed, explode this memcpy call into a 24488 // for loop that copies element-wise. 24489 // Likewise if this is an iterable rather than a pointer, do the same 24490 // lowering. The AIR instruction requires pointers with element types of 24491 // equal ABI size. 24492 24493 if (dest_ty.zigTypeTag(mod) != .Pointer or src_ty.zigTypeTag(mod) != .Pointer) { 24494 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{}); 24495 } 24496 24497 const dest_elem_ty = dest_ty.elemType2(mod); 24498 const src_elem_ty = src_ty.elemType2(mod); 24499 if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) { 24500 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{}); 24501 } 24502 24503 // If the length is comptime-known, then upgrade src and destination types 24504 // into pointer-to-array. At this point we know they are both pointers 24505 // already. 24506 var new_dest_ptr = dest_ptr; 24507 var new_src_ptr = src_ptr; 24508 if (len_val) |val| { 24509 const len = val.toUnsignedInt(mod); 24510 if (len == 0) { 24511 // This AIR instruction guarantees length > 0 if it is comptime-known. 24512 return; 24513 } 24514 new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len); 24515 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len); 24516 } 24517 24518 if (dest_len != .none) { 24519 // Change the src from slice to a many pointer, to avoid multiple ptr 24520 // slice extractions in AIR instructions. 24521 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 24522 if (new_src_ptr_ty.isSlice(mod)) { 24523 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 24524 } 24525 } else if (dest_len == .none and len_val == null) { 24526 // Change the dest to a slice, since its type must have the length. 24527 const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr); 24528 new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, .unneeded, dest_src, dest_src, dest_src, false); 24529 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 24530 if (new_src_ptr_ty.isSlice(mod)) { 24531 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 24532 } 24533 } 24534 24535 try sema.requireRuntimeBlock(block, src, runtime_src); 24536 24537 // Aliasing safety check. 24538 if (block.wantSafety()) { 24539 const len = if (len_val) |v| 24540 Air.internedToRef(v.toIntern()) 24541 else if (dest_len != .none) 24542 dest_len 24543 else 24544 src_len; 24545 24546 // Extract raw pointer from dest slice. The AIR instructions could support them, but 24547 // it would cause redundant machine code instructions. 24548 const new_dest_ptr_ty = sema.typeOf(new_dest_ptr); 24549 const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(mod)) 24550 try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty) 24551 else if (new_dest_ptr_ty.ptrSize(mod) == .One) ptr: { 24552 var dest_manyptr_ty_key = mod.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type; 24553 assert(dest_manyptr_ty_key.flags.size == .One); 24554 dest_manyptr_ty_key.child = dest_elem_ty.toIntern(); 24555 dest_manyptr_ty_key.flags.size = .Many; 24556 break :ptr try sema.coerceCompatiblePtrs(block, try sema.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src); 24557 } else new_dest_ptr; 24558 24559 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 24560 const raw_src_ptr = if (new_src_ptr_ty.isSlice(mod)) 24561 try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty) 24562 else if (new_src_ptr_ty.ptrSize(mod) == .One) ptr: { 24563 var src_manyptr_ty_key = mod.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type; 24564 assert(src_manyptr_ty_key.flags.size == .One); 24565 src_manyptr_ty_key.child = src_elem_ty.toIntern(); 24566 src_manyptr_ty_key.flags.size = .Many; 24567 break :ptr try sema.coerceCompatiblePtrs(block, try sema.ptrType(src_manyptr_ty_key), new_src_ptr, src_src); 24568 } else new_src_ptr; 24569 24570 // ok1: dest >= src + len 24571 // ok2: src >= dest + len 24572 const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src); 24573 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src); 24574 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len); 24575 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len); 24576 const ok = try block.addBinOp(.bool_or, ok1, ok2); 24577 try sema.addSafetyCheck(block, src, ok, .memcpy_alias); 24578 } 24579 24580 _ = try block.addInst(.{ 24581 .tag = .memcpy, 24582 .data = .{ .bin_op = .{ 24583 .lhs = new_dest_ptr, 24584 .rhs = new_src_ptr, 24585 } }, 24586 }); 24587 } 24588 24589 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 24590 const mod = sema.mod; 24591 const gpa = sema.gpa; 24592 const ip = &mod.intern_pool; 24593 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24594 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 24595 const src = inst_data.src(); 24596 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 24597 const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 24598 const dest_ptr = try sema.resolveInst(extra.lhs); 24599 const uncoerced_elem = try sema.resolveInst(extra.rhs); 24600 const dest_ptr_ty = sema.typeOf(dest_ptr); 24601 try checkMemOperand(sema, block, dest_src, dest_ptr_ty); 24602 24603 if (dest_ptr_ty.isConstPtr(mod)) { 24604 return sema.fail(block, dest_src, "cannot memset constant pointer", .{}); 24605 } 24606 24607 const dest_elem_ty: Type = dest_elem_ty: { 24608 const ptr_info = dest_ptr_ty.ptrInfo(mod); 24609 switch (ptr_info.flags.size) { 24610 .Slice => break :dest_elem_ty Type.fromInterned(ptr_info.child), 24611 .One => { 24612 if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Array) { 24613 break :dest_elem_ty Type.fromInterned(ptr_info.child).childType(mod); 24614 } 24615 }, 24616 .Many, .C => {}, 24617 } 24618 return sema.failWithOwnedErrorMsg(block, msg: { 24619 const msg = try sema.errMsg(block, src, "unknown @memset length", .{}); 24620 errdefer msg.destroy(sema.gpa); 24621 try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{ 24622 dest_ptr_ty.fmt(mod), 24623 }); 24624 break :msg msg; 24625 }); 24626 }; 24627 24628 const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); 24629 24630 const runtime_src = rs: { 24631 const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; 24632 const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len"), dest_src); 24633 const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src; 24634 const len_u64 = (try len_val.getUnsignedIntAdvanced(mod, sema)).?; 24635 const len = try sema.usizeCast(block, dest_src, len_u64); 24636 if (len == 0) { 24637 // This AIR instruction guarantees length > 0 if it is comptime-known. 24638 return; 24639 } 24640 24641 if (!ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src; 24642 const elem_val = try sema.resolveValue(elem) orelse break :rs value_src; 24643 const array_ty = try mod.arrayType(.{ 24644 .child = dest_elem_ty.toIntern(), 24645 .len = len_u64, 24646 }); 24647 const array_val = Value.fromInterned((try mod.intern(.{ .aggregate = .{ 24648 .ty = array_ty.toIntern(), 24649 .storage = .{ .repeated_elem = elem_val.toIntern() }, 24650 } }))); 24651 const array_ptr_ty = ty: { 24652 var info = dest_ptr_ty.ptrInfo(mod); 24653 info.flags.size = .One; 24654 info.child = array_ty.toIntern(); 24655 break :ty try mod.ptrType(info); 24656 }; 24657 const raw_ptr_val = if (dest_ptr_ty.isSlice(mod)) ptr_val.slicePtr(mod) else ptr_val; 24658 const array_ptr_val = try mod.getCoerced(raw_ptr_val, array_ptr_ty); 24659 return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty); 24660 }; 24661 24662 try sema.requireRuntimeBlock(block, src, runtime_src); 24663 _ = try block.addInst(.{ 24664 .tag = if (block.wantSafety()) .memset_safe else .memset, 24665 .data = .{ .bin_op = .{ 24666 .lhs = dest_ptr, 24667 .rhs = elem, 24668 } }, 24669 }); 24670 } 24671 24672 fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 24673 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24674 const src = LazySrcLoc.nodeOffset(extra.node); 24675 return sema.failWithUseOfAsync(block, src); 24676 } 24677 24678 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24679 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 24680 const src = inst_data.src(); 24681 return sema.failWithUseOfAsync(block, src); 24682 } 24683 24684 fn zirAwait( 24685 sema: *Sema, 24686 block: *Block, 24687 inst: Zir.Inst.Index, 24688 ) CompileError!Air.Inst.Ref { 24689 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; 24690 const src = inst_data.src(); 24691 24692 return sema.failWithUseOfAsync(block, src); 24693 } 24694 24695 fn zirAwaitNosuspend( 24696 sema: *Sema, 24697 block: *Block, 24698 extended: Zir.Inst.Extended.InstData, 24699 ) CompileError!Air.Inst.Ref { 24700 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24701 const src = LazySrcLoc.nodeOffset(extra.node); 24702 24703 return sema.failWithUseOfAsync(block, src); 24704 } 24705 24706 fn zirVarExtended( 24707 sema: *Sema, 24708 block: *Block, 24709 extended: Zir.Inst.Extended.InstData, 24710 ) CompileError!Air.Inst.Ref { 24711 const mod = sema.mod; 24712 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand); 24713 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 }; 24714 const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 }; 24715 const small: Zir.Inst.ExtendedVar.Small = @bitCast(extended.small); 24716 24717 var extra_index: usize = extra.end; 24718 24719 const lib_name = if (small.has_lib_name) lib_name: { 24720 const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]); 24721 extra_index += 1; 24722 try sema.handleExternLibName(block, ty_src, lib_name); 24723 break :lib_name lib_name; 24724 } else null; 24725 24726 // ZIR supports encoding this information but it is not used; the information 24727 // is encoded via the Decl entry. 24728 assert(!small.has_align); 24729 24730 const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: { 24731 const init_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 24732 extra_index += 1; 24733 break :blk try sema.resolveInst(init_ref); 24734 } else .none; 24735 24736 const have_ty = extra.data.var_type != .none; 24737 const var_ty = if (have_ty) 24738 try sema.resolveType(block, ty_src, extra.data.var_type) 24739 else 24740 sema.typeOf(uncasted_init); 24741 24742 const init_val = if (uncasted_init != .none) blk: { 24743 const init = if (have_ty) 24744 try sema.coerce(block, var_ty, uncasted_init, init_src) 24745 else 24746 uncasted_init; 24747 24748 break :blk ((try sema.resolveValue(init)) orelse { 24749 return sema.failWithNeededComptime(block, init_src, .{ 24750 .needed_comptime_reason = "container level variable initializers must be comptime-known", 24751 }); 24752 }).toIntern(); 24753 } else .none; 24754 24755 try sema.validateVarType(block, ty_src, var_ty, small.is_extern); 24756 24757 return Air.internedToRef((try mod.intern(.{ .variable = .{ 24758 .ty = var_ty.toIntern(), 24759 .init = init_val, 24760 .decl = sema.owner_decl_index, 24761 .lib_name = try mod.intern_pool.getOrPutStringOpt(sema.gpa, lib_name), 24762 .is_extern = small.is_extern, 24763 .is_const = small.is_const, 24764 .is_threadlocal = small.is_threadlocal, 24765 .is_weak_linkage = false, 24766 } }))); 24767 } 24768 24769 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 24770 const tracy = trace(@src()); 24771 defer tracy.end(); 24772 24773 const mod = sema.mod; 24774 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; 24775 const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); 24776 const target = mod.getTarget(); 24777 24778 const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node }; 24779 const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node }; 24780 const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node }; 24781 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; 24782 const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node }; 24783 const has_body = extra.data.body_len != 0; 24784 24785 var extra_index: usize = extra.end; 24786 24787 const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: { 24788 const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]); 24789 extra_index += 1; 24790 break :blk lib_name; 24791 } else null; 24792 24793 if (has_body and 24794 (extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and 24795 !target_util.supportsFunctionAlignment(target)) 24796 { 24797 return sema.fail(block, align_src, "target does not support function alignment", .{}); 24798 } 24799 24800 const @"align": ?Alignment = if (extra.data.bits.has_align_body) blk: { 24801 const body_len = sema.code.extra[extra_index]; 24802 extra_index += 1; 24803 const body = sema.code.bodySlice(extra_index, body_len); 24804 extra_index += body.len; 24805 24806 const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, .{ 24807 .needed_comptime_reason = "alignment must be comptime-known", 24808 }); 24809 if (val.isGenericPoison()) { 24810 break :blk null; 24811 } 24812 const alignment = try sema.validateAlignAllowZero(block, align_src, val.toUnsignedInt(mod)); 24813 const default = target_util.defaultFunctionAlignment(target); 24814 break :blk if (alignment == default) .none else alignment; 24815 } else if (extra.data.bits.has_align_ref) blk: { 24816 const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 24817 extra_index += 1; 24818 const align_tv = sema.resolveInstConst(block, align_src, align_ref, .{ 24819 .needed_comptime_reason = "alignment must be comptime-known", 24820 }) catch |err| switch (err) { 24821 error.GenericPoison => { 24822 break :blk null; 24823 }, 24824 else => |e| return e, 24825 }; 24826 const alignment = try sema.validateAlignAllowZero(block, align_src, align_tv.val.toUnsignedInt(mod)); 24827 const default = target_util.defaultFunctionAlignment(target); 24828 break :blk if (alignment == default) .none else alignment; 24829 } else .none; 24830 24831 const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: { 24832 const body_len = sema.code.extra[extra_index]; 24833 extra_index += 1; 24834 const body = sema.code.bodySlice(extra_index, body_len); 24835 extra_index += body.len; 24836 24837 const addrspace_ty = try sema.getBuiltinType("AddressSpace"); 24838 const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, .{ 24839 .needed_comptime_reason = "addrspace must be comptime-known", 24840 }); 24841 if (val.isGenericPoison()) { 24842 break :blk null; 24843 } 24844 break :blk mod.toEnum(std.builtin.AddressSpace, val); 24845 } else if (extra.data.bits.has_addrspace_ref) blk: { 24846 const addrspace_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 24847 extra_index += 1; 24848 const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref, .{ 24849 .needed_comptime_reason = "addrspace must be comptime-known", 24850 }) catch |err| switch (err) { 24851 error.GenericPoison => { 24852 break :blk null; 24853 }, 24854 else => |e| return e, 24855 }; 24856 break :blk mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); 24857 } else target_util.defaultAddressSpace(target, .function); 24858 24859 const section: Section = if (extra.data.bits.has_section_body) blk: { 24860 const body_len = sema.code.extra[extra_index]; 24861 extra_index += 1; 24862 const body = sema.code.bodySlice(extra_index, body_len); 24863 extra_index += body.len; 24864 24865 const ty = Type.slice_const_u8; 24866 const val = try sema.resolveGenericBody(block, section_src, body, inst, ty, .{ 24867 .needed_comptime_reason = "linksection must be comptime-known", 24868 }); 24869 if (val.isGenericPoison()) { 24870 break :blk .generic; 24871 } 24872 break :blk .{ .explicit = try val.toIpString(ty, mod) }; 24873 } else if (extra.data.bits.has_section_ref) blk: { 24874 const section_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 24875 extra_index += 1; 24876 const section_name = sema.resolveConstStringIntern(block, section_src, section_ref, .{ 24877 .needed_comptime_reason = "linksection must be comptime-known", 24878 }) catch |err| switch (err) { 24879 error.GenericPoison => { 24880 break :blk .generic; 24881 }, 24882 else => |e| return e, 24883 }; 24884 break :blk .{ .explicit = section_name }; 24885 } else .default; 24886 24887 const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: { 24888 const body_len = sema.code.extra[extra_index]; 24889 extra_index += 1; 24890 const body = sema.code.bodySlice(extra_index, body_len); 24891 extra_index += body.len; 24892 24893 const cc_ty = try sema.getBuiltinType("CallingConvention"); 24894 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{ 24895 .needed_comptime_reason = "calling convention must be comptime-known", 24896 }); 24897 if (val.isGenericPoison()) { 24898 break :blk null; 24899 } 24900 break :blk mod.toEnum(std.builtin.CallingConvention, val); 24901 } else if (extra.data.bits.has_cc_ref) blk: { 24902 const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 24903 extra_index += 1; 24904 const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref, .{ 24905 .needed_comptime_reason = "calling convention must be comptime-known", 24906 }) catch |err| switch (err) { 24907 error.GenericPoison => { 24908 break :blk null; 24909 }, 24910 else => |e| return e, 24911 }; 24912 break :blk mod.toEnum(std.builtin.CallingConvention, cc_tv.val); 24913 } else if (sema.owner_decl.is_exported and has_body) 24914 .C 24915 else 24916 .Unspecified; 24917 24918 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { 24919 const body_len = sema.code.extra[extra_index]; 24920 extra_index += 1; 24921 const body = sema.code.bodySlice(extra_index, body_len); 24922 extra_index += body.len; 24923 24924 const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, .{ 24925 .needed_comptime_reason = "return type must be comptime-known", 24926 }); 24927 const ty = val.toType(); 24928 break :blk ty; 24929 } else if (extra.data.bits.has_ret_ty_ref) blk: { 24930 const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); 24931 extra_index += 1; 24932 const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, .{ 24933 .needed_comptime_reason = "return type must be comptime-known", 24934 }) catch |err| switch (err) { 24935 error.GenericPoison => { 24936 break :blk Type.generic_poison; 24937 }, 24938 else => |e| return e, 24939 }; 24940 const ty = ret_ty_tv.val.toType(); 24941 break :blk ty; 24942 } else Type.void; 24943 24944 const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { 24945 const x = sema.code.extra[extra_index]; 24946 extra_index += 1; 24947 break :blk x; 24948 } else 0; 24949 24950 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 24951 if (has_body) { 24952 extra_index += extra.data.body_len; 24953 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 24954 } 24955 24956 const is_var_args = extra.data.bits.is_var_args; 24957 const is_inferred_error = extra.data.bits.is_inferred_error; 24958 const is_extern = extra.data.bits.is_extern; 24959 const is_noinline = extra.data.bits.is_noinline; 24960 24961 return sema.funcCommon( 24962 block, 24963 inst_data.src_node, 24964 inst, 24965 @"align", 24966 @"addrspace", 24967 section, 24968 cc, 24969 ret_ty, 24970 is_var_args, 24971 is_inferred_error, 24972 is_extern, 24973 has_body, 24974 src_locs, 24975 lib_name, 24976 noalias_bits, 24977 is_noinline, 24978 ); 24979 } 24980 24981 fn zirCUndef( 24982 sema: *Sema, 24983 block: *Block, 24984 extended: Zir.Inst.Extended.InstData, 24985 ) CompileError!Air.Inst.Ref { 24986 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24987 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24988 24989 const name = try sema.resolveConstString(block, src, extra.operand, .{ 24990 .needed_comptime_reason = "name of macro being undefined must be comptime-known", 24991 }); 24992 try block.c_import_buf.?.writer().print("#undef {s}\n", .{name}); 24993 return .void_value; 24994 } 24995 24996 fn zirCInclude( 24997 sema: *Sema, 24998 block: *Block, 24999 extended: Zir.Inst.Extended.InstData, 25000 ) CompileError!Air.Inst.Ref { 25001 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25002 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 25003 25004 const name = try sema.resolveConstString(block, src, extra.operand, .{ 25005 .needed_comptime_reason = "path being included must be comptime-known", 25006 }); 25007 try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name}); 25008 return .void_value; 25009 } 25010 25011 fn zirCDefine( 25012 sema: *Sema, 25013 block: *Block, 25014 extended: Zir.Inst.Extended.InstData, 25015 ) CompileError!Air.Inst.Ref { 25016 const mod = sema.mod; 25017 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25018 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 25019 const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 25020 25021 const name = try sema.resolveConstString(block, name_src, extra.lhs, .{ 25022 .needed_comptime_reason = "name of macro being undefined must be comptime-known", 25023 }); 25024 const rhs = try sema.resolveInst(extra.rhs); 25025 if (sema.typeOf(rhs).zigTypeTag(mod) != .Void) { 25026 const value = try sema.resolveConstString(block, val_src, extra.rhs, .{ 25027 .needed_comptime_reason = "value of macro being undefined must be comptime-known", 25028 }); 25029 try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); 25030 } else { 25031 try block.c_import_buf.?.writer().print("#define {s}\n", .{name}); 25032 } 25033 return .void_value; 25034 } 25035 25036 fn zirWasmMemorySize( 25037 sema: *Sema, 25038 block: *Block, 25039 extended: Zir.Inst.Extended.InstData, 25040 ) CompileError!Air.Inst.Ref { 25041 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25042 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 25043 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 25044 const target = sema.mod.getTarget(); 25045 if (!target.isWasm()) { 25046 return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 25047 } 25048 25049 const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.operand, Type.u32, .{ 25050 .needed_comptime_reason = "wasm memory size index must be comptime-known", 25051 })); 25052 try sema.requireRuntimeBlock(block, builtin_src, null); 25053 return block.addInst(.{ 25054 .tag = .wasm_memory_size, 25055 .data = .{ .pl_op = .{ 25056 .operand = .none, 25057 .payload = index, 25058 } }, 25059 }); 25060 } 25061 25062 fn zirWasmMemoryGrow( 25063 sema: *Sema, 25064 block: *Block, 25065 extended: Zir.Inst.Extended.InstData, 25066 ) CompileError!Air.Inst.Ref { 25067 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25068 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 25069 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 25070 const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 25071 const target = sema.mod.getTarget(); 25072 if (!target.isWasm()) { 25073 return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 25074 } 25075 25076 const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, Type.u32, .{ 25077 .needed_comptime_reason = "wasm memory size index must be comptime-known", 25078 })); 25079 const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src); 25080 25081 try sema.requireRuntimeBlock(block, builtin_src, null); 25082 return block.addInst(.{ 25083 .tag = .wasm_memory_grow, 25084 .data = .{ .pl_op = .{ 25085 .operand = delta, 25086 .payload = index, 25087 } }, 25088 }); 25089 } 25090 25091 fn resolvePrefetchOptions( 25092 sema: *Sema, 25093 block: *Block, 25094 src: LazySrcLoc, 25095 zir_ref: Zir.Inst.Ref, 25096 ) CompileError!std.builtin.PrefetchOptions { 25097 const mod = sema.mod; 25098 const gpa = sema.gpa; 25099 const ip = &mod.intern_pool; 25100 const options_ty = try sema.getBuiltinType("PrefetchOptions"); 25101 const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src); 25102 25103 const rw_src = sema.maybeOptionsSrc(block, src, "rw"); 25104 const locality_src = sema.maybeOptionsSrc(block, src, "locality"); 25105 const cache_src = sema.maybeOptionsSrc(block, src, "cache"); 25106 25107 const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw"), rw_src); 25108 const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ 25109 .needed_comptime_reason = "prefetch read/write must be comptime-known", 25110 }); 25111 25112 const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality"), locality_src); 25113 const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ 25114 .needed_comptime_reason = "prefetch locality must be comptime-known", 25115 }); 25116 25117 const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache"), cache_src); 25118 const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ 25119 .needed_comptime_reason = "prefetch cache must be comptime-known", 25120 }); 25121 25122 return std.builtin.PrefetchOptions{ 25123 .rw = mod.toEnum(std.builtin.PrefetchOptions.Rw, rw_val), 25124 .locality = @intCast(locality_val.toUnsignedInt(mod)), 25125 .cache = mod.toEnum(std.builtin.PrefetchOptions.Cache, cache_val), 25126 }; 25127 } 25128 25129 fn zirPrefetch( 25130 sema: *Sema, 25131 block: *Block, 25132 extended: Zir.Inst.Extended.InstData, 25133 ) CompileError!Air.Inst.Ref { 25134 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25135 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 25136 const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 25137 const ptr = try sema.resolveInst(extra.lhs); 25138 try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); 25139 25140 const options = sema.resolvePrefetchOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { 25141 error.NeededSourceLocation => { 25142 _ = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs); 25143 unreachable; 25144 }, 25145 else => |e| return e, 25146 }; 25147 25148 if (!block.is_comptime) { 25149 _ = try block.addInst(.{ 25150 .tag = .prefetch, 25151 .data = .{ .prefetch = .{ 25152 .ptr = ptr, 25153 .rw = options.rw, 25154 .locality = options.locality, 25155 .cache = options.cache, 25156 } }, 25157 }); 25158 } 25159 25160 return .void_value; 25161 } 25162 25163 fn resolveExternOptions( 25164 sema: *Sema, 25165 block: *Block, 25166 src: LazySrcLoc, 25167 zir_ref: Zir.Inst.Ref, 25168 ) CompileError!struct { 25169 name: InternPool.NullTerminatedString, 25170 library_name: InternPool.OptionalNullTerminatedString = .none, 25171 linkage: std.builtin.GlobalLinkage = .Strong, 25172 is_thread_local: bool = false, 25173 } { 25174 const mod = sema.mod; 25175 const gpa = sema.gpa; 25176 const ip = &mod.intern_pool; 25177 const options_inst = try sema.resolveInst(zir_ref); 25178 const extern_options_ty = try sema.getBuiltinType("ExternOptions"); 25179 const options = try sema.coerce(block, extern_options_ty, options_inst, src); 25180 25181 const name_src = sema.maybeOptionsSrc(block, src, "name"); 25182 const library_src = sema.maybeOptionsSrc(block, src, "library"); 25183 const linkage_src = sema.maybeOptionsSrc(block, src, "linkage"); 25184 const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local"); 25185 25186 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); 25187 const name_val = try sema.resolveConstDefinedValue(block, name_src, name_ref, .{ 25188 .needed_comptime_reason = "name of the extern symbol must be comptime-known", 25189 }); 25190 const name = try name_val.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); 25191 25192 const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src); 25193 const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ 25194 .needed_comptime_reason = "library in which extern symbol is must be comptime-known", 25195 }); 25196 25197 const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); 25198 const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ 25199 .needed_comptime_reason = "linkage of the extern symbol must be comptime-known", 25200 }); 25201 const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); 25202 25203 const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local"), thread_local_src); 25204 const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ 25205 .needed_comptime_reason = "threadlocality of the extern symbol must be comptime-known", 25206 }); 25207 25208 const library_name = if (library_name_val.optionalValue(mod)) |library_name_payload| library_name: { 25209 const library_name = try library_name_payload.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); 25210 if (library_name.len == 0) { 25211 return sema.fail(block, library_src, "library name cannot be empty", .{}); 25212 } 25213 try sema.handleExternLibName(block, library_src, library_name); 25214 break :library_name library_name; 25215 } else null; 25216 25217 if (name.len == 0) { 25218 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{}); 25219 } 25220 25221 if (linkage != .Weak and linkage != .Strong) { 25222 return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{}); 25223 } 25224 25225 return .{ 25226 .name = try ip.getOrPutString(gpa, name), 25227 .library_name = try ip.getOrPutStringOpt(gpa, library_name), 25228 .linkage = linkage, 25229 .is_thread_local = is_thread_local_val.toBool(), 25230 }; 25231 } 25232 25233 fn zirBuiltinExtern( 25234 sema: *Sema, 25235 block: *Block, 25236 extended: Zir.Inst.Extended.InstData, 25237 ) CompileError!Air.Inst.Ref { 25238 const mod = sema.mod; 25239 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 25240 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 25241 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 25242 25243 var ty = try sema.resolveType(block, ty_src, extra.lhs); 25244 if (!ty.isPtrAtRuntime(mod)) { 25245 return sema.fail(block, ty_src, "expected (optional) pointer", .{}); 25246 } 25247 if (!try sema.validateExternType(ty, .other)) { 25248 const msg = msg: { 25249 const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)}); 25250 errdefer msg.destroy(sema.gpa); 25251 const src_decl = sema.mod.declPtr(block.src_decl); 25252 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other); 25253 break :msg msg; 25254 }; 25255 return sema.failWithOwnedErrorMsg(block, msg); 25256 } 25257 25258 const options = sema.resolveExternOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { 25259 error.NeededSourceLocation => { 25260 _ = try sema.resolveExternOptions(block, options_src, extra.rhs); 25261 unreachable; 25262 }, 25263 else => |e| return e, 25264 }; 25265 25266 if (options.linkage == .Weak and !ty.ptrAllowsZero(mod)) { 25267 ty = try mod.optionalType(ty.toIntern()); 25268 } 25269 const ptr_info = ty.ptrInfo(mod); 25270 25271 // TODO check duplicate extern 25272 25273 const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, .none); 25274 errdefer mod.destroyDecl(new_decl_index); 25275 const new_decl = mod.declPtr(new_decl_index); 25276 new_decl.name = options.name; 25277 25278 { 25279 const new_var = try mod.intern(.{ .variable = .{ 25280 .ty = ptr_info.child, 25281 .init = .none, 25282 .decl = sema.owner_decl_index, 25283 .lib_name = options.library_name, 25284 .is_extern = true, 25285 .is_const = ptr_info.flags.is_const, 25286 .is_threadlocal = options.is_thread_local, 25287 .is_weak_linkage = options.linkage == .Weak, 25288 } }); 25289 25290 new_decl.src_line = sema.owner_decl.src_line; 25291 // We only access this decl through the decl_ref with the correct type created 25292 // below, so this type doesn't matter 25293 new_decl.ty = Type.fromInterned(ptr_info.child); 25294 new_decl.val = Value.fromInterned(new_var); 25295 new_decl.alignment = .none; 25296 new_decl.@"linksection" = .none; 25297 new_decl.has_tv = true; 25298 new_decl.analysis = .complete; 25299 new_decl.generation = mod.generation; 25300 } 25301 25302 try sema.ensureDeclAnalyzed(new_decl_index); 25303 25304 return Air.internedToRef((try mod.getCoerced(Value.fromInterned((try mod.intern(.{ .ptr = .{ 25305 .ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) { 25306 .ptr_type => ty.toIntern(), 25307 .opt_type => |child_type| child_type, 25308 else => unreachable, 25309 }, 25310 .addr = .{ .decl = new_decl_index }, 25311 } }))), ty)).toIntern()); 25312 } 25313 25314 fn zirWorkItem( 25315 sema: *Sema, 25316 block: *Block, 25317 extended: Zir.Inst.Extended.InstData, 25318 zir_tag: Zir.Inst.Extended, 25319 ) CompileError!Air.Inst.Ref { 25320 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 25321 const dimension_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 25322 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 25323 const target = sema.mod.getTarget(); 25324 25325 switch (target.cpu.arch) { 25326 // TODO: Allow for other GPU targets. 25327 .amdgcn => {}, 25328 else => { 25329 return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)}); 25330 }, 25331 } 25332 25333 const dimension: u32 = @intCast(try sema.resolveInt(block, dimension_src, extra.operand, Type.u32, .{ 25334 .needed_comptime_reason = "dimension must be comptime-known", 25335 })); 25336 try sema.requireRuntimeBlock(block, builtin_src, null); 25337 25338 return block.addInst(.{ 25339 .tag = switch (zir_tag) { 25340 .work_item_id => .work_item_id, 25341 .work_group_size => .work_group_size, 25342 .work_group_id => .work_group_id, 25343 else => unreachable, 25344 }, 25345 .data = .{ .pl_op = .{ 25346 .operand = .none, 25347 .payload = dimension, 25348 } }, 25349 }); 25350 } 25351 25352 fn zirInComptime( 25353 sema: *Sema, 25354 block: *Block, 25355 ) CompileError!Air.Inst.Ref { 25356 _ = sema; 25357 return if (block.is_comptime) .bool_true else .bool_false; 25358 } 25359 25360 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { 25361 if (block.is_comptime) { 25362 const msg = msg: { 25363 const msg = try sema.errMsg(block, src, "unable to evaluate comptime expression", .{}); 25364 errdefer msg.destroy(sema.gpa); 25365 25366 if (runtime_src) |some| { 25367 try sema.errNote(block, some, msg, "operation is runtime due to this operand", .{}); 25368 } 25369 if (block.comptime_reason) |some| { 25370 try some.explain(sema, msg); 25371 } 25372 break :msg msg; 25373 }; 25374 return sema.failWithOwnedErrorMsg(block, msg); 25375 } 25376 } 25377 25378 /// Emit a compile error if type cannot be used for a runtime variable. 25379 fn validateVarType( 25380 sema: *Sema, 25381 block: *Block, 25382 src: LazySrcLoc, 25383 var_ty: Type, 25384 is_extern: bool, 25385 ) CompileError!void { 25386 const mod = sema.mod; 25387 if (is_extern) { 25388 if (!try sema.validateExternType(var_ty, .other)) { 25389 const msg = msg: { 25390 const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)}); 25391 errdefer msg.destroy(sema.gpa); 25392 const src_decl = mod.declPtr(block.src_decl); 25393 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other); 25394 break :msg msg; 25395 }; 25396 return sema.failWithOwnedErrorMsg(block, msg); 25397 } 25398 } else { 25399 if (var_ty.zigTypeTag(mod) == .Opaque) { 25400 return sema.fail( 25401 block, 25402 src, 25403 "non-extern variable with opaque type '{}'", 25404 .{var_ty.fmt(mod)}, 25405 ); 25406 } 25407 } 25408 25409 if (!try sema.typeRequiresComptime(var_ty)) return; 25410 25411 const msg = msg: { 25412 const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)}); 25413 errdefer msg.destroy(sema.gpa); 25414 25415 const src_decl = mod.declPtr(block.src_decl); 25416 try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty); 25417 if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) { 25418 try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{}); 25419 } 25420 25421 break :msg msg; 25422 }; 25423 return sema.failWithOwnedErrorMsg(block, msg); 25424 } 25425 25426 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void); 25427 25428 fn explainWhyTypeIsComptime( 25429 sema: *Sema, 25430 msg: *Module.ErrorMsg, 25431 src_loc: Module.SrcLoc, 25432 ty: Type, 25433 ) CompileError!void { 25434 var type_set = TypeSet{}; 25435 defer type_set.deinit(sema.gpa); 25436 25437 try sema.resolveTypeFully(ty); 25438 return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set); 25439 } 25440 25441 fn explainWhyTypeIsComptimeInner( 25442 sema: *Sema, 25443 msg: *Module.ErrorMsg, 25444 src_loc: Module.SrcLoc, 25445 ty: Type, 25446 type_set: *TypeSet, 25447 ) CompileError!void { 25448 const mod = sema.mod; 25449 const ip = &mod.intern_pool; 25450 switch (ty.zigTypeTag(mod)) { 25451 .Bool, 25452 .Int, 25453 .Float, 25454 .ErrorSet, 25455 .Enum, 25456 .Frame, 25457 .AnyFrame, 25458 .Void, 25459 => return, 25460 25461 .Fn => { 25462 try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{ 25463 ty.fmt(sema.mod), 25464 }); 25465 }, 25466 25467 .Type => { 25468 try mod.errNoteNonLazy(src_loc, msg, "types are not available at runtime", .{}); 25469 }, 25470 25471 .ComptimeFloat, 25472 .ComptimeInt, 25473 .EnumLiteral, 25474 .NoReturn, 25475 .Undefined, 25476 .Null, 25477 => return, 25478 25479 .Opaque => { 25480 try mod.errNoteNonLazy(src_loc, msg, "opaque type '{}' has undefined size", .{ty.fmt(sema.mod)}); 25481 }, 25482 25483 .Array, .Vector => { 25484 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set); 25485 }, 25486 .Pointer => { 25487 const elem_ty = ty.elemType2(mod); 25488 if (elem_ty.zigTypeTag(mod) == .Fn) { 25489 const fn_info = mod.typeToFunc(elem_ty).?; 25490 if (fn_info.is_generic) { 25491 try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{}); 25492 } 25493 switch (fn_info.cc) { 25494 .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}), 25495 else => {}, 25496 } 25497 if (Type.fromInterned(fn_info.return_type).comptimeOnly(mod)) { 25498 try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{}); 25499 } 25500 return; 25501 } 25502 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set); 25503 }, 25504 25505 .Optional => { 25506 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(mod), type_set); 25507 }, 25508 .ErrorUnion => { 25509 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(mod), type_set); 25510 }, 25511 25512 .Struct => { 25513 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 25514 25515 if (mod.typeToStruct(ty)) |struct_type| { 25516 for (0..struct_type.field_types.len) |i| { 25517 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 25518 const field_src_loc = mod.fieldSrcLoc(struct_type.decl.unwrap().?, .{ 25519 .index = i, 25520 .range = .type, 25521 }); 25522 25523 if (try sema.typeRequiresComptime(field_ty)) { 25524 try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{}); 25525 try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field_ty, type_set); 25526 } 25527 } 25528 } 25529 // TODO tuples 25530 }, 25531 25532 .Union => { 25533 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 25534 25535 if (mod.typeToUnion(ty)) |union_obj| { 25536 for (0..union_obj.field_types.len) |i| { 25537 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[i]); 25538 const field_src_loc = mod.fieldSrcLoc(union_obj.decl, .{ 25539 .index = i, 25540 .range = .type, 25541 }); 25542 25543 if (try sema.typeRequiresComptime(field_ty)) { 25544 try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{}); 25545 try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field_ty, type_set); 25546 } 25547 } 25548 } 25549 }, 25550 } 25551 } 25552 25553 const ExternPosition = enum { 25554 ret_ty, 25555 param_ty, 25556 union_field, 25557 struct_field, 25558 element, 25559 other, 25560 }; 25561 25562 /// Returns true if `ty` is allowed in extern types. 25563 /// Does *NOT* require `ty` to be resolved in any way. 25564 /// Calls `resolveTypeLayout` for packed containers. 25565 fn validateExternType( 25566 sema: *Sema, 25567 ty: Type, 25568 position: ExternPosition, 25569 ) !bool { 25570 const mod = sema.mod; 25571 switch (ty.zigTypeTag(mod)) { 25572 .Type, 25573 .ComptimeFloat, 25574 .ComptimeInt, 25575 .EnumLiteral, 25576 .Undefined, 25577 .Null, 25578 .ErrorUnion, 25579 .ErrorSet, 25580 .Frame, 25581 => return false, 25582 .Void => return position == .union_field or position == .ret_ty or position == .struct_field or position == .element, 25583 .NoReturn => return position == .ret_ty, 25584 .Opaque, 25585 .Bool, 25586 .Float, 25587 .AnyFrame, 25588 => return true, 25589 .Pointer => { 25590 if (ty.childType(mod).zigTypeTag(mod) == .Fn) { 25591 return ty.isConstPtr(mod) and try sema.validateExternType(ty.childType(mod), .other); 25592 } 25593 return !(ty.isSlice(mod) or try sema.typeRequiresComptime(ty)); 25594 }, 25595 .Int => switch (ty.intInfo(mod).bits) { 25596 0, 8, 16, 32, 64, 128 => return true, 25597 else => return false, 25598 }, 25599 .Fn => { 25600 if (position != .other) return false; 25601 const target = sema.mod.getTarget(); 25602 // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI. 25603 // The goal is to experiment with more integrated CPU/GPU code. 25604 if (ty.fnCallingConvention(mod) == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) { 25605 return true; 25606 } 25607 return !target_util.fnCallConvAllowsZigTypes(target, ty.fnCallingConvention(mod)); 25608 }, 25609 .Enum => { 25610 return sema.validateExternType(ty.intTagType(mod), position); 25611 }, 25612 .Struct, .Union => switch (ty.containerLayout(mod)) { 25613 .Extern => return true, 25614 .Packed => { 25615 const bit_size = try ty.bitSizeAdvanced(mod, sema); 25616 switch (bit_size) { 25617 0, 8, 16, 32, 64, 128 => return true, 25618 else => return false, 25619 } 25620 }, 25621 .Auto => return !(try sema.typeHasRuntimeBits(ty)), 25622 }, 25623 .Array => { 25624 if (position == .ret_ty or position == .param_ty) return false; 25625 return sema.validateExternType(ty.elemType2(mod), .element); 25626 }, 25627 .Vector => return sema.validateExternType(ty.elemType2(mod), .element), 25628 .Optional => return ty.isPtrLikeOptional(mod), 25629 } 25630 } 25631 25632 fn explainWhyTypeIsNotExtern( 25633 sema: *Sema, 25634 msg: *Module.ErrorMsg, 25635 src_loc: Module.SrcLoc, 25636 ty: Type, 25637 position: ExternPosition, 25638 ) CompileError!void { 25639 const mod = sema.mod; 25640 switch (ty.zigTypeTag(mod)) { 25641 .Opaque, 25642 .Bool, 25643 .Float, 25644 .AnyFrame, 25645 => return, 25646 25647 .Type, 25648 .ComptimeFloat, 25649 .ComptimeInt, 25650 .EnumLiteral, 25651 .Undefined, 25652 .Null, 25653 .ErrorUnion, 25654 .ErrorSet, 25655 .Frame, 25656 => return, 25657 25658 .Pointer => { 25659 if (ty.isSlice(mod)) { 25660 try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); 25661 } else { 25662 const pointee_ty = ty.childType(mod); 25663 if (!ty.isConstPtr(mod) and pointee_ty.zigTypeTag(mod) == .Fn) { 25664 try mod.errNoteNonLazy(src_loc, msg, "pointer to extern function must be 'const'", .{}); 25665 } else if (try sema.typeRequiresComptime(ty)) { 25666 try mod.errNoteNonLazy(src_loc, msg, "pointer to comptime-only type '{}'", .{pointee_ty.fmt(sema.mod)}); 25667 try sema.explainWhyTypeIsComptime(msg, src_loc, ty); 25668 } 25669 try sema.explainWhyTypeIsNotExtern(msg, src_loc, pointee_ty, .other); 25670 } 25671 }, 25672 .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}), 25673 .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), 25674 .Int => if (!std.math.isPowerOfTwo(ty.intInfo(mod).bits)) { 25675 try mod.errNoteNonLazy(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{}); 25676 } else { 25677 try mod.errNoteNonLazy(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{}); 25678 }, 25679 .Fn => { 25680 if (position != .other) { 25681 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 25682 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 25683 return; 25684 } 25685 switch (ty.fnCallingConvention(mod)) { 25686 .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}), 25687 .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}), 25688 .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}), 25689 else => return, 25690 } 25691 }, 25692 .Enum => { 25693 const tag_ty = ty.intTagType(mod); 25694 try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)}); 25695 try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position); 25696 }, 25697 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}), 25698 .Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}), 25699 .Array => { 25700 if (position == .ret_ty) { 25701 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); 25702 } else if (position == .param_ty) { 25703 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); 25704 } 25705 try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element); 25706 }, 25707 .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element), 25708 .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}), 25709 } 25710 } 25711 25712 /// Returns true if `ty` is allowed in packed types. 25713 /// Does not require `ty` to be resolved in any way, but may resolve whether it is comptime-only. 25714 fn validatePackedType(sema: *Sema, ty: Type) !bool { 25715 const mod = sema.mod; 25716 switch (ty.zigTypeTag(mod)) { 25717 .Type, 25718 .ComptimeFloat, 25719 .ComptimeInt, 25720 .EnumLiteral, 25721 .Undefined, 25722 .Null, 25723 .ErrorUnion, 25724 .ErrorSet, 25725 .Frame, 25726 .NoReturn, 25727 .Opaque, 25728 .AnyFrame, 25729 .Fn, 25730 .Array, 25731 => return false, 25732 .Optional => return ty.isPtrLikeOptional(mod), 25733 .Void, 25734 .Bool, 25735 .Float, 25736 .Int, 25737 .Vector, 25738 .Enum, 25739 => return true, 25740 .Pointer => return !ty.isSlice(mod) and !try sema.typeRequiresComptime(ty), 25741 .Struct, .Union => return ty.containerLayout(mod) == .Packed, 25742 } 25743 } 25744 25745 fn explainWhyTypeIsNotPacked( 25746 sema: *Sema, 25747 msg: *Module.ErrorMsg, 25748 src_loc: Module.SrcLoc, 25749 ty: Type, 25750 ) CompileError!void { 25751 const mod = sema.mod; 25752 switch (ty.zigTypeTag(mod)) { 25753 .Void, 25754 .Bool, 25755 .Float, 25756 .Int, 25757 .Vector, 25758 .Enum, 25759 => return, 25760 .Type, 25761 .ComptimeFloat, 25762 .ComptimeInt, 25763 .EnumLiteral, 25764 .Undefined, 25765 .Null, 25766 .Frame, 25767 .NoReturn, 25768 .Opaque, 25769 .ErrorUnion, 25770 .ErrorSet, 25771 .AnyFrame, 25772 .Optional, 25773 .Array, 25774 => try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}), 25775 .Pointer => if (ty.isSlice(mod)) { 25776 try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); 25777 } else { 25778 try mod.errNoteNonLazy(src_loc, msg, "comptime-only pointer has no guaranteed in-memory representation", .{}); 25779 try sema.explainWhyTypeIsComptime(msg, src_loc, ty); 25780 }, 25781 .Fn => { 25782 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 25783 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 25784 }, 25785 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}), 25786 .Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}), 25787 } 25788 } 25789 25790 fn prepareSimplePanic(sema: *Sema, block: *Block) !void { 25791 const mod = sema.mod; 25792 25793 if (mod.panic_func_index == .none) { 25794 const decl_index = (try sema.getBuiltinDecl(block, "panic")); 25795 // decl_index may be an alias; we must find the decl that actually 25796 // owns the function. 25797 try sema.ensureDeclAnalyzed(decl_index); 25798 const tv = try mod.declPtr(decl_index).typedValue(); 25799 assert(tv.ty.zigTypeTag(mod) == .Fn); 25800 assert(try sema.fnHasRuntimeBits(tv.ty)); 25801 const func_index = tv.val.toIntern(); 25802 try mod.ensureFuncBodyAnalysisQueued(func_index); 25803 mod.panic_func_index = func_index; 25804 } 25805 25806 if (mod.null_stack_trace == .none) { 25807 const stack_trace_ty = try sema.getBuiltinType("StackTrace"); 25808 try sema.resolveTypeFields(stack_trace_ty); 25809 const target = mod.getTarget(); 25810 const ptr_stack_trace_ty = try sema.ptrType(.{ 25811 .child = stack_trace_ty.toIntern(), 25812 .flags = .{ 25813 .address_space = target_util.defaultAddressSpace(target, .global_constant), 25814 }, 25815 }); 25816 const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern()); 25817 mod.null_stack_trace = try mod.intern(.{ .opt = .{ 25818 .ty = opt_ptr_stack_trace_ty.toIntern(), 25819 .val = .none, 25820 } }); 25821 } 25822 } 25823 25824 /// Backends depend on panic decls being available when lowering safety-checked 25825 /// instructions. This function ensures the panic function will be available to 25826 /// be called during that time. 25827 fn preparePanicId(sema: *Sema, block: *Block, panic_id: Module.PanicId) !InternPool.DeclIndex { 25828 const mod = sema.mod; 25829 const gpa = sema.gpa; 25830 if (mod.panic_messages[@intFromEnum(panic_id)].unwrap()) |x| return x; 25831 25832 try sema.prepareSimplePanic(block); 25833 25834 const panic_messages_ty = try sema.getBuiltinType("panic_messages"); 25835 const msg_decl_index = (try sema.namespaceLookup( 25836 block, 25837 sema.src, 25838 panic_messages_ty.getNamespaceIndex(mod).unwrap().?, 25839 try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id)), 25840 )).?; 25841 try sema.ensureDeclAnalyzed(msg_decl_index); 25842 mod.panic_messages[@intFromEnum(panic_id)] = msg_decl_index.toOptional(); 25843 return msg_decl_index; 25844 } 25845 25846 fn addSafetyCheck( 25847 sema: *Sema, 25848 parent_block: *Block, 25849 src: LazySrcLoc, 25850 ok: Air.Inst.Ref, 25851 panic_id: Module.PanicId, 25852 ) !void { 25853 const gpa = sema.gpa; 25854 assert(!parent_block.is_comptime); 25855 25856 var fail_block: Block = .{ 25857 .parent = parent_block, 25858 .sema = sema, 25859 .src_decl = parent_block.src_decl, 25860 .namespace = parent_block.namespace, 25861 .wip_capture_scope = parent_block.wip_capture_scope, 25862 .instructions = .{}, 25863 .inlining = parent_block.inlining, 25864 .is_comptime = false, 25865 }; 25866 25867 defer fail_block.instructions.deinit(gpa); 25868 25869 try sema.safetyPanic(&fail_block, src, panic_id); 25870 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 25871 } 25872 25873 fn addSafetyCheckExtra( 25874 sema: *Sema, 25875 parent_block: *Block, 25876 ok: Air.Inst.Ref, 25877 fail_block: *Block, 25878 ) !void { 25879 const gpa = sema.gpa; 25880 25881 try parent_block.instructions.ensureUnusedCapacity(gpa, 1); 25882 25883 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + 25884 1 + // The main block only needs space for the cond_br. 25885 @typeInfo(Air.CondBr).Struct.fields.len + 25886 1 + // The ok branch of the cond_br only needs space for the br. 25887 fail_block.instructions.items.len); 25888 25889 try sema.air_instructions.ensureUnusedCapacity(gpa, 3); 25890 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 25891 const cond_br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1); 25892 const br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(cond_br_inst) + 1); 25893 sema.air_instructions.appendAssumeCapacity(.{ 25894 .tag = .block, 25895 .data = .{ .ty_pl = .{ 25896 .ty = .void_type, 25897 .payload = sema.addExtraAssumeCapacity(Air.Block{ 25898 .body_len = 1, 25899 }), 25900 } }, 25901 }); 25902 sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst)); 25903 25904 sema.air_instructions.appendAssumeCapacity(.{ 25905 .tag = .cond_br, 25906 .data = .{ .pl_op = .{ 25907 .operand = ok, 25908 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 25909 .then_body_len = 1, 25910 .else_body_len = @intCast(fail_block.instructions.items.len), 25911 }), 25912 } }, 25913 }); 25914 sema.air_extra.appendAssumeCapacity(@intFromEnum(br_inst)); 25915 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items)); 25916 25917 sema.air_instructions.appendAssumeCapacity(.{ 25918 .tag = .br, 25919 .data = .{ .br = .{ 25920 .block_inst = block_inst, 25921 .operand = .void_value, 25922 } }, 25923 }); 25924 25925 parent_block.instructions.appendAssumeCapacity(block_inst); 25926 } 25927 25928 fn panicWithMsg(sema: *Sema, block: *Block, src: LazySrcLoc, msg_inst: Air.Inst.Ref, operation: CallOperation) !void { 25929 const mod = sema.mod; 25930 25931 if (!mod.backendSupportsFeature(.panic_fn)) { 25932 _ = try block.addNoOp(.trap); 25933 return; 25934 } 25935 25936 try sema.prepareSimplePanic(block); 25937 25938 const panic_func = mod.funcInfo(mod.panic_func_index); 25939 const panic_fn = try sema.analyzeDeclVal(block, src, panic_func.owner_decl); 25940 const null_stack_trace = Air.internedToRef(mod.null_stack_trace); 25941 25942 const opt_usize_ty = try mod.optionalType(.usize_type); 25943 const null_ret_addr = Air.internedToRef((try mod.intern(.{ .opt = .{ 25944 .ty = opt_usize_ty.toIntern(), 25945 .val = .none, 25946 } }))); 25947 try sema.callBuiltin(block, src, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr }, operation); 25948 } 25949 25950 fn panicUnwrapError( 25951 sema: *Sema, 25952 parent_block: *Block, 25953 src: LazySrcLoc, 25954 operand: Air.Inst.Ref, 25955 unwrap_err_tag: Air.Inst.Tag, 25956 is_non_err_tag: Air.Inst.Tag, 25957 ) !void { 25958 assert(!parent_block.is_comptime); 25959 const ok = try parent_block.addUnOp(is_non_err_tag, operand); 25960 if (!sema.mod.comp.formatted_panics) { 25961 return sema.addSafetyCheck(parent_block, src, ok, .unwrap_error); 25962 } 25963 const gpa = sema.gpa; 25964 25965 var fail_block: Block = .{ 25966 .parent = parent_block, 25967 .sema = sema, 25968 .src_decl = parent_block.src_decl, 25969 .namespace = parent_block.namespace, 25970 .wip_capture_scope = parent_block.wip_capture_scope, 25971 .instructions = .{}, 25972 .inlining = parent_block.inlining, 25973 .is_comptime = false, 25974 }; 25975 25976 defer fail_block.instructions.deinit(gpa); 25977 25978 { 25979 if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) { 25980 _ = try fail_block.addNoOp(.trap); 25981 } else { 25982 const panic_fn = try sema.getBuiltin("panicUnwrapError"); 25983 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); 25984 const err_return_trace = try sema.getErrorReturnTrace(&fail_block); 25985 const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; 25986 try sema.callBuiltin(&fail_block, src, panic_fn, .auto, &args, .@"safety check"); 25987 } 25988 } 25989 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 25990 } 25991 25992 fn panicIndexOutOfBounds( 25993 sema: *Sema, 25994 parent_block: *Block, 25995 src: LazySrcLoc, 25996 index: Air.Inst.Ref, 25997 len: Air.Inst.Ref, 25998 cmp_op: Air.Inst.Tag, 25999 ) !void { 26000 assert(!parent_block.is_comptime); 26001 const ok = try parent_block.addBinOp(cmp_op, index, len); 26002 if (!sema.mod.comp.formatted_panics) { 26003 return sema.addSafetyCheck(parent_block, src, ok, .index_out_of_bounds); 26004 } 26005 try sema.safetyCheckFormatted(parent_block, src, ok, "panicOutOfBounds", &.{ index, len }); 26006 } 26007 26008 fn panicInactiveUnionField( 26009 sema: *Sema, 26010 parent_block: *Block, 26011 src: LazySrcLoc, 26012 active_tag: Air.Inst.Ref, 26013 wanted_tag: Air.Inst.Ref, 26014 ) !void { 26015 assert(!parent_block.is_comptime); 26016 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); 26017 if (!sema.mod.comp.formatted_panics) { 26018 return sema.addSafetyCheck(parent_block, src, ok, .inactive_union_field); 26019 } 26020 try sema.safetyCheckFormatted(parent_block, src, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag }); 26021 } 26022 26023 fn panicSentinelMismatch( 26024 sema: *Sema, 26025 parent_block: *Block, 26026 src: LazySrcLoc, 26027 maybe_sentinel: ?Value, 26028 sentinel_ty: Type, 26029 ptr: Air.Inst.Ref, 26030 sentinel_index: Air.Inst.Ref, 26031 ) !void { 26032 assert(!parent_block.is_comptime); 26033 const mod = sema.mod; 26034 const expected_sentinel_val = maybe_sentinel orelse return; 26035 const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern()); 26036 26037 const ptr_ty = sema.typeOf(ptr); 26038 const actual_sentinel = if (ptr_ty.isSlice(mod)) 26039 try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) 26040 else blk: { 26041 const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null); 26042 const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty); 26043 break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); 26044 }; 26045 26046 const ok = if (sentinel_ty.zigTypeTag(mod) == .Vector) ok: { 26047 const eql = 26048 try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); 26049 break :ok try parent_block.addInst(.{ 26050 .tag = .reduce, 26051 .data = .{ .reduce = .{ 26052 .operand = eql, 26053 .operation = .And, 26054 } }, 26055 }); 26056 } else if (sentinel_ty.isSelfComparable(mod, true)) 26057 try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel) 26058 else { 26059 const panic_fn = try sema.getBuiltin("checkNonScalarSentinel"); 26060 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel }; 26061 try sema.callBuiltin(parent_block, src, panic_fn, .auto, &args, .@"safety check"); 26062 return; 26063 }; 26064 26065 if (!sema.mod.comp.formatted_panics) { 26066 return sema.addSafetyCheck(parent_block, src, ok, .sentinel_mismatch); 26067 } 26068 try sema.safetyCheckFormatted(parent_block, src, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel }); 26069 } 26070 26071 fn safetyCheckFormatted( 26072 sema: *Sema, 26073 parent_block: *Block, 26074 src: LazySrcLoc, 26075 ok: Air.Inst.Ref, 26076 func: []const u8, 26077 args: []const Air.Inst.Ref, 26078 ) CompileError!void { 26079 assert(sema.mod.comp.formatted_panics); 26080 const gpa = sema.gpa; 26081 26082 var fail_block: Block = .{ 26083 .parent = parent_block, 26084 .sema = sema, 26085 .src_decl = parent_block.src_decl, 26086 .namespace = parent_block.namespace, 26087 .wip_capture_scope = parent_block.wip_capture_scope, 26088 .instructions = .{}, 26089 .inlining = parent_block.inlining, 26090 .is_comptime = false, 26091 }; 26092 26093 defer fail_block.instructions.deinit(gpa); 26094 26095 if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) { 26096 _ = try fail_block.addNoOp(.trap); 26097 } else { 26098 const panic_fn = try sema.getBuiltin(func); 26099 try sema.callBuiltin(&fail_block, src, panic_fn, .auto, args, .@"safety check"); 26100 } 26101 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 26102 } 26103 26104 fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Module.PanicId) CompileError!void { 26105 const msg_decl_index = try sema.preparePanicId(block, panic_id); 26106 const msg_inst = try sema.analyzeDeclVal(block, src, msg_decl_index); 26107 try sema.panicWithMsg(block, src, msg_inst, .@"safety check"); 26108 } 26109 26110 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { 26111 sema.branch_count += 1; 26112 if (sema.branch_count > sema.branch_quota) { 26113 const msg = try sema.errMsg( 26114 block, 26115 src, 26116 "evaluation exceeded {d} backwards branches", 26117 .{sema.branch_quota}, 26118 ); 26119 try sema.errNote( 26120 block, 26121 src, 26122 msg, 26123 "use @setEvalBranchQuota() to raise the branch limit from {d}", 26124 .{sema.branch_quota}, 26125 ); 26126 return sema.failWithOwnedErrorMsg(block, msg); 26127 } 26128 } 26129 26130 fn fieldVal( 26131 sema: *Sema, 26132 block: *Block, 26133 src: LazySrcLoc, 26134 object: Air.Inst.Ref, 26135 field_name: InternPool.NullTerminatedString, 26136 field_name_src: LazySrcLoc, 26137 ) CompileError!Air.Inst.Ref { 26138 // When editing this function, note that there is corresponding logic to be edited 26139 // in `fieldPtr`. This function takes a value and returns a value. 26140 26141 const mod = sema.mod; 26142 const ip = &mod.intern_pool; 26143 const object_src = src; // TODO better source location 26144 const object_ty = sema.typeOf(object); 26145 26146 // Zig allows dereferencing a single pointer during field lookup. Note that 26147 // we don't actually need to generate the dereference some field lookups, like the 26148 // length of arrays and other comptime operations. 26149 const is_pointer_to = object_ty.isSinglePointer(mod); 26150 26151 const inner_ty = if (is_pointer_to) 26152 object_ty.childType(mod) 26153 else 26154 object_ty; 26155 26156 switch (inner_ty.zigTypeTag(mod)) { 26157 .Array => { 26158 if (ip.stringEqlSlice(field_name, "len")) { 26159 return Air.internedToRef((try mod.intValue(Type.usize, inner_ty.arrayLen(mod))).toIntern()); 26160 } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) { 26161 const ptr_info = object_ty.ptrInfo(mod); 26162 const result_ty = try sema.ptrType(.{ 26163 .child = Type.fromInterned(ptr_info.child).childType(mod).toIntern(), 26164 .sentinel = if (inner_ty.sentinel(mod)) |s| s.toIntern() else .none, 26165 .flags = .{ 26166 .size = .Many, 26167 .alignment = ptr_info.flags.alignment, 26168 .is_const = ptr_info.flags.is_const, 26169 .is_volatile = ptr_info.flags.is_volatile, 26170 .is_allowzero = ptr_info.flags.is_allowzero, 26171 .address_space = ptr_info.flags.address_space, 26172 .vector_index = ptr_info.flags.vector_index, 26173 }, 26174 .packed_offset = ptr_info.packed_offset, 26175 }); 26176 return sema.coerce(block, result_ty, object, src); 26177 } else { 26178 return sema.fail( 26179 block, 26180 field_name_src, 26181 "no member named '{}' in '{}'", 26182 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 26183 ); 26184 } 26185 }, 26186 .Pointer => { 26187 const ptr_info = inner_ty.ptrInfo(mod); 26188 if (ptr_info.flags.size == .Slice) { 26189 if (ip.stringEqlSlice(field_name, "ptr")) { 26190 const slice = if (is_pointer_to) 26191 try sema.analyzeLoad(block, src, object, object_src) 26192 else 26193 object; 26194 return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); 26195 } else if (ip.stringEqlSlice(field_name, "len")) { 26196 const slice = if (is_pointer_to) 26197 try sema.analyzeLoad(block, src, object, object_src) 26198 else 26199 object; 26200 return sema.analyzeSliceLen(block, src, slice); 26201 } else { 26202 return sema.fail( 26203 block, 26204 field_name_src, 26205 "no member named '{}' in '{}'", 26206 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 26207 ); 26208 } 26209 } 26210 }, 26211 .Type => { 26212 const dereffed_type = if (is_pointer_to) 26213 try sema.analyzeLoad(block, src, object, object_src) 26214 else 26215 object; 26216 26217 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?; 26218 const child_type = val.toType(); 26219 26220 switch (try child_type.zigTypeTagOrPoison(mod)) { 26221 .ErrorSet => { 26222 switch (ip.indexToKey(child_type.toIntern())) { 26223 .error_set_type => |error_set_type| blk: { 26224 if (error_set_type.nameIndex(ip, field_name) != null) break :blk; 26225 const msg = msg: { 26226 const msg = try sema.errMsg(block, src, "no error named '{}' in '{}'", .{ 26227 field_name.fmt(ip), child_type.fmt(mod), 26228 }); 26229 errdefer msg.destroy(sema.gpa); 26230 try sema.addDeclaredHereNote(msg, child_type); 26231 break :msg msg; 26232 }; 26233 return sema.failWithOwnedErrorMsg(block, msg); 26234 }, 26235 .inferred_error_set_type => { 26236 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 26237 }, 26238 .simple_type => |t| { 26239 assert(t == .anyerror); 26240 _ = try mod.getErrorValue(field_name); 26241 }, 26242 else => unreachable, 26243 } 26244 26245 const error_set_type = if (!child_type.isAnyError(mod)) 26246 child_type 26247 else 26248 try mod.singleErrorSetType(field_name); 26249 return Air.internedToRef((try mod.intern(.{ .err = .{ 26250 .ty = error_set_type.toIntern(), 26251 .name = field_name, 26252 } }))); 26253 }, 26254 .Union => { 26255 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 26256 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 26257 return inst; 26258 } 26259 } 26260 try sema.resolveTypeFields(child_type); 26261 if (child_type.unionTagType(mod)) |enum_ty| { 26262 if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| { 26263 const field_index: u32 = @intCast(field_index_usize); 26264 return Air.internedToRef((try mod.enumValueFieldIndex(enum_ty, field_index)).toIntern()); 26265 } 26266 } 26267 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26268 }, 26269 .Enum => { 26270 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 26271 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 26272 return inst; 26273 } 26274 } 26275 const field_index_usize = child_type.enumFieldIndex(field_name, mod) orelse 26276 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26277 const field_index: u32 = @intCast(field_index_usize); 26278 const enum_val = try mod.enumValueFieldIndex(child_type, field_index); 26279 return Air.internedToRef(enum_val.toIntern()); 26280 }, 26281 .Struct, .Opaque => { 26282 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 26283 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 26284 return inst; 26285 } 26286 } 26287 return sema.failWithBadMemberAccess(block, child_type, src, field_name); 26288 }, 26289 else => { 26290 const msg = msg: { 26291 const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(mod)}); 26292 errdefer msg.destroy(sema.gpa); 26293 if (child_type.isSlice(mod)) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{}); 26294 if (child_type.zigTypeTag(mod) == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{}); 26295 break :msg msg; 26296 }; 26297 return sema.failWithOwnedErrorMsg(block, msg); 26298 }, 26299 } 26300 }, 26301 .Struct => if (is_pointer_to) { 26302 // Avoid loading the entire struct by fetching a pointer and loading that 26303 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 26304 return sema.analyzeLoad(block, src, field_ptr, object_src); 26305 } else { 26306 return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty); 26307 }, 26308 .Union => if (is_pointer_to) { 26309 // Avoid loading the entire union by fetching a pointer and loading that 26310 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 26311 return sema.analyzeLoad(block, src, field_ptr, object_src); 26312 } else { 26313 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty); 26314 }, 26315 else => {}, 26316 } 26317 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 26318 } 26319 26320 fn fieldPtr( 26321 sema: *Sema, 26322 block: *Block, 26323 src: LazySrcLoc, 26324 object_ptr: Air.Inst.Ref, 26325 field_name: InternPool.NullTerminatedString, 26326 field_name_src: LazySrcLoc, 26327 initializing: bool, 26328 ) CompileError!Air.Inst.Ref { 26329 // When editing this function, note that there is corresponding logic to be edited 26330 // in `fieldVal`. This function takes a pointer and returns a pointer. 26331 26332 const mod = sema.mod; 26333 const ip = &mod.intern_pool; 26334 const object_ptr_src = src; // TODO better source location 26335 const object_ptr_ty = sema.typeOf(object_ptr); 26336 const object_ty = switch (object_ptr_ty.zigTypeTag(mod)) { 26337 .Pointer => object_ptr_ty.childType(mod), 26338 else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(mod)}), 26339 }; 26340 26341 // Zig allows dereferencing a single pointer during field lookup. Note that 26342 // we don't actually need to generate the dereference some field lookups, like the 26343 // length of arrays and other comptime operations. 26344 const is_pointer_to = object_ty.isSinglePointer(mod); 26345 26346 const inner_ty = if (is_pointer_to) 26347 object_ty.childType(mod) 26348 else 26349 object_ty; 26350 26351 switch (inner_ty.zigTypeTag(mod)) { 26352 .Array => { 26353 if (ip.stringEqlSlice(field_name, "len")) { 26354 const int_val = try mod.intValue(Type.usize, inner_ty.arrayLen(mod)); 26355 return anonDeclRef(sema, int_val.toIntern()); 26356 } else { 26357 return sema.fail( 26358 block, 26359 field_name_src, 26360 "no member named '{}' in '{}'", 26361 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 26362 ); 26363 } 26364 }, 26365 .Pointer => if (inner_ty.isSlice(mod)) { 26366 const inner_ptr = if (is_pointer_to) 26367 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 26368 else 26369 object_ptr; 26370 26371 const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty; 26372 26373 if (ip.stringEqlSlice(field_name, "ptr")) { 26374 const slice_ptr_ty = inner_ty.slicePtrFieldType(mod); 26375 26376 const result_ty = try sema.ptrType(.{ 26377 .child = slice_ptr_ty.toIntern(), 26378 .flags = .{ 26379 .is_const = !attr_ptr_ty.ptrIsMutable(mod), 26380 .is_volatile = attr_ptr_ty.isVolatilePtr(mod), 26381 .address_space = attr_ptr_ty.ptrAddressSpace(mod), 26382 }, 26383 }); 26384 26385 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 26386 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 26387 .ty = result_ty.toIntern(), 26388 .addr = .{ .field = .{ 26389 .base = val.toIntern(), 26390 .index = Value.slice_ptr_index, 26391 } }, 26392 } }))); 26393 } 26394 try sema.requireRuntimeBlock(block, src, null); 26395 26396 const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); 26397 try sema.checkKnownAllocPtr(inner_ptr, field_ptr); 26398 return field_ptr; 26399 } else if (ip.stringEqlSlice(field_name, "len")) { 26400 const result_ty = try sema.ptrType(.{ 26401 .child = .usize_type, 26402 .flags = .{ 26403 .is_const = !attr_ptr_ty.ptrIsMutable(mod), 26404 .is_volatile = attr_ptr_ty.isVolatilePtr(mod), 26405 .address_space = attr_ptr_ty.ptrAddressSpace(mod), 26406 }, 26407 }); 26408 26409 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 26410 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 26411 .ty = result_ty.toIntern(), 26412 .addr = .{ .field = .{ 26413 .base = val.toIntern(), 26414 .index = Value.slice_len_index, 26415 } }, 26416 } }))); 26417 } 26418 try sema.requireRuntimeBlock(block, src, null); 26419 26420 const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); 26421 try sema.checkKnownAllocPtr(inner_ptr, field_ptr); 26422 return field_ptr; 26423 } else { 26424 return sema.fail( 26425 block, 26426 field_name_src, 26427 "no member named '{}' in '{}'", 26428 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 26429 ); 26430 } 26431 }, 26432 .Type => { 26433 _ = try sema.resolveConstDefinedValue(block, .unneeded, object_ptr, undefined); 26434 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); 26435 const inner = if (is_pointer_to) 26436 try sema.analyzeLoad(block, src, result, object_ptr_src) 26437 else 26438 result; 26439 26440 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?; 26441 const child_type = val.toType(); 26442 26443 switch (child_type.zigTypeTag(mod)) { 26444 .ErrorSet => { 26445 switch (ip.indexToKey(child_type.toIntern())) { 26446 .error_set_type => |error_set_type| blk: { 26447 if (error_set_type.nameIndex(ip, field_name) != null) { 26448 break :blk; 26449 } 26450 return sema.fail(block, src, "no error named '{}' in '{}'", .{ 26451 field_name.fmt(ip), child_type.fmt(mod), 26452 }); 26453 }, 26454 .inferred_error_set_type => { 26455 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 26456 }, 26457 .simple_type => |t| { 26458 assert(t == .anyerror); 26459 _ = try mod.getErrorValue(field_name); 26460 }, 26461 else => unreachable, 26462 } 26463 26464 const error_set_type = if (!child_type.isAnyError(mod)) 26465 child_type 26466 else 26467 try mod.singleErrorSetType(field_name); 26468 return anonDeclRef(sema, try mod.intern(.{ .err = .{ 26469 .ty = error_set_type.toIntern(), 26470 .name = field_name, 26471 } })); 26472 }, 26473 .Union => { 26474 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 26475 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 26476 return inst; 26477 } 26478 } 26479 try sema.resolveTypeFields(child_type); 26480 if (child_type.unionTagType(mod)) |enum_ty| { 26481 if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| { 26482 const field_index_u32: u32 = @intCast(field_index); 26483 const idx_val = try mod.enumValueFieldIndex(enum_ty, field_index_u32); 26484 return anonDeclRef(sema, idx_val.toIntern()); 26485 } 26486 } 26487 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26488 }, 26489 .Enum => { 26490 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 26491 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 26492 return inst; 26493 } 26494 } 26495 const field_index = child_type.enumFieldIndex(field_name, mod) orelse { 26496 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26497 }; 26498 const field_index_u32: u32 = @intCast(field_index); 26499 const idx_val = try mod.enumValueFieldIndex(child_type, field_index_u32); 26500 return anonDeclRef(sema, idx_val.toIntern()); 26501 }, 26502 .Struct, .Opaque => { 26503 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 26504 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 26505 return inst; 26506 } 26507 } 26508 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 26509 }, 26510 else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(mod)}), 26511 } 26512 }, 26513 .Struct => { 26514 const inner_ptr = if (is_pointer_to) 26515 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 26516 else 26517 object_ptr; 26518 const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 26519 try sema.checkKnownAllocPtr(inner_ptr, field_ptr); 26520 return field_ptr; 26521 }, 26522 .Union => { 26523 const inner_ptr = if (is_pointer_to) 26524 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 26525 else 26526 object_ptr; 26527 const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 26528 try sema.checkKnownAllocPtr(inner_ptr, field_ptr); 26529 return field_ptr; 26530 }, 26531 else => {}, 26532 } 26533 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 26534 } 26535 26536 const ResolvedFieldCallee = union(enum) { 26537 /// The LHS of the call was an actual field with this value. 26538 direct: Air.Inst.Ref, 26539 /// This is a method call, with the function and first argument given. 26540 method: struct { 26541 func_inst: Air.Inst.Ref, 26542 arg0_inst: Air.Inst.Ref, 26543 }, 26544 }; 26545 26546 fn fieldCallBind( 26547 sema: *Sema, 26548 block: *Block, 26549 src: LazySrcLoc, 26550 raw_ptr: Air.Inst.Ref, 26551 field_name: InternPool.NullTerminatedString, 26552 field_name_src: LazySrcLoc, 26553 ) CompileError!ResolvedFieldCallee { 26554 // When editing this function, note that there is corresponding logic to be edited 26555 // in `fieldVal`. This function takes a pointer and returns a pointer. 26556 26557 const mod = sema.mod; 26558 const ip = &mod.intern_pool; 26559 const raw_ptr_src = src; // TODO better source location 26560 const raw_ptr_ty = sema.typeOf(raw_ptr); 26561 const inner_ty = if (raw_ptr_ty.zigTypeTag(mod) == .Pointer and (raw_ptr_ty.ptrSize(mod) == .One or raw_ptr_ty.ptrSize(mod) == .C)) 26562 raw_ptr_ty.childType(mod) 26563 else 26564 return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(mod)}); 26565 26566 // Optionally dereference a second pointer to get the concrete type. 26567 const is_double_ptr = inner_ty.zigTypeTag(mod) == .Pointer and inner_ty.ptrSize(mod) == .One; 26568 const concrete_ty = if (is_double_ptr) inner_ty.childType(mod) else inner_ty; 26569 const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; 26570 const object_ptr = if (is_double_ptr) 26571 try sema.analyzeLoad(block, src, raw_ptr, src) 26572 else 26573 raw_ptr; 26574 26575 find_field: { 26576 switch (concrete_ty.zigTypeTag(mod)) { 26577 .Struct => { 26578 try sema.resolveTypeFields(concrete_ty); 26579 if (mod.typeToStruct(concrete_ty)) |struct_type| { 26580 const field_index = struct_type.nameIndex(ip, field_name) orelse 26581 break :find_field; 26582 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); 26583 26584 return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr); 26585 } else if (concrete_ty.isTuple(mod)) { 26586 if (ip.stringEqlSlice(field_name, "len")) { 26587 return .{ .direct = try mod.intRef(Type.usize, concrete_ty.structFieldCount(mod)) }; 26588 } 26589 if (field_name.toUnsigned(ip)) |field_index| { 26590 if (field_index >= concrete_ty.structFieldCount(mod)) break :find_field; 26591 return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.structFieldType(field_index, mod), field_index, object_ptr); 26592 } 26593 } else { 26594 const max = concrete_ty.structFieldCount(mod); 26595 for (0..max) |i_usize| { 26596 const i: u32 = @intCast(i_usize); 26597 if (field_name == concrete_ty.structFieldName(i, mod).unwrap().?) { 26598 return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.structFieldType(i, mod), i, object_ptr); 26599 } 26600 } 26601 } 26602 }, 26603 .Union => { 26604 try sema.resolveTypeFields(concrete_ty); 26605 const union_obj = mod.typeToUnion(concrete_ty).?; 26606 const field_index = union_obj.nameIndex(ip, field_name) orelse break :find_field; 26607 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 26608 26609 return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr); 26610 }, 26611 .Type => { 26612 const namespace = try sema.analyzeLoad(block, src, object_ptr, src); 26613 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) }; 26614 }, 26615 else => {}, 26616 } 26617 } 26618 26619 // If we get here, we need to look for a decl in the struct type instead. 26620 const found_decl = switch (concrete_ty.zigTypeTag(mod)) { 26621 .Struct, .Opaque, .Union, .Enum => found_decl: { 26622 if (concrete_ty.getNamespaceIndex(mod).unwrap()) |namespace| { 26623 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| { 26624 try sema.addReferencedBy(block, src, decl_idx); 26625 const decl_val = try sema.analyzeDeclVal(block, src, decl_idx); 26626 const decl_type = sema.typeOf(decl_val); 26627 if (mod.typeToFunc(decl_type)) |func_type| f: { 26628 if (func_type.param_types.len == 0) break :f; 26629 26630 const first_param_type = Type.fromInterned(func_type.param_types.get(ip)[0]); 26631 // zig fmt: off 26632 if (first_param_type.isGenericPoison() or ( 26633 first_param_type.zigTypeTag(mod) == .Pointer and 26634 (first_param_type.ptrSize(mod) == .One or 26635 first_param_type.ptrSize(mod) == .C) and 26636 first_param_type.childType(mod).eql(concrete_ty, mod))) 26637 { 26638 // zig fmt: on 26639 // Note that if the param type is generic poison, we know that it must 26640 // specifically be `anytype` since it's the first parameter, meaning we 26641 // can safely assume it can be a pointer. 26642 // TODO: bound fn calls on rvalues should probably 26643 // generate a by-value argument somehow. 26644 return .{ .method = .{ 26645 .func_inst = decl_val, 26646 .arg0_inst = object_ptr, 26647 } }; 26648 } else if (first_param_type.eql(concrete_ty, mod)) { 26649 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 26650 return .{ .method = .{ 26651 .func_inst = decl_val, 26652 .arg0_inst = deref, 26653 } }; 26654 } else if (first_param_type.zigTypeTag(mod) == .Optional) { 26655 const child = first_param_type.optionalChild(mod); 26656 if (child.eql(concrete_ty, mod)) { 26657 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 26658 return .{ .method = .{ 26659 .func_inst = decl_val, 26660 .arg0_inst = deref, 26661 } }; 26662 } else if (child.zigTypeTag(mod) == .Pointer and 26663 child.ptrSize(mod) == .One and 26664 child.childType(mod).eql(concrete_ty, mod)) 26665 { 26666 return .{ .method = .{ 26667 .func_inst = decl_val, 26668 .arg0_inst = object_ptr, 26669 } }; 26670 } 26671 } else if (first_param_type.zigTypeTag(mod) == .ErrorUnion and 26672 first_param_type.errorUnionPayload(mod).eql(concrete_ty, mod)) 26673 { 26674 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 26675 return .{ .method = .{ 26676 .func_inst = decl_val, 26677 .arg0_inst = deref, 26678 } }; 26679 } 26680 } 26681 break :found_decl decl_idx; 26682 } 26683 } 26684 break :found_decl null; 26685 }, 26686 else => null, 26687 }; 26688 26689 const msg = msg: { 26690 const msg = try sema.errMsg(block, src, "no field or member function named '{}' in '{}'", .{ 26691 field_name.fmt(ip), 26692 concrete_ty.fmt(mod), 26693 }); 26694 errdefer msg.destroy(sema.gpa); 26695 try sema.addDeclaredHereNote(msg, concrete_ty); 26696 if (found_decl) |decl_idx| { 26697 const decl = mod.declPtr(decl_idx); 26698 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{}' is not a member function", .{field_name.fmt(ip)}); 26699 } 26700 break :msg msg; 26701 }; 26702 return sema.failWithOwnedErrorMsg(block, msg); 26703 } 26704 26705 fn finishFieldCallBind( 26706 sema: *Sema, 26707 block: *Block, 26708 src: LazySrcLoc, 26709 ptr_ty: Type, 26710 field_ty: Type, 26711 field_index: u32, 26712 object_ptr: Air.Inst.Ref, 26713 ) CompileError!ResolvedFieldCallee { 26714 const mod = sema.mod; 26715 const ptr_field_ty = try sema.ptrType(.{ 26716 .child = field_ty.toIntern(), 26717 .flags = .{ 26718 .is_const = !ptr_ty.ptrIsMutable(mod), 26719 .address_space = ptr_ty.ptrAddressSpace(mod), 26720 }, 26721 }); 26722 26723 const container_ty = ptr_ty.childType(mod); 26724 if (container_ty.zigTypeTag(mod) == .Struct) { 26725 if (container_ty.structFieldIsComptime(field_index, mod)) { 26726 try sema.resolveStructFieldInits(container_ty); 26727 const default_val = (try container_ty.structFieldValueComptime(mod, field_index)).?; 26728 return .{ .direct = Air.internedToRef(default_val.toIntern()) }; 26729 } 26730 } 26731 26732 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| { 26733 const pointer = Air.internedToRef((try mod.intern(.{ .ptr = .{ 26734 .ty = ptr_field_ty.toIntern(), 26735 .addr = .{ .field = .{ 26736 .base = struct_ptr_val.toIntern(), 26737 .index = field_index, 26738 } }, 26739 } }))); 26740 return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) }; 26741 } 26742 26743 try sema.requireRuntimeBlock(block, src, null); 26744 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); 26745 return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) }; 26746 } 26747 26748 fn namespaceLookup( 26749 sema: *Sema, 26750 block: *Block, 26751 src: LazySrcLoc, 26752 namespace: InternPool.NamespaceIndex, 26753 decl_name: InternPool.NullTerminatedString, 26754 ) CompileError!?InternPool.DeclIndex { 26755 const mod = sema.mod; 26756 const gpa = sema.gpa; 26757 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { 26758 const decl = mod.declPtr(decl_index); 26759 if (!decl.is_pub and decl.getFileScope(mod) != block.getFileScope(mod)) { 26760 const msg = msg: { 26761 const msg = try sema.errMsg(block, src, "'{}' is not marked 'pub'", .{ 26762 decl_name.fmt(&mod.intern_pool), 26763 }); 26764 errdefer msg.destroy(gpa); 26765 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "declared here", .{}); 26766 break :msg msg; 26767 }; 26768 return sema.failWithOwnedErrorMsg(block, msg); 26769 } 26770 return decl_index; 26771 } 26772 return null; 26773 } 26774 26775 fn namespaceLookupRef( 26776 sema: *Sema, 26777 block: *Block, 26778 src: LazySrcLoc, 26779 namespace: InternPool.NamespaceIndex, 26780 decl_name: InternPool.NullTerminatedString, 26781 ) CompileError!?Air.Inst.Ref { 26782 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; 26783 try sema.addReferencedBy(block, src, decl); 26784 return try sema.analyzeDeclRef(decl); 26785 } 26786 26787 fn namespaceLookupVal( 26788 sema: *Sema, 26789 block: *Block, 26790 src: LazySrcLoc, 26791 namespace: InternPool.NamespaceIndex, 26792 decl_name: InternPool.NullTerminatedString, 26793 ) CompileError!?Air.Inst.Ref { 26794 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; 26795 return try sema.analyzeDeclVal(block, src, decl); 26796 } 26797 26798 fn structFieldPtr( 26799 sema: *Sema, 26800 block: *Block, 26801 src: LazySrcLoc, 26802 struct_ptr: Air.Inst.Ref, 26803 field_name: InternPool.NullTerminatedString, 26804 field_name_src: LazySrcLoc, 26805 struct_ty: Type, 26806 initializing: bool, 26807 ) CompileError!Air.Inst.Ref { 26808 const mod = sema.mod; 26809 const ip = &mod.intern_pool; 26810 assert(struct_ty.zigTypeTag(mod) == .Struct); 26811 26812 try sema.resolveTypeFields(struct_ty); 26813 try sema.resolveStructLayout(struct_ty); 26814 26815 if (struct_ty.isTuple(mod)) { 26816 if (ip.stringEqlSlice(field_name, "len")) { 26817 const len_inst = try mod.intRef(Type.usize, struct_ty.structFieldCount(mod)); 26818 return sema.analyzeRef(block, src, len_inst); 26819 } 26820 const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); 26821 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); 26822 } else if (struct_ty.isAnonStruct(mod)) { 26823 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); 26824 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); 26825 } 26826 26827 const struct_type = mod.typeToStruct(struct_ty).?; 26828 26829 const field_index = struct_type.nameIndex(ip, field_name) orelse 26830 return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name); 26831 26832 return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty, initializing); 26833 } 26834 26835 fn structFieldPtrByIndex( 26836 sema: *Sema, 26837 block: *Block, 26838 src: LazySrcLoc, 26839 struct_ptr: Air.Inst.Ref, 26840 field_index: u32, 26841 field_src: LazySrcLoc, 26842 struct_ty: Type, 26843 initializing: bool, 26844 ) CompileError!Air.Inst.Ref { 26845 const mod = sema.mod; 26846 const ip = &mod.intern_pool; 26847 if (struct_ty.isAnonStruct(mod)) { 26848 return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing); 26849 } 26850 26851 const struct_type = mod.typeToStruct(struct_ty).?; 26852 const field_ty = struct_type.field_types.get(ip)[field_index]; 26853 const struct_ptr_ty = sema.typeOf(struct_ptr); 26854 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); 26855 26856 var ptr_ty_data: InternPool.Key.PtrType = .{ 26857 .child = field_ty, 26858 .flags = .{ 26859 .is_const = struct_ptr_ty_info.flags.is_const, 26860 .is_volatile = struct_ptr_ty_info.flags.is_volatile, 26861 .address_space = struct_ptr_ty_info.flags.address_space, 26862 }, 26863 }; 26864 26865 const target = mod.getTarget(); 26866 26867 const parent_align = if (struct_ptr_ty_info.flags.alignment != .none) 26868 struct_ptr_ty_info.flags.alignment 26869 else 26870 try sema.typeAbiAlignment(Type.fromInterned(struct_ptr_ty_info.child)); 26871 26872 if (struct_type.layout == .Packed) { 26873 comptime assert(Type.packed_struct_layout_version == 2); 26874 26875 var running_bits: u16 = 0; 26876 for (0..struct_type.field_types.len) |i| { 26877 const f_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 26878 if (!(try sema.typeHasRuntimeBits(f_ty))) continue; 26879 26880 if (i == field_index) { 26881 ptr_ty_data.packed_offset.bit_offset = running_bits; 26882 } 26883 running_bits += @intCast(f_ty.bitSize(mod)); 26884 } 26885 ptr_ty_data.packed_offset.host_size = (running_bits + 7) / 8; 26886 26887 // If this is a packed struct embedded in another one, we need to offset 26888 // the bits against each other. 26889 if (struct_ptr_ty_info.packed_offset.host_size != 0) { 26890 ptr_ty_data.packed_offset.host_size = struct_ptr_ty_info.packed_offset.host_size; 26891 ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset; 26892 } 26893 26894 ptr_ty_data.flags.alignment = parent_align; 26895 26896 // If the field happens to be byte-aligned, simplify the pointer type. 26897 // The pointee type bit size must match its ABI byte size so that loads and stores 26898 // do not interfere with the surrounding packed bits. 26899 // We do not attempt this with big-endian targets yet because of nested 26900 // structs and floats. I need to double-check the desired behavior for big endian 26901 // targets before adding the necessary complications to this code. This will not 26902 // cause miscompilations; it only means the field pointer uses bit masking when it 26903 // might not be strictly necessary. 26904 if (parent_align != .none and ptr_ty_data.packed_offset.bit_offset % 8 == 0 and 26905 target.cpu.arch.endian() == .little) 26906 { 26907 const elem_size_bytes = try sema.typeAbiSize(Type.fromInterned(ptr_ty_data.child)); 26908 const elem_size_bits = Type.fromInterned(ptr_ty_data.child).bitSize(mod); 26909 if (elem_size_bytes * 8 == elem_size_bits) { 26910 const byte_offset = ptr_ty_data.packed_offset.bit_offset / 8; 26911 const new_align: Alignment = @enumFromInt(@ctz(byte_offset | parent_align.toByteUnitsOptional().?)); 26912 assert(new_align != .none); 26913 ptr_ty_data.flags.alignment = new_align; 26914 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 }; 26915 } 26916 } 26917 } else if (struct_type.layout == .Extern) { 26918 // For extern structs, field alignment might be bigger than type's 26919 // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the 26920 // second field is aligned as u32. 26921 const field_offset = struct_ty.structFieldOffset(field_index, mod); 26922 ptr_ty_data.flags.alignment = if (parent_align == .none) 26923 .none 26924 else 26925 @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset))); 26926 } else { 26927 // Our alignment is capped at the field alignment. 26928 const field_align = try sema.structFieldAlignment( 26929 struct_type.fieldAlign(ip, field_index), 26930 Type.fromInterned(field_ty), 26931 struct_type.layout, 26932 ); 26933 ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none) 26934 field_align 26935 else 26936 field_align.min(parent_align); 26937 } 26938 26939 const ptr_field_ty = try sema.ptrType(ptr_ty_data); 26940 26941 if (struct_type.fieldIsComptime(ip, field_index)) { 26942 try sema.resolveStructFieldInits(struct_ty); 26943 const val = try mod.intern(.{ .ptr = .{ 26944 .ty = ptr_field_ty.toIntern(), 26945 .addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] }, 26946 } }); 26947 return Air.internedToRef(val); 26948 } 26949 26950 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { 26951 const val = try mod.intern(.{ .ptr = .{ 26952 .ty = ptr_field_ty.toIntern(), 26953 .addr = .{ .field = .{ 26954 .base = try struct_ptr_val.intern(struct_ptr_ty, mod), 26955 .index = field_index, 26956 } }, 26957 } }); 26958 return Air.internedToRef(val); 26959 } 26960 26961 try sema.requireRuntimeBlock(block, src, null); 26962 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); 26963 } 26964 26965 fn structFieldVal( 26966 sema: *Sema, 26967 block: *Block, 26968 src: LazySrcLoc, 26969 struct_byval: Air.Inst.Ref, 26970 field_name: InternPool.NullTerminatedString, 26971 field_name_src: LazySrcLoc, 26972 struct_ty: Type, 26973 ) CompileError!Air.Inst.Ref { 26974 const mod = sema.mod; 26975 const ip = &mod.intern_pool; 26976 assert(struct_ty.zigTypeTag(mod) == .Struct); 26977 26978 try sema.resolveTypeFields(struct_ty); 26979 26980 switch (ip.indexToKey(struct_ty.toIntern())) { 26981 .struct_type => |struct_type| { 26982 if (struct_type.isTuple(ip)) 26983 return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); 26984 26985 const field_index = struct_type.nameIndex(ip, field_name) orelse 26986 return sema.failWithBadStructFieldAccess(block, struct_type, field_name_src, field_name); 26987 if (struct_type.fieldIsComptime(ip, field_index)) { 26988 try sema.resolveStructFieldInits(struct_ty); 26989 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]); 26990 } 26991 26992 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); 26993 26994 if (try sema.resolveValue(struct_byval)) |struct_val| { 26995 if (struct_val.isUndef(mod)) return mod.undefRef(field_ty); 26996 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { 26997 return Air.internedToRef(opv.toIntern()); 26998 } 26999 return Air.internedToRef((try struct_val.fieldValue(mod, field_index)).toIntern()); 27000 } 27001 27002 try sema.requireRuntimeBlock(block, src, null); 27003 try sema.resolveTypeLayout(field_ty); 27004 return block.addStructFieldVal(struct_byval, field_index, field_ty); 27005 }, 27006 .anon_struct_type => |anon_struct| { 27007 if (anon_struct.names.len == 0) { 27008 return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); 27009 } else { 27010 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); 27011 return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty); 27012 } 27013 }, 27014 else => unreachable, 27015 } 27016 } 27017 27018 fn tupleFieldVal( 27019 sema: *Sema, 27020 block: *Block, 27021 src: LazySrcLoc, 27022 tuple_byval: Air.Inst.Ref, 27023 field_name: InternPool.NullTerminatedString, 27024 field_name_src: LazySrcLoc, 27025 tuple_ty: Type, 27026 ) CompileError!Air.Inst.Ref { 27027 const mod = sema.mod; 27028 if (mod.intern_pool.stringEqlSlice(field_name, "len")) { 27029 return mod.intRef(Type.usize, tuple_ty.structFieldCount(mod)); 27030 } 27031 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src); 27032 return sema.tupleFieldValByIndex(block, src, tuple_byval, field_index, tuple_ty); 27033 } 27034 27035 /// Asserts that `field_name` is not "len". 27036 fn tupleFieldIndex( 27037 sema: *Sema, 27038 block: *Block, 27039 tuple_ty: Type, 27040 field_name: InternPool.NullTerminatedString, 27041 field_name_src: LazySrcLoc, 27042 ) CompileError!u32 { 27043 const mod = sema.mod; 27044 assert(!mod.intern_pool.stringEqlSlice(field_name, "len")); 27045 if (field_name.toUnsigned(&mod.intern_pool)) |field_index| { 27046 if (field_index < tuple_ty.structFieldCount(mod)) return field_index; 27047 return sema.fail(block, field_name_src, "index '{}' out of bounds of tuple '{}'", .{ 27048 field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), 27049 }); 27050 } 27051 27052 return sema.fail(block, field_name_src, "no field named '{}' in tuple '{}'", .{ 27053 field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), 27054 }); 27055 } 27056 27057 fn tupleFieldValByIndex( 27058 sema: *Sema, 27059 block: *Block, 27060 src: LazySrcLoc, 27061 tuple_byval: Air.Inst.Ref, 27062 field_index: u32, 27063 tuple_ty: Type, 27064 ) CompileError!Air.Inst.Ref { 27065 const mod = sema.mod; 27066 const field_ty = tuple_ty.structFieldType(field_index, mod); 27067 27068 if (tuple_ty.structFieldIsComptime(field_index, mod)) 27069 try sema.resolveStructFieldInits(tuple_ty); 27070 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| { 27071 return Air.internedToRef(default_value.toIntern()); 27072 } 27073 27074 if (try sema.resolveValue(tuple_byval)) |tuple_val| { 27075 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { 27076 return Air.internedToRef(opv.toIntern()); 27077 } 27078 return switch (mod.intern_pool.indexToKey(tuple_val.toIntern())) { 27079 .undef => mod.undefRef(field_ty), 27080 .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) { 27081 .bytes => |bytes| try mod.intValue(Type.u8, bytes[0]), 27082 .elems => |elems| Value.fromInterned(elems[field_index]), 27083 .repeated_elem => |elem| Value.fromInterned(elem), 27084 }.toIntern()), 27085 else => unreachable, 27086 }; 27087 } 27088 27089 try sema.requireRuntimeBlock(block, src, null); 27090 try sema.resolveTypeLayout(field_ty); 27091 return block.addStructFieldVal(tuple_byval, field_index, field_ty); 27092 } 27093 27094 fn unionFieldPtr( 27095 sema: *Sema, 27096 block: *Block, 27097 src: LazySrcLoc, 27098 union_ptr: Air.Inst.Ref, 27099 field_name: InternPool.NullTerminatedString, 27100 field_name_src: LazySrcLoc, 27101 union_ty: Type, 27102 initializing: bool, 27103 ) CompileError!Air.Inst.Ref { 27104 const mod = sema.mod; 27105 const ip = &mod.intern_pool; 27106 27107 assert(union_ty.zigTypeTag(mod) == .Union); 27108 27109 const union_ptr_ty = sema.typeOf(union_ptr); 27110 const union_ptr_info = union_ptr_ty.ptrInfo(mod); 27111 try sema.resolveTypeFields(union_ty); 27112 const union_obj = mod.typeToUnion(union_ty).?; 27113 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 27114 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 27115 const ptr_field_ty = try sema.ptrType(.{ 27116 .child = field_ty.toIntern(), 27117 .flags = .{ 27118 .is_const = union_ptr_info.flags.is_const, 27119 .is_volatile = union_ptr_info.flags.is_volatile, 27120 .address_space = union_ptr_info.flags.address_space, 27121 .alignment = if (union_obj.getLayout(ip) == .Auto) blk: { 27122 const union_align = if (union_ptr_info.flags.alignment != .none) 27123 union_ptr_info.flags.alignment 27124 else 27125 try sema.typeAbiAlignment(union_ty); 27126 const field_align = try sema.unionFieldAlignment(union_obj, field_index); 27127 break :blk union_align.min(field_align); 27128 } else union_ptr_info.flags.alignment, 27129 }, 27130 .packed_offset = union_ptr_info.packed_offset, 27131 }); 27132 const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, mod).?); 27133 27134 if (initializing and field_ty.zigTypeTag(mod) == .NoReturn) { 27135 const msg = msg: { 27136 const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{}); 27137 errdefer msg.destroy(sema.gpa); 27138 27139 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 27140 field_name.fmt(ip), 27141 }); 27142 try sema.addDeclaredHereNote(msg, union_ty); 27143 break :msg msg; 27144 }; 27145 return sema.failWithOwnedErrorMsg(block, msg); 27146 } 27147 27148 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { 27149 switch (union_obj.getLayout(ip)) { 27150 .Auto => if (!initializing) { 27151 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse 27152 break :ct; 27153 if (union_val.isUndef(mod)) { 27154 return sema.failWithUseOfUndef(block, src); 27155 } 27156 const un = ip.indexToKey(union_val.toIntern()).un; 27157 const field_tag = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27158 const tag_matches = un.tag == field_tag.toIntern(); 27159 if (!tag_matches) { 27160 const msg = msg: { 27161 const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), mod).?; 27162 const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, mod); 27163 const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ 27164 field_name.fmt(ip), 27165 active_field_name.fmt(ip), 27166 }); 27167 errdefer msg.destroy(sema.gpa); 27168 try sema.addDeclaredHereNote(msg, union_ty); 27169 break :msg msg; 27170 }; 27171 return sema.failWithOwnedErrorMsg(block, msg); 27172 } 27173 }, 27174 .Packed, .Extern => {}, 27175 } 27176 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 27177 .ty = ptr_field_ty.toIntern(), 27178 .addr = .{ .field = .{ 27179 .base = union_ptr_val.toIntern(), 27180 .index = field_index, 27181 } }, 27182 } }))); 27183 } 27184 27185 try sema.requireRuntimeBlock(block, src, null); 27186 if (!initializing and union_obj.getLayout(ip) == .Auto and block.wantSafety() and 27187 union_ty.unionTagTypeSafety(mod) != null and union_obj.field_names.len > 1) 27188 { 27189 const wanted_tag_val = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27190 const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); 27191 // TODO would it be better if get_union_tag supported pointers to unions? 27192 const union_val = try block.addTyOp(.load, union_ty, union_ptr); 27193 const active_tag = try block.addTyOp(.get_union_tag, Type.fromInterned(union_obj.enum_tag_ty), union_val); 27194 try sema.panicInactiveUnionField(block, src, active_tag, wanted_tag); 27195 } 27196 if (field_ty.zigTypeTag(mod) == .NoReturn) { 27197 _ = try block.addNoOp(.unreach); 27198 return .unreachable_value; 27199 } 27200 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); 27201 } 27202 27203 fn unionFieldVal( 27204 sema: *Sema, 27205 block: *Block, 27206 src: LazySrcLoc, 27207 union_byval: Air.Inst.Ref, 27208 field_name: InternPool.NullTerminatedString, 27209 field_name_src: LazySrcLoc, 27210 union_ty: Type, 27211 ) CompileError!Air.Inst.Ref { 27212 const mod = sema.mod; 27213 const ip = &mod.intern_pool; 27214 assert(union_ty.zigTypeTag(mod) == .Union); 27215 27216 try sema.resolveTypeFields(union_ty); 27217 const union_obj = mod.typeToUnion(union_ty).?; 27218 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 27219 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 27220 const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, mod).?); 27221 27222 if (try sema.resolveValue(union_byval)) |union_val| { 27223 if (union_val.isUndef(mod)) return mod.undefRef(field_ty); 27224 27225 const un = ip.indexToKey(union_val.toIntern()).un; 27226 const field_tag = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27227 const tag_matches = un.tag == field_tag.toIntern(); 27228 switch (union_obj.getLayout(ip)) { 27229 .Auto => { 27230 if (tag_matches) { 27231 return Air.internedToRef(un.val); 27232 } else { 27233 const msg = msg: { 27234 const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), mod).?; 27235 const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, mod); 27236 const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ 27237 field_name.fmt(ip), active_field_name.fmt(ip), 27238 }); 27239 errdefer msg.destroy(sema.gpa); 27240 try sema.addDeclaredHereNote(msg, union_ty); 27241 break :msg msg; 27242 }; 27243 return sema.failWithOwnedErrorMsg(block, msg); 27244 } 27245 }, 27246 .Packed, .Extern => |layout| { 27247 if (tag_matches) { 27248 return Air.internedToRef(un.val); 27249 } else { 27250 const old_ty = if (un.tag == .none) 27251 Type.fromInterned(ip.typeOf(un.val)) 27252 else 27253 union_ty.unionFieldType(Value.fromInterned(un.tag), mod).?; 27254 27255 if (try sema.bitCastUnionFieldVal(block, src, Value.fromInterned(un.val), old_ty, field_ty, layout)) |new_val| { 27256 return Air.internedToRef(new_val.toIntern()); 27257 } 27258 } 27259 }, 27260 } 27261 } 27262 27263 try sema.requireRuntimeBlock(block, src, null); 27264 if (union_obj.getLayout(ip) == .Auto and block.wantSafety() and 27265 union_ty.unionTagTypeSafety(mod) != null and union_obj.field_names.len > 1) 27266 { 27267 const wanted_tag_val = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index); 27268 const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); 27269 const active_tag = try block.addTyOp(.get_union_tag, Type.fromInterned(union_obj.enum_tag_ty), union_byval); 27270 try sema.panicInactiveUnionField(block, src, active_tag, wanted_tag); 27271 } 27272 if (field_ty.zigTypeTag(mod) == .NoReturn) { 27273 _ = try block.addNoOp(.unreach); 27274 return .unreachable_value; 27275 } 27276 try sema.resolveTypeLayout(field_ty); 27277 return block.addStructFieldVal(union_byval, field_index, field_ty); 27278 } 27279 27280 fn elemPtr( 27281 sema: *Sema, 27282 block: *Block, 27283 src: LazySrcLoc, 27284 indexable_ptr: Air.Inst.Ref, 27285 elem_index: Air.Inst.Ref, 27286 elem_index_src: LazySrcLoc, 27287 init: bool, 27288 oob_safety: bool, 27289 ) CompileError!Air.Inst.Ref { 27290 const mod = sema.mod; 27291 const indexable_ptr_src = src; // TODO better source location 27292 const indexable_ptr_ty = sema.typeOf(indexable_ptr); 27293 27294 const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(mod)) { 27295 .Pointer => indexable_ptr_ty.childType(mod), 27296 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(mod)}), 27297 }; 27298 try checkIndexable(sema, block, src, indexable_ty); 27299 27300 const elem_ptr = switch (indexable_ty.zigTypeTag(mod)) { 27301 .Array, .Vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), 27302 .Struct => blk: { 27303 // Tuple field access. 27304 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ 27305 .needed_comptime_reason = "tuple field access index must be comptime-known", 27306 }); 27307 const index: u32 = @intCast(index_val.toUnsignedInt(mod)); 27308 break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); 27309 }, 27310 else => { 27311 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); 27312 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); 27313 }, 27314 }; 27315 27316 try sema.checkKnownAllocPtr(indexable_ptr, elem_ptr); 27317 return elem_ptr; 27318 } 27319 27320 /// Asserts that the type of indexable is pointer. 27321 fn elemPtrOneLayerOnly( 27322 sema: *Sema, 27323 block: *Block, 27324 src: LazySrcLoc, 27325 indexable: Air.Inst.Ref, 27326 elem_index: Air.Inst.Ref, 27327 elem_index_src: LazySrcLoc, 27328 init: bool, 27329 oob_safety: bool, 27330 ) CompileError!Air.Inst.Ref { 27331 const indexable_src = src; // TODO better source location 27332 const indexable_ty = sema.typeOf(indexable); 27333 const mod = sema.mod; 27334 27335 try checkIndexable(sema, block, src, indexable_ty); 27336 27337 switch (indexable_ty.ptrSize(mod)) { 27338 .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27339 .Many, .C => { 27340 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 27341 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 27342 const runtime_src = rs: { 27343 const ptr_val = maybe_ptr_val orelse break :rs indexable_src; 27344 const index_val = maybe_index_val orelse break :rs elem_index_src; 27345 const index: usize = @intCast(index_val.toUnsignedInt(mod)); 27346 const result_ty = try sema.elemPtrType(indexable_ty, index); 27347 const elem_ptr = try ptr_val.elemPtr(result_ty, index, mod); 27348 return Air.internedToRef(elem_ptr.toIntern()); 27349 }; 27350 const result_ty = try sema.elemPtrType(indexable_ty, null); 27351 27352 try sema.requireRuntimeBlock(block, src, runtime_src); 27353 return block.addPtrElemPtr(indexable, elem_index, result_ty); 27354 }, 27355 .One => { 27356 const child_ty = indexable_ty.childType(mod); 27357 const elem_ptr = switch (child_ty.zigTypeTag(mod)) { 27358 .Array, .Vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety), 27359 .Struct => blk: { 27360 assert(child_ty.isTuple(mod)); 27361 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ 27362 .needed_comptime_reason = "tuple field access index must be comptime-known", 27363 }); 27364 const index: u32 = @intCast(index_val.toUnsignedInt(mod)); 27365 break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); 27366 }, 27367 else => unreachable, // Guaranteed by checkIndexable 27368 }; 27369 try sema.checkKnownAllocPtr(indexable, elem_ptr); 27370 return elem_ptr; 27371 }, 27372 } 27373 } 27374 27375 fn elemVal( 27376 sema: *Sema, 27377 block: *Block, 27378 src: LazySrcLoc, 27379 indexable: Air.Inst.Ref, 27380 elem_index_uncasted: Air.Inst.Ref, 27381 elem_index_src: LazySrcLoc, 27382 oob_safety: bool, 27383 ) CompileError!Air.Inst.Ref { 27384 const indexable_src = src; // TODO better source location 27385 const indexable_ty = sema.typeOf(indexable); 27386 const mod = sema.mod; 27387 27388 try checkIndexable(sema, block, src, indexable_ty); 27389 27390 // TODO in case of a vector of pointers, we need to detect whether the element 27391 // index is a scalar or vector instead of unconditionally casting to usize. 27392 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src); 27393 27394 switch (indexable_ty.zigTypeTag(mod)) { 27395 .Pointer => switch (indexable_ty.ptrSize(mod)) { 27396 .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27397 .Many, .C => { 27398 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 27399 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 27400 27401 const runtime_src = rs: { 27402 const indexable_val = maybe_indexable_val orelse break :rs indexable_src; 27403 const index_val = maybe_index_val orelse break :rs elem_index_src; 27404 const index: usize = @intCast(index_val.toUnsignedInt(mod)); 27405 const elem_ty = indexable_ty.elemType2(mod); 27406 const many_ptr_ty = try mod.manyConstPtrType(elem_ty); 27407 const many_ptr_val = try mod.getCoerced(indexable_val, many_ptr_ty); 27408 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty); 27409 const elem_ptr_val = try many_ptr_val.elemPtr(elem_ptr_ty, index, mod); 27410 if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { 27411 return Air.internedToRef((try mod.getCoerced(elem_val, elem_ty)).toIntern()); 27412 } 27413 break :rs indexable_src; 27414 }; 27415 27416 try sema.requireRuntimeBlock(block, src, runtime_src); 27417 return block.addBinOp(.ptr_elem_val, indexable, elem_index); 27418 }, 27419 .One => { 27420 arr_sent: { 27421 const inner_ty = indexable_ty.childType(mod); 27422 if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent; 27423 const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent; 27424 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; 27425 const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod)); 27426 if (index != inner_ty.arrayLen(mod)) break :arr_sent; 27427 return Air.internedToRef(sentinel.toIntern()); 27428 } 27429 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); 27430 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); 27431 }, 27432 }, 27433 .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 27434 .Vector => { 27435 // TODO: If the index is a vector, the result should be a vector. 27436 return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety); 27437 }, 27438 .Struct => { 27439 // Tuple field access. 27440 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ 27441 .needed_comptime_reason = "tuple field access index must be comptime-known", 27442 }); 27443 const index: u32 = @intCast(index_val.toUnsignedInt(mod)); 27444 return sema.tupleField(block, indexable_src, indexable, elem_index_src, index); 27445 }, 27446 else => unreachable, 27447 } 27448 } 27449 27450 fn validateRuntimeElemAccess( 27451 sema: *Sema, 27452 block: *Block, 27453 elem_index_src: LazySrcLoc, 27454 elem_ty: Type, 27455 parent_ty: Type, 27456 parent_src: LazySrcLoc, 27457 ) CompileError!void { 27458 const mod = sema.mod; 27459 if (try sema.typeRequiresComptime(elem_ty)) { 27460 const msg = msg: { 27461 const msg = try sema.errMsg( 27462 block, 27463 elem_index_src, 27464 "values of type '{}' must be comptime-known, but index value is runtime-known", 27465 .{parent_ty.fmt(mod)}, 27466 ); 27467 errdefer msg.destroy(sema.gpa); 27468 27469 const src_decl = mod.declPtr(block.src_decl); 27470 try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty); 27471 27472 break :msg msg; 27473 }; 27474 return sema.failWithOwnedErrorMsg(block, msg); 27475 } 27476 } 27477 27478 fn tupleFieldPtr( 27479 sema: *Sema, 27480 block: *Block, 27481 tuple_ptr_src: LazySrcLoc, 27482 tuple_ptr: Air.Inst.Ref, 27483 field_index_src: LazySrcLoc, 27484 field_index: u32, 27485 init: bool, 27486 ) CompileError!Air.Inst.Ref { 27487 const mod = sema.mod; 27488 const tuple_ptr_ty = sema.typeOf(tuple_ptr); 27489 const tuple_ty = tuple_ptr_ty.childType(mod); 27490 try sema.resolveTypeFields(tuple_ty); 27491 const field_count = tuple_ty.structFieldCount(mod); 27492 27493 if (field_count == 0) { 27494 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); 27495 } 27496 27497 if (field_index >= field_count) { 27498 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 27499 field_index, field_count, 27500 }); 27501 } 27502 27503 const field_ty = tuple_ty.structFieldType(field_index, mod); 27504 const ptr_field_ty = try sema.ptrType(.{ 27505 .child = field_ty.toIntern(), 27506 .flags = .{ 27507 .is_const = !tuple_ptr_ty.ptrIsMutable(mod), 27508 .is_volatile = tuple_ptr_ty.isVolatilePtr(mod), 27509 .address_space = tuple_ptr_ty.ptrAddressSpace(mod), 27510 }, 27511 }); 27512 27513 if (tuple_ty.structFieldIsComptime(field_index, mod)) 27514 try sema.resolveStructFieldInits(tuple_ty); 27515 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| { 27516 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 27517 .ty = ptr_field_ty.toIntern(), 27518 .addr = .{ .comptime_field = default_val.toIntern() }, 27519 } }))); 27520 } 27521 27522 if (try sema.resolveValue(tuple_ptr)) |tuple_ptr_val| { 27523 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 27524 .ty = ptr_field_ty.toIntern(), 27525 .addr = .{ .field = .{ 27526 .base = tuple_ptr_val.toIntern(), 27527 .index = field_index, 27528 } }, 27529 } }))); 27530 } 27531 27532 if (!init) { 27533 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src); 27534 } 27535 27536 try sema.requireRuntimeBlock(block, tuple_ptr_src, null); 27537 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); 27538 } 27539 27540 fn tupleField( 27541 sema: *Sema, 27542 block: *Block, 27543 tuple_src: LazySrcLoc, 27544 tuple: Air.Inst.Ref, 27545 field_index_src: LazySrcLoc, 27546 field_index: u32, 27547 ) CompileError!Air.Inst.Ref { 27548 const mod = sema.mod; 27549 const tuple_ty = sema.typeOf(tuple); 27550 try sema.resolveTypeFields(tuple_ty); 27551 const field_count = tuple_ty.structFieldCount(mod); 27552 27553 if (field_count == 0) { 27554 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); 27555 } 27556 27557 if (field_index >= field_count) { 27558 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 27559 field_index, field_count, 27560 }); 27561 } 27562 27563 const field_ty = tuple_ty.structFieldType(field_index, mod); 27564 27565 if (tuple_ty.structFieldIsComptime(field_index, mod)) 27566 try sema.resolveStructFieldInits(tuple_ty); 27567 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| { 27568 return Air.internedToRef(default_value.toIntern()); // comptime field 27569 } 27570 27571 if (try sema.resolveValue(tuple)) |tuple_val| { 27572 if (tuple_val.isUndef(mod)) return mod.undefRef(field_ty); 27573 return Air.internedToRef((try tuple_val.fieldValue(mod, field_index)).toIntern()); 27574 } 27575 27576 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); 27577 27578 try sema.requireRuntimeBlock(block, tuple_src, null); 27579 try sema.resolveTypeLayout(field_ty); 27580 return block.addStructFieldVal(tuple, field_index, field_ty); 27581 } 27582 27583 fn elemValArray( 27584 sema: *Sema, 27585 block: *Block, 27586 src: LazySrcLoc, 27587 array_src: LazySrcLoc, 27588 array: Air.Inst.Ref, 27589 elem_index_src: LazySrcLoc, 27590 elem_index: Air.Inst.Ref, 27591 oob_safety: bool, 27592 ) CompileError!Air.Inst.Ref { 27593 const mod = sema.mod; 27594 const array_ty = sema.typeOf(array); 27595 const array_sent = array_ty.sentinel(mod); 27596 const array_len = array_ty.arrayLen(mod); 27597 const array_len_s = array_len + @intFromBool(array_sent != null); 27598 const elem_ty = array_ty.childType(mod); 27599 27600 if (array_len_s == 0) { 27601 return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); 27602 } 27603 27604 const maybe_undef_array_val = try sema.resolveValue(array); 27605 // index must be defined since it can access out of bounds 27606 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 27607 27608 if (maybe_index_val) |index_val| { 27609 const index: usize = @intCast(index_val.toUnsignedInt(mod)); 27610 if (array_sent) |s| { 27611 if (index == array_len) { 27612 return Air.internedToRef(s.toIntern()); 27613 } 27614 } 27615 if (index >= array_len_s) { 27616 const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else ""; 27617 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 27618 } 27619 } 27620 if (maybe_undef_array_val) |array_val| { 27621 if (array_val.isUndef(mod)) { 27622 return mod.undefRef(elem_ty); 27623 } 27624 if (maybe_index_val) |index_val| { 27625 const index: usize = @intCast(index_val.toUnsignedInt(mod)); 27626 const elem_val = try array_val.elemValue(mod, index); 27627 return Air.internedToRef(elem_val.toIntern()); 27628 } 27629 } 27630 27631 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src); 27632 27633 const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src; 27634 try sema.requireRuntimeBlock(block, src, runtime_src); 27635 try sema.queueFullTypeResolution(array_ty); 27636 if (oob_safety and block.wantSafety()) { 27637 // Runtime check is only needed if unable to comptime check 27638 if (maybe_index_val == null) { 27639 const len_inst = try mod.intRef(Type.usize, array_len); 27640 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt; 27641 try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op); 27642 } 27643 } 27644 return block.addBinOp(.array_elem_val, array, elem_index); 27645 } 27646 27647 fn elemPtrArray( 27648 sema: *Sema, 27649 block: *Block, 27650 src: LazySrcLoc, 27651 array_ptr_src: LazySrcLoc, 27652 array_ptr: Air.Inst.Ref, 27653 elem_index_src: LazySrcLoc, 27654 elem_index: Air.Inst.Ref, 27655 init: bool, 27656 oob_safety: bool, 27657 ) CompileError!Air.Inst.Ref { 27658 const mod = sema.mod; 27659 const array_ptr_ty = sema.typeOf(array_ptr); 27660 const array_ty = array_ptr_ty.childType(mod); 27661 const array_sent = array_ty.sentinel(mod) != null; 27662 const array_len = array_ty.arrayLen(mod); 27663 const array_len_s = array_len + @intFromBool(array_sent); 27664 27665 if (array_len_s == 0) { 27666 return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{}); 27667 } 27668 27669 const maybe_undef_array_ptr_val = try sema.resolveValue(array_ptr); 27670 // The index must not be undefined since it can be out of bounds. 27671 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 27672 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod)); 27673 if (index >= array_len_s) { 27674 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; 27675 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 27676 } 27677 break :o index; 27678 } else null; 27679 27680 const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset); 27681 27682 if (maybe_undef_array_ptr_val) |array_ptr_val| { 27683 if (array_ptr_val.isUndef(mod)) { 27684 return mod.undefRef(elem_ptr_ty); 27685 } 27686 if (offset) |index| { 27687 const elem_ptr = try array_ptr_val.elemPtr(elem_ptr_ty, index, mod); 27688 return Air.internedToRef(elem_ptr.toIntern()); 27689 } 27690 } 27691 27692 if (!init) { 27693 try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(mod), array_ty, array_ptr_src); 27694 } 27695 27696 const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; 27697 try sema.requireRuntimeBlock(block, src, runtime_src); 27698 27699 // Runtime check is only needed if unable to comptime check. 27700 if (oob_safety and block.wantSafety() and offset == null) { 27701 const len_inst = try mod.intRef(Type.usize, array_len); 27702 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; 27703 try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op); 27704 } 27705 27706 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); 27707 } 27708 27709 fn elemValSlice( 27710 sema: *Sema, 27711 block: *Block, 27712 src: LazySrcLoc, 27713 slice_src: LazySrcLoc, 27714 slice: Air.Inst.Ref, 27715 elem_index_src: LazySrcLoc, 27716 elem_index: Air.Inst.Ref, 27717 oob_safety: bool, 27718 ) CompileError!Air.Inst.Ref { 27719 const mod = sema.mod; 27720 const slice_ty = sema.typeOf(slice); 27721 const slice_sent = slice_ty.sentinel(mod) != null; 27722 const elem_ty = slice_ty.elemType2(mod); 27723 var runtime_src = slice_src; 27724 27725 // slice must be defined since it can dereferenced as null 27726 const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); 27727 // index must be defined since it can index out of bounds 27728 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 27729 27730 if (maybe_slice_val) |slice_val| { 27731 runtime_src = elem_index_src; 27732 const slice_len = slice_val.sliceLen(mod); 27733 const slice_len_s = slice_len + @intFromBool(slice_sent); 27734 if (slice_len_s == 0) { 27735 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 27736 } 27737 if (maybe_index_val) |index_val| { 27738 const index: usize = @intCast(index_val.toUnsignedInt(mod)); 27739 if (index >= slice_len_s) { 27740 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 27741 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 27742 } 27743 const elem_ptr_ty = try sema.elemPtrType(slice_ty, index); 27744 const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod); 27745 if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { 27746 return Air.internedToRef(elem_val.toIntern()); 27747 } 27748 runtime_src = slice_src; 27749 } 27750 } 27751 27752 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src); 27753 27754 try sema.requireRuntimeBlock(block, src, runtime_src); 27755 if (oob_safety and block.wantSafety()) { 27756 const len_inst = if (maybe_slice_val) |slice_val| 27757 try mod.intRef(Type.usize, slice_val.sliceLen(mod)) 27758 else 27759 try block.addTyOp(.slice_len, Type.usize, slice); 27760 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 27761 try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op); 27762 } 27763 try sema.queueFullTypeResolution(sema.typeOf(slice)); 27764 return block.addBinOp(.slice_elem_val, slice, elem_index); 27765 } 27766 27767 fn elemPtrSlice( 27768 sema: *Sema, 27769 block: *Block, 27770 src: LazySrcLoc, 27771 slice_src: LazySrcLoc, 27772 slice: Air.Inst.Ref, 27773 elem_index_src: LazySrcLoc, 27774 elem_index: Air.Inst.Ref, 27775 oob_safety: bool, 27776 ) CompileError!Air.Inst.Ref { 27777 const mod = sema.mod; 27778 const slice_ty = sema.typeOf(slice); 27779 const slice_sent = slice_ty.sentinel(mod) != null; 27780 27781 const maybe_undef_slice_val = try sema.resolveValue(slice); 27782 // The index must not be undefined since it can be out of bounds. 27783 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 27784 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod)); 27785 break :o index; 27786 } else null; 27787 27788 const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset); 27789 27790 if (maybe_undef_slice_val) |slice_val| { 27791 if (slice_val.isUndef(mod)) { 27792 return mod.undefRef(elem_ptr_ty); 27793 } 27794 const slice_len = slice_val.sliceLen(mod); 27795 const slice_len_s = slice_len + @intFromBool(slice_sent); 27796 if (slice_len_s == 0) { 27797 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 27798 } 27799 if (offset) |index| { 27800 if (index >= slice_len_s) { 27801 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 27802 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 27803 } 27804 const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod); 27805 return Air.internedToRef(elem_ptr_val.toIntern()); 27806 } 27807 } 27808 27809 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src); 27810 27811 const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src; 27812 try sema.requireRuntimeBlock(block, src, runtime_src); 27813 if (oob_safety and block.wantSafety()) { 27814 const len_inst = len: { 27815 if (maybe_undef_slice_val) |slice_val| 27816 if (!slice_val.isUndef(mod)) 27817 break :len try mod.intRef(Type.usize, slice_val.sliceLen(mod)); 27818 break :len try block.addTyOp(.slice_len, Type.usize, slice); 27819 }; 27820 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 27821 try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op); 27822 } 27823 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); 27824 } 27825 27826 fn coerce( 27827 sema: *Sema, 27828 block: *Block, 27829 dest_ty_unresolved: Type, 27830 inst: Air.Inst.Ref, 27831 inst_src: LazySrcLoc, 27832 ) CompileError!Air.Inst.Ref { 27833 return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) { 27834 error.NotCoercible => unreachable, 27835 else => |e| return e, 27836 }; 27837 } 27838 27839 const CoersionError = CompileError || error{ 27840 /// When coerce is called recursively, this error should be returned instead of using `fail` 27841 /// to ensure correct types in compile errors. 27842 NotCoercible, 27843 }; 27844 27845 const CoerceOpts = struct { 27846 /// Should coerceExtra emit error messages. 27847 report_err: bool = true, 27848 /// Ignored if `report_err == false`. 27849 is_ret: bool = false, 27850 /// Should coercion to comptime_int ermit an error message. 27851 no_cast_to_comptime_int: bool = false, 27852 27853 param_src: struct { 27854 func_inst: Air.Inst.Ref = .none, 27855 param_i: u32 = undefined, 27856 27857 fn get(info: @This(), sema: *Sema) !?Module.SrcLoc { 27858 if (info.func_inst == .none) return null; 27859 const mod = sema.mod; 27860 const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null; 27861 const param_src = Module.paramSrc(0, mod, fn_decl, info.param_i); 27862 if (param_src == .node_offset_param) { 27863 return Module.SrcLoc{ 27864 .file_scope = fn_decl.getFileScope(mod), 27865 .parent_decl_node = fn_decl.src_node, 27866 .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param), 27867 }; 27868 } 27869 return param_src.toSrcLoc(fn_decl, mod); 27870 } 27871 } = .{}, 27872 }; 27873 27874 fn coerceExtra( 27875 sema: *Sema, 27876 block: *Block, 27877 dest_ty: Type, 27878 inst: Air.Inst.Ref, 27879 inst_src: LazySrcLoc, 27880 opts: CoerceOpts, 27881 ) CoersionError!Air.Inst.Ref { 27882 if (dest_ty.isGenericPoison()) return inst; 27883 const mod = sema.mod; 27884 const dest_ty_src = inst_src; // TODO better source location 27885 try sema.resolveTypeFields(dest_ty); 27886 const inst_ty = sema.typeOf(inst); 27887 try sema.resolveTypeFields(inst_ty); 27888 const target = mod.getTarget(); 27889 // If the types are the same, we can return the operand. 27890 if (dest_ty.eql(inst_ty, mod)) 27891 return inst; 27892 27893 const maybe_inst_val = try sema.resolveValue(inst); 27894 27895 var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); 27896 if (in_memory_result == .ok) { 27897 if (maybe_inst_val) |val| { 27898 return sema.coerceInMemory(val, dest_ty); 27899 } 27900 try sema.requireRuntimeBlock(block, inst_src, null); 27901 try sema.queueFullTypeResolution(dest_ty); 27902 const new_val = try block.addBitCast(dest_ty, inst); 27903 try sema.checkKnownAllocPtr(inst, new_val); 27904 return new_val; 27905 } 27906 27907 switch (dest_ty.zigTypeTag(mod)) { 27908 .Optional => optional: { 27909 if (maybe_inst_val) |val| { 27910 // undefined sets the optional bit also to undefined. 27911 if (val.toIntern() == .undef) { 27912 return mod.undefRef(dest_ty); 27913 } 27914 27915 // null to ?T 27916 if (val.toIntern() == .null_value) { 27917 return Air.internedToRef((try mod.intern(.{ .opt = .{ 27918 .ty = dest_ty.toIntern(), 27919 .val = .none, 27920 } }))); 27921 } 27922 } 27923 27924 // cast from ?*T and ?[*]T to ?*anyopaque 27925 // but don't do it if the source type is a double pointer 27926 if (dest_ty.isPtrLikeOptional(mod) and 27927 dest_ty.elemType2(mod).toIntern() == .anyopaque_type and 27928 inst_ty.isPtrAtRuntime(mod)) 27929 anyopaque_check: { 27930 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; 27931 const elem_ty = inst_ty.elemType2(mod); 27932 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) { 27933 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 27934 .actual = inst_ty, 27935 .wanted = dest_ty, 27936 } }; 27937 break :optional; 27938 } 27939 // Let the logic below handle wrapping the optional now that 27940 // it has been checked to correctly coerce. 27941 if (!inst_ty.isPtrLikeOptional(mod)) break :anyopaque_check; 27942 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27943 } 27944 27945 // T to ?T 27946 const child_type = dest_ty.optionalChild(mod); 27947 const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 27948 error.NotCoercible => { 27949 if (in_memory_result == .no_match) { 27950 // Try to give more useful notes 27951 in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src); 27952 } 27953 break :optional; 27954 }, 27955 else => |e| return e, 27956 }; 27957 return try sema.wrapOptional(block, dest_ty, intermediate, inst_src); 27958 }, 27959 .Pointer => pointer: { 27960 const dest_info = dest_ty.ptrInfo(mod); 27961 27962 // Function body to function pointer. 27963 if (inst_ty.zigTypeTag(mod) == .Fn) { 27964 const fn_val = try sema.resolveConstDefinedValue(block, .unneeded, inst, undefined); 27965 const fn_decl = fn_val.pointerDecl(mod).?; 27966 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl); 27967 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); 27968 } 27969 27970 // *T to *[1]T 27971 single_item: { 27972 if (dest_info.flags.size != .One) break :single_item; 27973 if (!inst_ty.isSinglePointer(mod)) break :single_item; 27974 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 27975 const ptr_elem_ty = inst_ty.childType(mod); 27976 const array_ty = Type.fromInterned(dest_info.child); 27977 if (array_ty.zigTypeTag(mod) != .Array) break :single_item; 27978 const array_elem_ty = array_ty.childType(mod); 27979 if (array_ty.arrayLen(mod) != 1) break :single_item; 27980 const dest_is_mut = !dest_info.flags.is_const; 27981 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { 27982 .ok => {}, 27983 else => break :single_item, 27984 } 27985 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27986 } 27987 27988 // Coercions where the source is a single pointer to an array. 27989 src_array_ptr: { 27990 if (!inst_ty.isSinglePointer(mod)) break :src_array_ptr; 27991 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 27992 const array_ty = inst_ty.childType(mod); 27993 if (array_ty.zigTypeTag(mod) != .Array) break :src_array_ptr; 27994 const array_elem_type = array_ty.childType(mod); 27995 const dest_is_mut = !dest_info.flags.is_const; 27996 27997 const dst_elem_type = Type.fromInterned(dest_info.child); 27998 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src); 27999 switch (elem_res) { 28000 .ok => {}, 28001 else => { 28002 in_memory_result = .{ .ptr_child = .{ 28003 .child = try elem_res.dupe(sema.arena), 28004 .actual = array_elem_type, 28005 .wanted = dst_elem_type, 28006 } }; 28007 break :src_array_ptr; 28008 }, 28009 } 28010 28011 if (dest_info.sentinel != .none) { 28012 if (array_ty.sentinel(mod)) |inst_sent| { 28013 if (Air.internedToRef(dest_info.sentinel) != 28014 try sema.coerceInMemory(inst_sent, dst_elem_type)) 28015 { 28016 in_memory_result = .{ .ptr_sentinel = .{ 28017 .actual = inst_sent, 28018 .wanted = Value.fromInterned(dest_info.sentinel), 28019 .ty = dst_elem_type, 28020 } }; 28021 break :src_array_ptr; 28022 } 28023 } else { 28024 in_memory_result = .{ .ptr_sentinel = .{ 28025 .actual = Value.@"unreachable", 28026 .wanted = Value.fromInterned(dest_info.sentinel), 28027 .ty = dst_elem_type, 28028 } }; 28029 break :src_array_ptr; 28030 } 28031 } 28032 28033 switch (dest_info.flags.size) { 28034 .Slice => { 28035 // *[N]T to []T 28036 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src); 28037 }, 28038 .C => { 28039 // *[N]T to [*c]T 28040 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28041 }, 28042 .Many => { 28043 // *[N]T to [*]T 28044 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28045 }, 28046 .One => {}, 28047 } 28048 } 28049 28050 // coercion from C pointer 28051 if (inst_ty.isCPtr(mod)) src_c_ptr: { 28052 if (dest_info.flags.size == .Slice) break :src_c_ptr; 28053 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr; 28054 // In this case we must add a safety check because the C pointer 28055 // could be null. 28056 const src_elem_ty = inst_ty.childType(mod); 28057 const dest_is_mut = !dest_info.flags.is_const; 28058 const dst_elem_type = Type.fromInterned(dest_info.child); 28059 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { 28060 .ok => {}, 28061 else => break :src_c_ptr, 28062 } 28063 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28064 } 28065 28066 // cast from *T and [*]T to *anyopaque 28067 // but don't do it if the source type is a double pointer 28068 if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(mod) == .Pointer) to_anyopaque: { 28069 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 28070 const elem_ty = inst_ty.elemType2(mod); 28071 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) { 28072 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 28073 .actual = inst_ty, 28074 .wanted = dest_ty, 28075 } }; 28076 break :pointer; 28077 } 28078 if (dest_ty.isSlice(mod)) break :to_anyopaque; 28079 if (inst_ty.isSlice(mod)) { 28080 in_memory_result = .{ .slice_to_anyopaque = .{ 28081 .actual = inst_ty, 28082 .wanted = dest_ty, 28083 } }; 28084 break :pointer; 28085 } 28086 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28087 } 28088 28089 switch (dest_info.flags.size) { 28090 // coercion to C pointer 28091 .C => switch (inst_ty.zigTypeTag(mod)) { 28092 .Null => return Air.internedToRef(try mod.intern(.{ .ptr = .{ 28093 .ty = dest_ty.toIntern(), 28094 .addr = .{ .int = .zero_usize }, 28095 } })), 28096 .ComptimeInt => { 28097 const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 28098 error.NotCoercible => break :pointer, 28099 else => |e| return e, 28100 }; 28101 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 28102 }, 28103 .Int => { 28104 const ptr_size_ty = switch (inst_ty.intInfo(mod).signedness) { 28105 .signed => Type.isize, 28106 .unsigned => Type.usize, 28107 }; 28108 const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 28109 error.NotCoercible => { 28110 // Try to give more useful notes 28111 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src); 28112 break :pointer; 28113 }, 28114 else => |e| return e, 28115 }; 28116 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 28117 }, 28118 .Pointer => p: { 28119 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 28120 const inst_info = inst_ty.ptrInfo(mod); 28121 switch (try sema.coerceInMemoryAllowed( 28122 block, 28123 Type.fromInterned(dest_info.child), 28124 Type.fromInterned(inst_info.child), 28125 !dest_info.flags.is_const, 28126 target, 28127 dest_ty_src, 28128 inst_src, 28129 )) { 28130 .ok => {}, 28131 else => break :p, 28132 } 28133 if (inst_info.flags.size == .Slice) { 28134 assert(dest_info.sentinel == .none); 28135 if (inst_info.sentinel == .none or 28136 inst_info.sentinel != (try mod.intValue(Type.fromInterned(inst_info.child), 0)).toIntern()) 28137 break :p; 28138 28139 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 28140 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 28141 } 28142 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 28143 }, 28144 else => {}, 28145 }, 28146 .One => switch (Type.fromInterned(dest_info.child).zigTypeTag(mod)) { 28147 .Union => { 28148 // pointer to anonymous struct to pointer to union 28149 if (inst_ty.isSinglePointer(mod) and 28150 inst_ty.childType(mod).isAnonStruct(mod) and 28151 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 28152 { 28153 return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src); 28154 } 28155 }, 28156 .Struct => { 28157 // pointer to anonymous struct to pointer to struct 28158 if (inst_ty.isSinglePointer(mod) and 28159 inst_ty.childType(mod).isAnonStruct(mod) and 28160 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 28161 { 28162 return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) { 28163 error.NotCoercible => break :pointer, 28164 else => |e| return e, 28165 }; 28166 } 28167 }, 28168 .Array => { 28169 // pointer to tuple to pointer to array 28170 if (inst_ty.isSinglePointer(mod) and 28171 inst_ty.childType(mod).isTuple(mod) and 28172 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 28173 { 28174 return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src); 28175 } 28176 }, 28177 else => {}, 28178 }, 28179 .Slice => to_slice: { 28180 if (inst_ty.zigTypeTag(mod) == .Array) { 28181 return sema.fail( 28182 block, 28183 inst_src, 28184 "array literal requires address-of operator (&) to coerce to slice type '{}'", 28185 .{dest_ty.fmt(mod)}, 28186 ); 28187 } 28188 28189 if (!inst_ty.isSinglePointer(mod)) break :to_slice; 28190 const inst_child_ty = inst_ty.childType(mod); 28191 if (!inst_child_ty.isTuple(mod)) break :to_slice; 28192 28193 // empty tuple to zero-length slice 28194 // note that this allows coercing to a mutable slice. 28195 if (inst_child_ty.structFieldCount(mod) == 0) { 28196 // Optional slice is represented with a null pointer so 28197 // we use a dummy pointer value with the required alignment. 28198 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 28199 .ty = dest_ty.toIntern(), 28200 .addr = .{ .int = if (dest_info.flags.alignment != .none) 28201 (try mod.intValue( 28202 Type.usize, 28203 dest_info.flags.alignment.toByteUnitsOptional().?, 28204 )).toIntern() 28205 else 28206 try mod.intern_pool.getCoercedInts( 28207 mod.gpa, 28208 mod.intern_pool.indexToKey( 28209 (try Type.fromInterned(dest_info.child).lazyAbiAlignment(mod)).toIntern(), 28210 ).int, 28211 .usize_type, 28212 ) }, 28213 .len = (try mod.intValue(Type.usize, 0)).toIntern(), 28214 } }))); 28215 } 28216 28217 // pointer to tuple to slice 28218 if (!dest_info.flags.is_const) { 28219 const err_msg = err_msg: { 28220 const err_msg = try sema.errMsg(block, inst_src, "cannot cast pointer to tuple to '{}'", .{dest_ty.fmt(mod)}); 28221 errdefer err_msg.deinit(sema.gpa); 28222 try sema.errNote(block, dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{}); 28223 break :err_msg err_msg; 28224 }; 28225 return sema.failWithOwnedErrorMsg(block, err_msg); 28226 } 28227 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); 28228 }, 28229 .Many => p: { 28230 if (!inst_ty.isSlice(mod)) break :p; 28231 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 28232 const inst_info = inst_ty.ptrInfo(mod); 28233 28234 switch (try sema.coerceInMemoryAllowed( 28235 block, 28236 Type.fromInterned(dest_info.child), 28237 Type.fromInterned(inst_info.child), 28238 !dest_info.flags.is_const, 28239 target, 28240 dest_ty_src, 28241 inst_src, 28242 )) { 28243 .ok => {}, 28244 else => break :p, 28245 } 28246 28247 if (dest_info.sentinel == .none or inst_info.sentinel == .none or 28248 Air.internedToRef(dest_info.sentinel) != 28249 try sema.coerceInMemory(Value.fromInterned(inst_info.sentinel), Type.fromInterned(dest_info.child))) 28250 break :p; 28251 28252 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 28253 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 28254 }, 28255 } 28256 }, 28257 .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) { 28258 .Float, .ComptimeFloat => float: { 28259 const val = maybe_inst_val orelse { 28260 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { 28261 if (!opts.report_err) return error.NotCoercible; 28262 return sema.failWithNeededComptime(block, inst_src, .{ 28263 .needed_comptime_reason = "value being casted to 'comptime_int' must be comptime-known", 28264 }); 28265 } 28266 break :float; 28267 }; 28268 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact); 28269 return Air.internedToRef(result_val.toIntern()); 28270 }, 28271 .Int, .ComptimeInt => { 28272 if (maybe_inst_val) |val| { 28273 // comptime-known integer to other number 28274 if (!(try sema.intFitsInType(val, dest_ty, null))) { 28275 if (!opts.report_err) return error.NotCoercible; 28276 return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) }); 28277 } 28278 return switch (mod.intern_pool.indexToKey(val.toIntern())) { 28279 .undef => try mod.undefRef(dest_ty), 28280 .int => |int| Air.internedToRef( 28281 try mod.intern_pool.getCoercedInts(mod.gpa, int, dest_ty.toIntern()), 28282 ), 28283 else => unreachable, 28284 }; 28285 } 28286 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { 28287 if (!opts.report_err) return error.NotCoercible; 28288 if (opts.no_cast_to_comptime_int) return inst; 28289 return sema.failWithNeededComptime(block, inst_src, .{ 28290 .needed_comptime_reason = "value being casted to 'comptime_int' must be comptime-known", 28291 }); 28292 } 28293 28294 // integer widening 28295 const dst_info = dest_ty.intInfo(mod); 28296 const src_info = inst_ty.intInfo(mod); 28297 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or 28298 // small enough unsigned ints can get casted to large enough signed ints 28299 (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) 28300 { 28301 try sema.requireRuntimeBlock(block, inst_src, null); 28302 return block.addTyOp(.intcast, dest_ty, inst); 28303 } 28304 }, 28305 else => {}, 28306 }, 28307 .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) { 28308 .ComptimeFloat => { 28309 const val = try sema.resolveConstDefinedValue(block, .unneeded, inst, undefined); 28310 const result_val = try val.floatCast(dest_ty, mod); 28311 return Air.internedToRef(result_val.toIntern()); 28312 }, 28313 .Float => { 28314 if (maybe_inst_val) |val| { 28315 const result_val = try val.floatCast(dest_ty, mod); 28316 if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) { 28317 return sema.fail( 28318 block, 28319 inst_src, 28320 "type '{}' cannot represent float value '{}'", 28321 .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) }, 28322 ); 28323 } 28324 return Air.internedToRef(result_val.toIntern()); 28325 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { 28326 if (!opts.report_err) return error.NotCoercible; 28327 return sema.failWithNeededComptime(block, inst_src, .{ 28328 .needed_comptime_reason = "value being casted to 'comptime_float' must be comptime-known", 28329 }); 28330 } 28331 28332 // float widening 28333 const src_bits = inst_ty.floatBits(target); 28334 const dst_bits = dest_ty.floatBits(target); 28335 if (dst_bits >= src_bits) { 28336 try sema.requireRuntimeBlock(block, inst_src, null); 28337 return block.addTyOp(.fpext, dest_ty, inst); 28338 } 28339 }, 28340 .Int, .ComptimeInt => int: { 28341 const val = maybe_inst_val orelse { 28342 if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { 28343 if (!opts.report_err) return error.NotCoercible; 28344 return sema.failWithNeededComptime(block, inst_src, .{ 28345 .needed_comptime_reason = "value being casted to 'comptime_float' must be comptime-known", 28346 }); 28347 } 28348 break :int; 28349 }; 28350 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, mod, sema); 28351 // TODO implement this compile error 28352 //const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty); 28353 //if (!int_again_val.eql(val, inst_ty, mod)) { 28354 // return sema.fail( 28355 // block, 28356 // inst_src, 28357 // "type '{}' cannot represent integer value '{}'", 28358 // .{ dest_ty.fmt(mod), val }, 28359 // ); 28360 //} 28361 return Air.internedToRef(result_val.toIntern()); 28362 }, 28363 else => {}, 28364 }, 28365 .Enum => switch (inst_ty.zigTypeTag(mod)) { 28366 .EnumLiteral => { 28367 // enum literal to enum 28368 const val = try sema.resolveConstDefinedValue(block, .unneeded, inst, undefined); 28369 const string = mod.intern_pool.indexToKey(val.toIntern()).enum_literal; 28370 const field_index = dest_ty.enumFieldIndex(string, mod) orelse { 28371 const msg = msg: { 28372 const msg = try sema.errMsg( 28373 block, 28374 inst_src, 28375 "no field named '{}' in enum '{}'", 28376 .{ string.fmt(&mod.intern_pool), dest_ty.fmt(mod) }, 28377 ); 28378 errdefer msg.destroy(sema.gpa); 28379 try sema.addDeclaredHereNote(msg, dest_ty); 28380 break :msg msg; 28381 }; 28382 return sema.failWithOwnedErrorMsg(block, msg); 28383 }; 28384 return Air.internedToRef((try mod.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern()); 28385 }, 28386 .Union => blk: { 28387 // union to its own tag type 28388 const union_tag_ty = inst_ty.unionTagType(mod) orelse break :blk; 28389 if (union_tag_ty.eql(dest_ty, mod)) { 28390 return sema.unionToTag(block, dest_ty, inst, inst_src); 28391 } 28392 }, 28393 else => {}, 28394 }, 28395 .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) { 28396 .ErrorUnion => eu: { 28397 if (maybe_inst_val) |inst_val| { 28398 switch (inst_val.toIntern()) { 28399 .undef => return mod.undefRef(dest_ty), 28400 else => switch (mod.intern_pool.indexToKey(inst_val.toIntern())) { 28401 .error_union => |error_union| switch (error_union.val) { 28402 .err_name => |err_name| { 28403 const error_set_ty = inst_ty.errorUnionSet(mod); 28404 const error_set_val = Air.internedToRef((try mod.intern(.{ .err = .{ 28405 .ty = error_set_ty.toIntern(), 28406 .name = err_name, 28407 } }))); 28408 return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src); 28409 }, 28410 .payload => |payload| { 28411 const payload_val = Air.internedToRef(payload); 28412 return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) { 28413 error.NotCoercible => break :eu, 28414 else => |e| return e, 28415 }; 28416 }, 28417 }, 28418 else => unreachable, 28419 }, 28420 } 28421 } 28422 }, 28423 .ErrorSet => { 28424 // E to E!T 28425 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src); 28426 }, 28427 else => eu: { 28428 // T to E!T 28429 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) { 28430 error.NotCoercible => break :eu, 28431 else => |e| return e, 28432 }; 28433 }, 28434 }, 28435 .Union => switch (inst_ty.zigTypeTag(mod)) { 28436 .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), 28437 .Struct => { 28438 if (inst_ty.isAnonStruct(mod)) { 28439 return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src); 28440 } 28441 }, 28442 else => {}, 28443 }, 28444 .Array => switch (inst_ty.zigTypeTag(mod)) { 28445 .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 28446 .Struct => { 28447 if (inst == .empty_struct) { 28448 return sema.arrayInitEmpty(block, inst_src, dest_ty); 28449 } 28450 if (inst_ty.isTuple(mod)) { 28451 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 28452 } 28453 }, 28454 else => {}, 28455 }, 28456 .Vector => switch (inst_ty.zigTypeTag(mod)) { 28457 .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 28458 .Struct => { 28459 if (inst_ty.isTuple(mod)) { 28460 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 28461 } 28462 }, 28463 else => {}, 28464 }, 28465 .Struct => blk: { 28466 if (inst == .empty_struct) { 28467 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src); 28468 } 28469 if (inst_ty.isTupleOrAnonStruct(mod)) { 28470 return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) { 28471 error.NotCoercible => break :blk, 28472 else => |e| return e, 28473 }; 28474 } 28475 }, 28476 else => {}, 28477 } 28478 28479 // undefined to anything. We do this after the big switch above so that 28480 // special logic has a chance to run first, such as `*[N]T` to `[]T` which 28481 // should initialize the length field of the slice. 28482 if (maybe_inst_val) |val| if (val.toIntern() == .undef) return mod.undefRef(dest_ty); 28483 28484 if (!opts.report_err) return error.NotCoercible; 28485 28486 if (opts.is_ret and dest_ty.zigTypeTag(mod) == .NoReturn) { 28487 const msg = msg: { 28488 const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{}); 28489 errdefer msg.destroy(sema.gpa); 28490 28491 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 28492 const src_decl = mod.funcOwnerDeclPtr(sema.func_index); 28493 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{}); 28494 break :msg msg; 28495 }; 28496 return sema.failWithOwnedErrorMsg(block, msg); 28497 } 28498 28499 const msg = msg: { 28500 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), inst_ty.fmt(mod) }); 28501 errdefer msg.destroy(sema.gpa); 28502 28503 // E!T to T 28504 if (inst_ty.zigTypeTag(mod) == .ErrorUnion and 28505 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) 28506 { 28507 try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{}); 28508 try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); 28509 } 28510 28511 // ?T to T 28512 if (inst_ty.zigTypeTag(mod) == .Optional and 28513 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) 28514 { 28515 try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{}); 28516 try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 28517 } 28518 28519 try in_memory_result.report(sema, block, inst_src, msg); 28520 28521 // Add notes about function return type 28522 if (opts.is_ret and 28523 mod.test_functions.get(mod.funcOwnerDeclIndex(sema.func_index)) == null) 28524 { 28525 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 28526 const src_decl = mod.funcOwnerDeclPtr(sema.func_index); 28527 if (inst_ty.isError(mod) and !dest_ty.isError(mod)) { 28528 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{}); 28529 } else { 28530 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{}); 28531 } 28532 } 28533 28534 if (try opts.param_src.get(sema)) |param_src| { 28535 try mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{}); 28536 } 28537 28538 // TODO maybe add "cannot store an error in type '{}'" note 28539 28540 break :msg msg; 28541 }; 28542 return sema.failWithOwnedErrorMsg(block, msg); 28543 } 28544 28545 fn coerceInMemory( 28546 sema: *Sema, 28547 val: Value, 28548 dst_ty: Type, 28549 ) CompileError!Air.Inst.Ref { 28550 return Air.internedToRef((try sema.mod.getCoerced(val, dst_ty)).toIntern()); 28551 } 28552 28553 const InMemoryCoercionResult = union(enum) { 28554 ok, 28555 no_match: Pair, 28556 int_not_coercible: Int, 28557 error_union_payload: PairAndChild, 28558 array_len: IntPair, 28559 array_sentinel: Sentinel, 28560 array_elem: PairAndChild, 28561 vector_len: IntPair, 28562 vector_elem: PairAndChild, 28563 optional_shape: Pair, 28564 optional_child: PairAndChild, 28565 from_anyerror, 28566 missing_error: []const InternPool.NullTerminatedString, 28567 /// true if wanted is var args 28568 fn_var_args: bool, 28569 /// true if wanted is generic 28570 fn_generic: bool, 28571 fn_param_count: IntPair, 28572 fn_param_noalias: IntPair, 28573 fn_param_comptime: ComptimeParam, 28574 fn_param: Param, 28575 fn_cc: CC, 28576 fn_return_type: PairAndChild, 28577 ptr_child: PairAndChild, 28578 ptr_addrspace: AddressSpace, 28579 ptr_sentinel: Sentinel, 28580 ptr_size: Size, 28581 ptr_qualifiers: Qualifiers, 28582 ptr_allowzero: Pair, 28583 ptr_bit_range: BitRange, 28584 ptr_alignment: AlignPair, 28585 double_ptr_to_anyopaque: Pair, 28586 slice_to_anyopaque: Pair, 28587 28588 const Pair = struct { 28589 actual: Type, 28590 wanted: Type, 28591 }; 28592 28593 const PairAndChild = struct { 28594 child: *InMemoryCoercionResult, 28595 actual: Type, 28596 wanted: Type, 28597 }; 28598 28599 const Param = struct { 28600 child: *InMemoryCoercionResult, 28601 actual: Type, 28602 wanted: Type, 28603 index: u64, 28604 }; 28605 28606 const ComptimeParam = struct { 28607 index: u64, 28608 wanted: bool, 28609 }; 28610 28611 const Sentinel = struct { 28612 // unreachable_value indicates no sentinel 28613 actual: Value, 28614 wanted: Value, 28615 ty: Type, 28616 }; 28617 28618 const Int = struct { 28619 actual_signedness: std.builtin.Signedness, 28620 wanted_signedness: std.builtin.Signedness, 28621 actual_bits: u16, 28622 wanted_bits: u16, 28623 }; 28624 28625 const IntPair = struct { 28626 actual: u64, 28627 wanted: u64, 28628 }; 28629 28630 const AlignPair = struct { 28631 actual: Alignment, 28632 wanted: Alignment, 28633 }; 28634 28635 const Size = struct { 28636 actual: std.builtin.Type.Pointer.Size, 28637 wanted: std.builtin.Type.Pointer.Size, 28638 }; 28639 28640 const Qualifiers = struct { 28641 actual_const: bool, 28642 wanted_const: bool, 28643 actual_volatile: bool, 28644 wanted_volatile: bool, 28645 }; 28646 28647 const AddressSpace = struct { 28648 actual: std.builtin.AddressSpace, 28649 wanted: std.builtin.AddressSpace, 28650 }; 28651 28652 const CC = struct { 28653 actual: std.builtin.CallingConvention, 28654 wanted: std.builtin.CallingConvention, 28655 }; 28656 28657 const BitRange = struct { 28658 actual_host: u16, 28659 wanted_host: u16, 28660 actual_offset: u16, 28661 wanted_offset: u16, 28662 }; 28663 28664 fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult { 28665 const res = try arena.create(InMemoryCoercionResult); 28666 res.* = child.*; 28667 return res; 28668 } 28669 28670 fn report(res: *const InMemoryCoercionResult, sema: *Sema, block: *Block, src: LazySrcLoc, msg: *Module.ErrorMsg) !void { 28671 const mod = sema.mod; 28672 var cur = res; 28673 while (true) switch (cur.*) { 28674 .ok => unreachable, 28675 .no_match => |types| { 28676 try sema.addDeclaredHereNote(msg, types.wanted); 28677 try sema.addDeclaredHereNote(msg, types.actual); 28678 break; 28679 }, 28680 .int_not_coercible => |int| { 28681 try sema.errNote(block, src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{ 28682 @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits, 28683 }); 28684 break; 28685 }, 28686 .error_union_payload => |pair| { 28687 try sema.errNote(block, src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{ 28688 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28689 }); 28690 cur = pair.child; 28691 }, 28692 .array_len => |lens| { 28693 try sema.errNote(block, src, msg, "array of length {d} cannot cast into an array of length {d}", .{ 28694 lens.actual, lens.wanted, 28695 }); 28696 break; 28697 }, 28698 .array_sentinel => |sentinel| { 28699 if (sentinel.actual.toIntern() != .unreachable_value) { 28700 try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{ 28701 sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod), 28702 }); 28703 } else { 28704 try sema.errNote(block, src, msg, "destination array requires '{}' sentinel", .{ 28705 sentinel.wanted.fmtValue(sentinel.ty, mod), 28706 }); 28707 } 28708 break; 28709 }, 28710 .array_elem => |pair| { 28711 try sema.errNote(block, src, msg, "array element type '{}' cannot cast into array element type '{}'", .{ 28712 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28713 }); 28714 cur = pair.child; 28715 }, 28716 .vector_len => |lens| { 28717 try sema.errNote(block, src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{ 28718 lens.actual, lens.wanted, 28719 }); 28720 break; 28721 }, 28722 .vector_elem => |pair| { 28723 try sema.errNote(block, src, msg, "vector element type '{}' cannot cast into vector element type '{}'", .{ 28724 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28725 }); 28726 cur = pair.child; 28727 }, 28728 .optional_shape => |pair| { 28729 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ 28730 pair.actual.optionalChild(mod).fmt(mod), pair.wanted.optionalChild(mod).fmt(mod), 28731 }); 28732 break; 28733 }, 28734 .optional_child => |pair| { 28735 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ 28736 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28737 }); 28738 cur = pair.child; 28739 }, 28740 .from_anyerror => { 28741 try sema.errNote(block, src, msg, "global error set cannot cast into a smaller set", .{}); 28742 break; 28743 }, 28744 .missing_error => |missing_errors| { 28745 for (missing_errors) |err| { 28746 try sema.errNote(block, src, msg, "'error.{}' not a member of destination error set", .{err.fmt(&mod.intern_pool)}); 28747 } 28748 break; 28749 }, 28750 .fn_var_args => |wanted_var_args| { 28751 if (wanted_var_args) { 28752 try sema.errNote(block, src, msg, "non-variadic function cannot cast into a variadic function", .{}); 28753 } else { 28754 try sema.errNote(block, src, msg, "variadic function cannot cast into a non-variadic function", .{}); 28755 } 28756 break; 28757 }, 28758 .fn_generic => |wanted_generic| { 28759 if (wanted_generic) { 28760 try sema.errNote(block, src, msg, "non-generic function cannot cast into a generic function", .{}); 28761 } else { 28762 try sema.errNote(block, src, msg, "generic function cannot cast into a non-generic function", .{}); 28763 } 28764 break; 28765 }, 28766 .fn_param_count => |lens| { 28767 try sema.errNote(block, src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{ 28768 lens.actual, lens.wanted, 28769 }); 28770 break; 28771 }, 28772 .fn_param_noalias => |param| { 28773 var index: u6 = 0; 28774 var actual_noalias = false; 28775 while (true) : (index += 1) { 28776 const actual: u1 = @truncate(param.actual >> index); 28777 const wanted: u1 = @truncate(param.wanted >> index); 28778 if (actual != wanted) { 28779 actual_noalias = actual == 1; 28780 break; 28781 } 28782 } 28783 if (!actual_noalias) { 28784 try sema.errNote(block, src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index}); 28785 } else { 28786 try sema.errNote(block, src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index}); 28787 } 28788 break; 28789 }, 28790 .fn_param_comptime => |param| { 28791 if (param.wanted) { 28792 try sema.errNote(block, src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index}); 28793 } else { 28794 try sema.errNote(block, src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index}); 28795 } 28796 break; 28797 }, 28798 .fn_param => |param| { 28799 try sema.errNote(block, src, msg, "parameter {d} '{}' cannot cast into '{}'", .{ 28800 param.index, param.actual.fmt(mod), param.wanted.fmt(mod), 28801 }); 28802 cur = param.child; 28803 }, 28804 .fn_cc => |cc| { 28805 try sema.errNote(block, src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) }); 28806 break; 28807 }, 28808 .fn_return_type => |pair| { 28809 try sema.errNote(block, src, msg, "return type '{}' cannot cast into return type '{}'", .{ 28810 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28811 }); 28812 cur = pair.child; 28813 }, 28814 .ptr_child => |pair| { 28815 try sema.errNote(block, src, msg, "pointer type child '{}' cannot cast into pointer type child '{}'", .{ 28816 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28817 }); 28818 cur = pair.child; 28819 }, 28820 .ptr_addrspace => |@"addrspace"| { 28821 try sema.errNote(block, src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) }); 28822 break; 28823 }, 28824 .ptr_sentinel => |sentinel| { 28825 if (sentinel.actual.toIntern() != .unreachable_value) { 28826 try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{ 28827 sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod), 28828 }); 28829 } else { 28830 try sema.errNote(block, src, msg, "destination pointer requires '{}' sentinel", .{ 28831 sentinel.wanted.fmtValue(sentinel.ty, mod), 28832 }); 28833 } 28834 break; 28835 }, 28836 .ptr_size => |size| { 28837 try sema.errNote(block, src, msg, "a {s} pointer cannot cast into a {s} pointer", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) }); 28838 break; 28839 }, 28840 .ptr_qualifiers => |qualifiers| { 28841 const ok_const = !qualifiers.actual_const or qualifiers.wanted_const; 28842 const ok_volatile = !qualifiers.actual_volatile or qualifiers.wanted_volatile; 28843 if (!ok_const) { 28844 try sema.errNote(block, src, msg, "cast discards const qualifier", .{}); 28845 } else if (!ok_volatile) { 28846 try sema.errNote(block, src, msg, "cast discards volatile qualifier", .{}); 28847 } 28848 break; 28849 }, 28850 .ptr_allowzero => |pair| { 28851 const wanted_allow_zero = pair.wanted.ptrAllowsZero(mod); 28852 const actual_allow_zero = pair.actual.ptrAllowsZero(mod); 28853 if (actual_allow_zero and !wanted_allow_zero) { 28854 try sema.errNote(block, src, msg, "'{}' could have null values which are illegal in type '{}'", .{ 28855 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28856 }); 28857 } else { 28858 try sema.errNote(block, src, msg, "mutable '{}' allows illegal null values stored to type '{}'", .{ 28859 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28860 }); 28861 } 28862 break; 28863 }, 28864 .ptr_bit_range => |bit_range| { 28865 if (bit_range.actual_host != bit_range.wanted_host) { 28866 try sema.errNote(block, src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{ 28867 bit_range.actual_host, bit_range.wanted_host, 28868 }); 28869 } 28870 if (bit_range.actual_offset != bit_range.wanted_offset) { 28871 try sema.errNote(block, src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{ 28872 bit_range.actual_offset, bit_range.wanted_offset, 28873 }); 28874 } 28875 break; 28876 }, 28877 .ptr_alignment => |pair| { 28878 try sema.errNote(block, src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{ 28879 pair.actual.toByteUnits(0), pair.wanted.toByteUnits(0), 28880 }); 28881 break; 28882 }, 28883 .double_ptr_to_anyopaque => |pair| { 28884 try sema.errNote(block, src, msg, "cannot implicitly cast double pointer '{}' to anyopaque pointer '{}'", .{ 28885 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28886 }); 28887 break; 28888 }, 28889 .slice_to_anyopaque => |pair| { 28890 try sema.errNote(block, src, msg, "cannot implicitly cast slice '{}' to anyopaque pointer '{}'", .{ 28891 pair.actual.fmt(mod), pair.wanted.fmt(mod), 28892 }); 28893 try sema.errNote(block, src, msg, "consider using '.ptr'", .{}); 28894 break; 28895 }, 28896 }; 28897 } 28898 }; 28899 28900 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 { 28901 return switch (size) { 28902 .One => "single", 28903 .Many => "many", 28904 .C => "C", 28905 .Slice => unreachable, 28906 }; 28907 } 28908 28909 /// If pointers have the same representation in runtime memory, a bitcast AIR instruction 28910 /// may be used for the coercion. 28911 /// * `const` attribute can be gained 28912 /// * `volatile` attribute can be gained 28913 /// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut 28914 /// * alignment can be decreased 28915 /// * bit offset attributes must match exactly 28916 /// * `*`/`[*]` must match exactly, but `[*c]` matches either one 28917 /// * sentinel-terminated pointers can coerce into `[*]` 28918 fn coerceInMemoryAllowed( 28919 sema: *Sema, 28920 block: *Block, 28921 dest_ty: Type, 28922 src_ty: Type, 28923 dest_is_mut: bool, 28924 target: std.Target, 28925 dest_src: LazySrcLoc, 28926 src_src: LazySrcLoc, 28927 ) CompileError!InMemoryCoercionResult { 28928 const mod = sema.mod; 28929 28930 if (dest_ty.eql(src_ty, mod)) 28931 return .ok; 28932 28933 const dest_tag = dest_ty.zigTypeTag(mod); 28934 const src_tag = src_ty.zigTypeTag(mod); 28935 28936 // Differently-named integers with the same number of bits. 28937 if (dest_tag == .Int and src_tag == .Int) { 28938 const dest_info = dest_ty.intInfo(mod); 28939 const src_info = src_ty.intInfo(mod); 28940 28941 if (dest_info.signedness == src_info.signedness and 28942 dest_info.bits == src_info.bits) 28943 { 28944 return .ok; 28945 } 28946 28947 if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or 28948 // small enough unsigned ints can get casted to large enough signed ints 28949 (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or 28950 (dest_info.signedness == .unsigned and src_info.signedness == .signed)) 28951 { 28952 return InMemoryCoercionResult{ .int_not_coercible = .{ 28953 .actual_signedness = src_info.signedness, 28954 .wanted_signedness = dest_info.signedness, 28955 .actual_bits = src_info.bits, 28956 .wanted_bits = dest_info.bits, 28957 } }; 28958 } 28959 } 28960 28961 // Differently-named floats with the same number of bits. 28962 if (dest_tag == .Float and src_tag == .Float) { 28963 const dest_bits = dest_ty.floatBits(target); 28964 const src_bits = src_ty.floatBits(target); 28965 if (dest_bits == src_bits) { 28966 return .ok; 28967 } 28968 } 28969 28970 // Pointers / Pointer-like Optionals 28971 const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty); 28972 const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty); 28973 if (maybe_dest_ptr_ty) |dest_ptr_ty| { 28974 if (maybe_src_ptr_ty) |src_ptr_ty| { 28975 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src); 28976 } 28977 } 28978 28979 // Slices 28980 if (dest_ty.isSlice(mod) and src_ty.isSlice(mod)) { 28981 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); 28982 } 28983 28984 // Functions 28985 if (dest_tag == .Fn and src_tag == .Fn) { 28986 return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, target, dest_src, src_src); 28987 } 28988 28989 // Error Unions 28990 if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) { 28991 const dest_payload = dest_ty.errorUnionPayload(mod); 28992 const src_payload = src_ty.errorUnionPayload(mod); 28993 const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src); 28994 if (child != .ok) { 28995 return InMemoryCoercionResult{ .error_union_payload = .{ 28996 .child = try child.dupe(sema.arena), 28997 .actual = src_payload, 28998 .wanted = dest_payload, 28999 } }; 29000 } 29001 return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src); 29002 } 29003 29004 // Error Sets 29005 if (dest_tag == .ErrorSet and src_tag == .ErrorSet) { 29006 return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src); 29007 } 29008 29009 // Arrays 29010 if (dest_tag == .Array and src_tag == .Array) { 29011 const dest_info = dest_ty.arrayInfo(mod); 29012 const src_info = src_ty.arrayInfo(mod); 29013 if (dest_info.len != src_info.len) { 29014 return InMemoryCoercionResult{ .array_len = .{ 29015 .actual = src_info.len, 29016 .wanted = dest_info.len, 29017 } }; 29018 } 29019 29020 const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src); 29021 if (child != .ok) { 29022 return InMemoryCoercionResult{ .array_elem = .{ 29023 .child = try child.dupe(sema.arena), 29024 .actual = src_info.elem_type, 29025 .wanted = dest_info.elem_type, 29026 } }; 29027 } 29028 const ok_sent = dest_info.sentinel == null or 29029 (src_info.sentinel != null and 29030 dest_info.sentinel.?.eql( 29031 try mod.getCoerced(src_info.sentinel.?, dest_info.elem_type), 29032 dest_info.elem_type, 29033 mod, 29034 )); 29035 if (!ok_sent) { 29036 return InMemoryCoercionResult{ .array_sentinel = .{ 29037 .actual = src_info.sentinel orelse Value.@"unreachable", 29038 .wanted = dest_info.sentinel orelse Value.@"unreachable", 29039 .ty = dest_info.elem_type, 29040 } }; 29041 } 29042 return .ok; 29043 } 29044 29045 // Vectors 29046 if (dest_tag == .Vector and src_tag == .Vector) { 29047 const dest_len = dest_ty.vectorLen(mod); 29048 const src_len = src_ty.vectorLen(mod); 29049 if (dest_len != src_len) { 29050 return InMemoryCoercionResult{ .vector_len = .{ 29051 .actual = src_len, 29052 .wanted = dest_len, 29053 } }; 29054 } 29055 29056 const dest_elem_ty = dest_ty.scalarType(mod); 29057 const src_elem_ty = src_ty.scalarType(mod); 29058 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src); 29059 if (child != .ok) { 29060 return InMemoryCoercionResult{ .vector_elem = .{ 29061 .child = try child.dupe(sema.arena), 29062 .actual = src_elem_ty, 29063 .wanted = dest_elem_ty, 29064 } }; 29065 } 29066 29067 return .ok; 29068 } 29069 29070 // Arrays <-> Vectors 29071 if ((dest_tag == .Vector and src_tag == .Array) or 29072 (dest_tag == .Array and src_tag == .Vector)) 29073 { 29074 const dest_len = dest_ty.arrayLen(mod); 29075 const src_len = src_ty.arrayLen(mod); 29076 if (dest_len != src_len) { 29077 return InMemoryCoercionResult{ .array_len = .{ 29078 .actual = src_len, 29079 .wanted = dest_len, 29080 } }; 29081 } 29082 29083 const dest_elem_ty = dest_ty.childType(mod); 29084 const src_elem_ty = src_ty.childType(mod); 29085 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src); 29086 if (child != .ok) { 29087 return InMemoryCoercionResult{ .array_elem = .{ 29088 .child = try child.dupe(sema.arena), 29089 .actual = src_elem_ty, 29090 .wanted = dest_elem_ty, 29091 } }; 29092 } 29093 29094 if (dest_tag == .Array) { 29095 const dest_info = dest_ty.arrayInfo(mod); 29096 if (dest_info.sentinel != null) { 29097 return InMemoryCoercionResult{ .array_sentinel = .{ 29098 .actual = Value.@"unreachable", 29099 .wanted = dest_info.sentinel.?, 29100 .ty = dest_info.elem_type, 29101 } }; 29102 } 29103 } 29104 29105 // The memory layout of @Vector(N, iM) is the same as the integer type i(N*M), 29106 // that is to say, the padding bits are not in the same place as the array [N]iM. 29107 // If there's no padding, the bitcast is possible. 29108 const elem_bit_size = dest_elem_ty.bitSize(mod); 29109 const elem_abi_byte_size = dest_elem_ty.abiSize(mod); 29110 if (elem_abi_byte_size * 8 == elem_bit_size) 29111 return .ok; 29112 } 29113 29114 // Optionals 29115 if (dest_tag == .Optional and src_tag == .Optional) { 29116 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) { 29117 return InMemoryCoercionResult{ .optional_shape = .{ 29118 .actual = src_ty, 29119 .wanted = dest_ty, 29120 } }; 29121 } 29122 const dest_child_type = dest_ty.optionalChild(mod); 29123 const src_child_type = src_ty.optionalChild(mod); 29124 29125 const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src); 29126 if (child != .ok) { 29127 return InMemoryCoercionResult{ .optional_child = .{ 29128 .child = try child.dupe(sema.arena), 29129 .actual = src_child_type, 29130 .wanted = dest_child_type, 29131 } }; 29132 } 29133 29134 return .ok; 29135 } 29136 29137 // Tuples (with in-memory-coercible fields) 29138 if (dest_ty.isTuple(mod) and src_ty.isTuple(mod)) tuple: { 29139 if (dest_ty.containerLayout(mod) != src_ty.containerLayout(mod)) break :tuple; 29140 if (dest_ty.structFieldCount(mod) != src_ty.structFieldCount(mod)) break :tuple; 29141 const field_count = dest_ty.structFieldCount(mod); 29142 for (0..field_count) |field_idx| { 29143 if (dest_ty.structFieldIsComptime(field_idx, mod) != src_ty.structFieldIsComptime(field_idx, mod)) break :tuple; 29144 if (dest_ty.structFieldAlign(field_idx, mod) != src_ty.structFieldAlign(field_idx, mod)) break :tuple; 29145 const dest_field_ty = dest_ty.structFieldType(field_idx, mod); 29146 const src_field_ty = src_ty.structFieldType(field_idx, mod); 29147 const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src); 29148 if (field != .ok) break :tuple; 29149 } 29150 return .ok; 29151 } 29152 29153 return InMemoryCoercionResult{ .no_match = .{ 29154 .actual = dest_ty, 29155 .wanted = src_ty, 29156 } }; 29157 } 29158 29159 fn coerceInMemoryAllowedErrorSets( 29160 sema: *Sema, 29161 block: *Block, 29162 dest_ty: Type, 29163 src_ty: Type, 29164 dest_src: LazySrcLoc, 29165 src_src: LazySrcLoc, 29166 ) !InMemoryCoercionResult { 29167 const mod = sema.mod; 29168 const gpa = sema.gpa; 29169 const ip = &mod.intern_pool; 29170 29171 // Coercion to `anyerror`. Note that this check can return false negatives 29172 // in case the error sets did not get resolved. 29173 if (dest_ty.isAnyError(mod)) { 29174 return .ok; 29175 } 29176 29177 if (dest_ty.toIntern() == .adhoc_inferred_error_set_type) { 29178 // We are trying to coerce an error set to the current function's 29179 // inferred error set. 29180 const dst_ies = sema.fn_ret_ty_ies.?; 29181 try dst_ies.addErrorSet(src_ty, ip, sema.arena); 29182 return .ok; 29183 } 29184 29185 if (ip.isInferredErrorSetType(dest_ty.toIntern())) { 29186 const dst_ies_func_index = ip.iesFuncIndex(dest_ty.toIntern()); 29187 if (sema.fn_ret_ty_ies) |dst_ies| { 29188 if (dst_ies.func == dst_ies_func_index) { 29189 // We are trying to coerce an error set to the current function's 29190 // inferred error set. 29191 try dst_ies.addErrorSet(src_ty, ip, sema.arena); 29192 return .ok; 29193 } 29194 } 29195 switch (try sema.resolveInferredErrorSet(block, dest_src, dest_ty.toIntern())) { 29196 // isAnyError might have changed from a false negative to a true 29197 // positive after resolution. 29198 .anyerror_type => return .ok, 29199 else => {}, 29200 } 29201 } 29202 29203 var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa); 29204 defer missing_error_buf.deinit(); 29205 29206 switch (src_ty.toIntern()) { 29207 .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { 29208 .simple_type => unreachable, // filtered out above 29209 .error_set_type, .inferred_error_set_type => return .from_anyerror, 29210 else => unreachable, 29211 }, 29212 29213 else => switch (ip.indexToKey(src_ty.toIntern())) { 29214 .inferred_error_set_type => { 29215 const resolved_src_ty = try sema.resolveInferredErrorSet(block, src_src, src_ty.toIntern()); 29216 // src anyerror status might have changed after the resolution. 29217 if (resolved_src_ty == .anyerror_type) { 29218 // dest_ty.isAnyError(mod) == true is already checked for at this point. 29219 return .from_anyerror; 29220 } 29221 29222 for (ip.indexToKey(resolved_src_ty).error_set_type.names.get(ip)) |key| { 29223 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) { 29224 try missing_error_buf.append(key); 29225 } 29226 } 29227 29228 if (missing_error_buf.items.len != 0) { 29229 return InMemoryCoercionResult{ 29230 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 29231 }; 29232 } 29233 29234 return .ok; 29235 }, 29236 .error_set_type => |error_set_type| { 29237 for (error_set_type.names.get(ip)) |name| { 29238 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) { 29239 try missing_error_buf.append(name); 29240 } 29241 } 29242 29243 if (missing_error_buf.items.len != 0) { 29244 return InMemoryCoercionResult{ 29245 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 29246 }; 29247 } 29248 29249 return .ok; 29250 }, 29251 else => unreachable, 29252 }, 29253 } 29254 } 29255 29256 fn coerceInMemoryAllowedFns( 29257 sema: *Sema, 29258 block: *Block, 29259 dest_ty: Type, 29260 src_ty: Type, 29261 target: std.Target, 29262 dest_src: LazySrcLoc, 29263 src_src: LazySrcLoc, 29264 ) !InMemoryCoercionResult { 29265 const mod = sema.mod; 29266 const ip = &mod.intern_pool; 29267 29268 const dest_info = mod.typeToFunc(dest_ty).?; 29269 const src_info = mod.typeToFunc(src_ty).?; 29270 29271 { 29272 if (dest_info.is_var_args != src_info.is_var_args) { 29273 return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; 29274 } 29275 29276 if (dest_info.is_generic != src_info.is_generic) { 29277 return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; 29278 } 29279 29280 if (dest_info.cc != src_info.cc) { 29281 return InMemoryCoercionResult{ .fn_cc = .{ 29282 .actual = src_info.cc, 29283 .wanted = dest_info.cc, 29284 } }; 29285 } 29286 29287 switch (src_info.return_type) { 29288 .noreturn_type, .generic_poison_type => {}, 29289 else => { 29290 const dest_return_type = Type.fromInterned(dest_info.return_type); 29291 const src_return_type = Type.fromInterned(src_info.return_type); 29292 const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src); 29293 if (rt != .ok) { 29294 return InMemoryCoercionResult{ .fn_return_type = .{ 29295 .child = try rt.dupe(sema.arena), 29296 .actual = src_return_type, 29297 .wanted = dest_return_type, 29298 } }; 29299 } 29300 }, 29301 } 29302 } 29303 29304 const params_len = params_len: { 29305 if (dest_info.param_types.len != src_info.param_types.len) { 29306 return InMemoryCoercionResult{ .fn_param_count = .{ 29307 .actual = src_info.param_types.len, 29308 .wanted = dest_info.param_types.len, 29309 } }; 29310 } 29311 29312 if (dest_info.noalias_bits != src_info.noalias_bits) { 29313 return InMemoryCoercionResult{ .fn_param_noalias = .{ 29314 .actual = src_info.noalias_bits, 29315 .wanted = dest_info.noalias_bits, 29316 } }; 29317 } 29318 29319 break :params_len dest_info.param_types.len; 29320 }; 29321 29322 for (0..params_len) |param_i| { 29323 const dest_param_ty = Type.fromInterned(dest_info.param_types.get(ip)[param_i]); 29324 const src_param_ty = Type.fromInterned(src_info.param_types.get(ip)[param_i]); 29325 29326 const param_i_small: u5 = @intCast(param_i); 29327 if (dest_info.paramIsComptime(param_i_small) != src_info.paramIsComptime(param_i_small)) { 29328 return InMemoryCoercionResult{ .fn_param_comptime = .{ 29329 .index = param_i, 29330 .wanted = dest_info.paramIsComptime(param_i_small), 29331 } }; 29332 } 29333 29334 switch (src_param_ty.toIntern()) { 29335 .generic_poison_type => {}, 29336 else => { 29337 // Note: Cast direction is reversed here. 29338 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src); 29339 if (param != .ok) { 29340 return InMemoryCoercionResult{ .fn_param = .{ 29341 .child = try param.dupe(sema.arena), 29342 .actual = src_param_ty, 29343 .wanted = dest_param_ty, 29344 .index = param_i, 29345 } }; 29346 } 29347 }, 29348 } 29349 } 29350 29351 return .ok; 29352 } 29353 29354 fn coerceInMemoryAllowedPtrs( 29355 sema: *Sema, 29356 block: *Block, 29357 dest_ty: Type, 29358 src_ty: Type, 29359 dest_ptr_ty: Type, 29360 src_ptr_ty: Type, 29361 dest_is_mut: bool, 29362 target: std.Target, 29363 dest_src: LazySrcLoc, 29364 src_src: LazySrcLoc, 29365 ) !InMemoryCoercionResult { 29366 const mod = sema.mod; 29367 const dest_info = dest_ptr_ty.ptrInfo(mod); 29368 const src_info = src_ptr_ty.ptrInfo(mod); 29369 29370 const ok_ptr_size = src_info.flags.size == dest_info.flags.size or 29371 src_info.flags.size == .C or dest_info.flags.size == .C; 29372 if (!ok_ptr_size) { 29373 return InMemoryCoercionResult{ .ptr_size = .{ 29374 .actual = src_info.flags.size, 29375 .wanted = dest_info.flags.size, 29376 } }; 29377 } 29378 29379 const ok_cv_qualifiers = 29380 (!src_info.flags.is_const or dest_info.flags.is_const) and 29381 (!src_info.flags.is_volatile or dest_info.flags.is_volatile); 29382 29383 if (!ok_cv_qualifiers) { 29384 return InMemoryCoercionResult{ .ptr_qualifiers = .{ 29385 .actual_const = src_info.flags.is_const, 29386 .wanted_const = dest_info.flags.is_const, 29387 .actual_volatile = src_info.flags.is_volatile, 29388 .wanted_volatile = dest_info.flags.is_volatile, 29389 } }; 29390 } 29391 29392 if (dest_info.flags.address_space != src_info.flags.address_space) { 29393 return InMemoryCoercionResult{ .ptr_addrspace = .{ 29394 .actual = src_info.flags.address_space, 29395 .wanted = dest_info.flags.address_space, 29396 } }; 29397 } 29398 29399 const child = try sema.coerceInMemoryAllowed(block, Type.fromInterned(dest_info.child), Type.fromInterned(src_info.child), !dest_info.flags.is_const, target, dest_src, src_src); 29400 if (child != .ok) { 29401 return InMemoryCoercionResult{ .ptr_child = .{ 29402 .child = try child.dupe(sema.arena), 29403 .actual = Type.fromInterned(src_info.child), 29404 .wanted = Type.fromInterned(dest_info.child), 29405 } }; 29406 } 29407 29408 const dest_allow_zero = dest_ty.ptrAllowsZero(mod); 29409 const src_allow_zero = src_ty.ptrAllowsZero(mod); 29410 29411 const ok_allows_zero = (dest_allow_zero and 29412 (src_allow_zero or !dest_is_mut)) or 29413 (!dest_allow_zero and !src_allow_zero); 29414 if (!ok_allows_zero) { 29415 return InMemoryCoercionResult{ .ptr_allowzero = .{ 29416 .actual = src_ty, 29417 .wanted = dest_ty, 29418 } }; 29419 } 29420 29421 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or 29422 src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) 29423 { 29424 return InMemoryCoercionResult{ .ptr_bit_range = .{ 29425 .actual_host = src_info.packed_offset.host_size, 29426 .wanted_host = dest_info.packed_offset.host_size, 29427 .actual_offset = src_info.packed_offset.bit_offset, 29428 .wanted_offset = dest_info.packed_offset.bit_offset, 29429 } }; 29430 } 29431 29432 const ok_sent = dest_info.sentinel == .none or src_info.flags.size == .C or 29433 (src_info.sentinel != .none and 29434 dest_info.sentinel == try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child)); 29435 if (!ok_sent) { 29436 return InMemoryCoercionResult{ .ptr_sentinel = .{ 29437 .actual = switch (src_info.sentinel) { 29438 .none => Value.@"unreachable", 29439 else => Value.fromInterned(src_info.sentinel), 29440 }, 29441 .wanted = switch (dest_info.sentinel) { 29442 .none => Value.@"unreachable", 29443 else => Value.fromInterned(dest_info.sentinel), 29444 }, 29445 .ty = Type.fromInterned(dest_info.child), 29446 } }; 29447 } 29448 29449 // If both pointers have alignment 0, it means they both want ABI alignment. 29450 // In this case, if they share the same child type, no need to resolve 29451 // pointee type alignment. Otherwise both pointee types must have their alignment 29452 // resolved and we compare the alignment numerically. 29453 if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or 29454 dest_info.child != src_info.child) 29455 { 29456 const src_align = if (src_info.flags.alignment != .none) 29457 src_info.flags.alignment 29458 else 29459 try sema.typeAbiAlignment(Type.fromInterned(src_info.child)); 29460 29461 const dest_align = if (dest_info.flags.alignment != .none) 29462 dest_info.flags.alignment 29463 else 29464 try sema.typeAbiAlignment(Type.fromInterned(dest_info.child)); 29465 29466 if (dest_align.compare(.gt, src_align)) { 29467 return InMemoryCoercionResult{ .ptr_alignment = .{ 29468 .actual = src_align, 29469 .wanted = dest_align, 29470 } }; 29471 } 29472 } 29473 29474 return .ok; 29475 } 29476 29477 fn coerceVarArgParam( 29478 sema: *Sema, 29479 block: *Block, 29480 inst: Air.Inst.Ref, 29481 inst_src: LazySrcLoc, 29482 ) !Air.Inst.Ref { 29483 if (block.is_typeof) return inst; 29484 29485 const mod = sema.mod; 29486 const uncasted_ty = sema.typeOf(inst); 29487 const coerced = switch (uncasted_ty.zigTypeTag(mod)) { 29488 // TODO consider casting to c_int/f64 if they fit 29489 .ComptimeInt, .ComptimeFloat => return sema.fail( 29490 block, 29491 inst_src, 29492 "integer and float literals passed to variadic function must be casted to a fixed-size number type", 29493 .{}, 29494 ), 29495 .Fn => fn_ptr: { 29496 const fn_val = try sema.resolveConstDefinedValue(block, .unneeded, inst, undefined); 29497 const fn_decl = fn_val.pointerDecl(mod).?; 29498 break :fn_ptr try sema.analyzeDeclRef(fn_decl); 29499 }, 29500 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), 29501 .Float => float: { 29502 const target = sema.mod.getTarget(); 29503 const double_bits = target.c_type_bit_size(.double); 29504 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget()); 29505 if (inst_bits >= double_bits) break :float inst; 29506 switch (double_bits) { 29507 32 => break :float try sema.coerce(block, Type.f32, inst, inst_src), 29508 64 => break :float try sema.coerce(block, Type.f64, inst, inst_src), 29509 else => unreachable, 29510 } 29511 }, 29512 else => if (uncasted_ty.isAbiInt(mod)) int: { 29513 if (!try sema.validateExternType(uncasted_ty, .param_ty)) break :int inst; 29514 const target = sema.mod.getTarget(); 29515 const uncasted_info = uncasted_ty.intInfo(mod); 29516 if (uncasted_info.bits <= target.c_type_bit_size(switch (uncasted_info.signedness) { 29517 .signed => .int, 29518 .unsigned => .uint, 29519 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 29520 .signed => Type.c_int, 29521 .unsigned => Type.c_uint, 29522 }, inst, inst_src); 29523 if (uncasted_info.bits <= target.c_type_bit_size(switch (uncasted_info.signedness) { 29524 .signed => .long, 29525 .unsigned => .ulong, 29526 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 29527 .signed => Type.c_long, 29528 .unsigned => Type.c_ulong, 29529 }, inst, inst_src); 29530 if (uncasted_info.bits <= target.c_type_bit_size(switch (uncasted_info.signedness) { 29531 .signed => .longlong, 29532 .unsigned => .ulonglong, 29533 })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { 29534 .signed => Type.c_longlong, 29535 .unsigned => Type.c_ulonglong, 29536 }, inst, inst_src); 29537 break :int inst; 29538 } else inst, 29539 }; 29540 29541 const coerced_ty = sema.typeOf(coerced); 29542 if (!try sema.validateExternType(coerced_ty, .param_ty)) { 29543 const msg = msg: { 29544 const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)}); 29545 errdefer msg.destroy(sema.gpa); 29546 29547 const src_decl = sema.mod.declPtr(block.src_decl); 29548 try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty); 29549 29550 try sema.addDeclaredHereNote(msg, coerced_ty); 29551 break :msg msg; 29552 }; 29553 return sema.failWithOwnedErrorMsg(block, msg); 29554 } 29555 return coerced; 29556 } 29557 29558 // TODO migrate callsites to use storePtr2 instead. 29559 fn storePtr( 29560 sema: *Sema, 29561 block: *Block, 29562 src: LazySrcLoc, 29563 ptr: Air.Inst.Ref, 29564 uncasted_operand: Air.Inst.Ref, 29565 ) CompileError!void { 29566 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store; 29567 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag); 29568 } 29569 29570 fn storePtr2( 29571 sema: *Sema, 29572 block: *Block, 29573 src: LazySrcLoc, 29574 ptr: Air.Inst.Ref, 29575 ptr_src: LazySrcLoc, 29576 uncasted_operand: Air.Inst.Ref, 29577 operand_src: LazySrcLoc, 29578 air_tag: Air.Inst.Tag, 29579 ) CompileError!void { 29580 const mod = sema.mod; 29581 const ptr_ty = sema.typeOf(ptr); 29582 if (ptr_ty.isConstPtr(mod)) 29583 return sema.fail(block, ptr_src, "cannot assign to constant", .{}); 29584 29585 const elem_ty = ptr_ty.childType(mod); 29586 29587 // To generate better code for tuples, we detect a tuple operand here, and 29588 // analyze field loads and stores directly. This avoids an extra allocation + memcpy 29589 // which would occur if we used `coerce`. 29590 // However, we avoid this mechanism if the destination element type is a tuple, 29591 // because the regular store will be better for this case. 29592 // If the destination type is a struct we don't want this mechanism to trigger, because 29593 // this code does not handle tuple-to-struct coercion which requires dealing with missing 29594 // fields. 29595 const operand_ty = sema.typeOf(uncasted_operand); 29596 if (operand_ty.isTuple(mod) and elem_ty.zigTypeTag(mod) == .Array) { 29597 const field_count = operand_ty.structFieldCount(mod); 29598 var i: u32 = 0; 29599 while (i < field_count) : (i += 1) { 29600 const elem_src = operand_src; // TODO better source location 29601 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); 29602 const elem_index = try mod.intRef(Type.usize, i); 29603 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true); 29604 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); 29605 } 29606 return; 29607 } 29608 29609 // TODO do the same thing for anon structs as for tuples above. 29610 // However, beware of the need to handle missing/extra fields. 29611 29612 const is_ret = air_tag == .ret_ptr; 29613 29614 // Detect if we are storing an array operand to a bitcasted vector pointer. 29615 // If so, we instead reach through the bitcasted pointer to the vector pointer, 29616 // bitcast the array operand to a vector, and then lower this as a store of 29617 // a vector value to a vector pointer. This generally results in better code, 29618 // as well as working around an LLVM bug: 29619 // https://github.com/ziglang/zig/issues/11154 29620 if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { 29621 const vector_ty = sema.typeOf(vector_ptr).childType(mod); 29622 const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 29623 error.NotCoercible => unreachable, 29624 else => |e| return e, 29625 }; 29626 try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store); 29627 return; 29628 } 29629 29630 const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 29631 error.NotCoercible => unreachable, 29632 else => |e| return e, 29633 }; 29634 const maybe_operand_val = try sema.resolveValue(operand); 29635 29636 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 29637 const operand_val = maybe_operand_val orelse { 29638 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); 29639 break :rs operand_src; 29640 }; 29641 if (ptr_val.isComptimeMutablePtr(mod)) { 29642 try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); 29643 return; 29644 } else break :rs ptr_src; 29645 } else ptr_src; 29646 29647 // We do this after the possible comptime store above, for the case of field_ptr stores 29648 // to unions because we want the comptime tag to be set, even if the field type is void. 29649 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { 29650 return; 29651 } 29652 29653 try sema.requireRuntimeBlock(block, src, runtime_src); 29654 try sema.queueFullTypeResolution(elem_ty); 29655 29656 if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) { 29657 const ptr_inst = ptr.toIndex().?; 29658 const air_tags = sema.air_instructions.items(.tag); 29659 if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) { 29660 const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl; 29661 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 29662 _ = try block.addInst(.{ 29663 .tag = .vector_store_elem, 29664 .data = .{ .vector_store_elem = .{ 29665 .vector_ptr = bin_op.lhs, 29666 .payload = try block.sema.addExtra(Air.Bin{ 29667 .lhs = bin_op.rhs, 29668 .rhs = operand, 29669 }), 29670 } }, 29671 }); 29672 return; 29673 } 29674 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{ 29675 ptr_ty.fmt(sema.mod), 29676 }); 29677 } 29678 29679 const store_inst = if (is_ret) 29680 try block.addBinOp(.store, ptr, operand) 29681 else 29682 try block.addBinOp(air_tag, ptr, operand); 29683 29684 try sema.checkComptimeKnownStore(block, store_inst); 29685 29686 return; 29687 } 29688 29689 /// Given an AIR store instruction, checks whether we are performing a 29690 /// comptime-known store to a local alloc, and updates `maybe_comptime_allocs` 29691 /// accordingly. 29692 fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref) !void { 29693 const store_inst = store_inst_ref.toIndex().?; 29694 const inst_data = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op; 29695 const ptr = inst_data.lhs.toIndex() orelse return; 29696 const operand = inst_data.rhs; 29697 29698 const maybe_base_alloc = sema.base_allocs.get(ptr) orelse return; 29699 const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse return; 29700 29701 ct: { 29702 if (null == try sema.resolveValue(operand)) break :ct; 29703 if (maybe_comptime_alloc.runtime_index != block.runtime_index) break :ct; 29704 return maybe_comptime_alloc.stores.append(sema.arena, store_inst); 29705 } 29706 29707 // Store is runtime-known 29708 _ = sema.maybe_comptime_allocs.remove(maybe_base_alloc); 29709 } 29710 29711 /// Given an AIR instruction transforming a pointer (struct_field_ptr, 29712 /// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a 29713 /// local alloc, and updates `base_allocs` accordingly. 29714 fn checkKnownAllocPtr(sema: *Sema, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void { 29715 const base_ptr_inst = base_ptr.toIndex() orelse return; 29716 const new_ptr_inst = new_ptr.toIndex() orelse return; 29717 const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return; 29718 try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst); 29719 29720 switch (sema.air_instructions.items(.tag)[@intFromEnum(new_ptr_inst)]) { 29721 .optional_payload_ptr_set, .errunion_payload_ptr_set => { 29722 const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return; 29723 try maybe_comptime_alloc.non_elideable_pointers.append(sema.arena, new_ptr_inst); 29724 }, 29725 .ptr_elem_ptr => { 29726 const tmp_air = sema.getTmpAir(); 29727 const pl_idx = tmp_air.instructions.items(.data)[@intFromEnum(new_ptr_inst)].ty_pl.payload; 29728 const bin = tmp_air.extraData(Air.Bin, pl_idx).data; 29729 const index_ref = bin.rhs; 29730 29731 // If the index value is runtime-known, this pointer is also runtime-known, so 29732 // we must in turn make the alloc value runtime-known. 29733 if (null == try sema.resolveValue(index_ref)) { 29734 _ = sema.maybe_comptime_allocs.remove(alloc_inst); 29735 } 29736 }, 29737 else => {}, 29738 } 29739 } 29740 29741 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector 29742 /// pointer. Only if the final element type matches the vector element type, and the 29743 /// lengths match. 29744 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { 29745 const mod = sema.mod; 29746 const array_ty = sema.typeOf(ptr).childType(mod); 29747 if (array_ty.zigTypeTag(mod) != .Array) return null; 29748 var ptr_ref = ptr; 29749 var ptr_inst = ptr_ref.toIndex() orelse return null; 29750 const air_datas = sema.air_instructions.items(.data); 29751 const air_tags = sema.air_instructions.items(.tag); 29752 const vector_ty = while (air_tags[@intFromEnum(ptr_inst)] == .bitcast) { 29753 ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand; 29754 if (!sema.isKnownZigType(ptr_ref, .Pointer)) return null; 29755 const child_ty = sema.typeOf(ptr_ref).childType(mod); 29756 if (child_ty.zigTypeTag(mod) == .Vector) break child_ty; 29757 ptr_inst = ptr_ref.toIndex() orelse return null; 29758 } else return null; 29759 29760 // We have a pointer-to-array and a pointer-to-vector. If the elements and 29761 // lengths match, return the result. 29762 if (array_ty.childType(mod).eql(vector_ty.childType(mod), sema.mod) and 29763 array_ty.arrayLen(mod) == vector_ty.vectorLen(mod)) 29764 { 29765 return ptr_ref; 29766 } else { 29767 return null; 29768 } 29769 } 29770 29771 /// Call when you have Value objects rather than Air instructions, and you want to 29772 /// assert the store must be done at comptime. 29773 fn storePtrVal( 29774 sema: *Sema, 29775 block: *Block, 29776 src: LazySrcLoc, 29777 ptr_val: Value, 29778 operand_val: Value, 29779 operand_ty: Type, 29780 ) !void { 29781 const mod = sema.mod; 29782 var mut_kit = try sema.beginComptimePtrMutation(block, src, ptr_val, operand_ty); 29783 try sema.checkComptimeVarStore(block, src, mut_kit.mut_decl); 29784 29785 switch (mut_kit.pointee) { 29786 .opv => {}, 29787 .direct => |val_ptr| { 29788 if (mut_kit.mut_decl.runtime_index == .comptime_field_ptr) { 29789 val_ptr.* = Value.fromInterned((try val_ptr.intern(operand_ty, mod))); 29790 if (!operand_val.eql(val_ptr.*, operand_ty, mod)) { 29791 // TODO use failWithInvalidComptimeFieldStore 29792 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); 29793 } 29794 return; 29795 } 29796 val_ptr.* = Value.fromInterned((try operand_val.intern(operand_ty, mod))); 29797 }, 29798 .reinterpret => |reinterpret| { 29799 const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod)); 29800 const buffer = try sema.gpa.alloc(u8, abi_size); 29801 defer sema.gpa.free(buffer); 29802 reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) { 29803 error.OutOfMemory => return error.OutOfMemory, 29804 error.ReinterpretDeclRef => unreachable, 29805 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 29806 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), 29807 }; 29808 if (reinterpret.write_packed) { 29809 operand_val.writeToPackedMemory(operand_ty, mod, buffer[reinterpret.byte_offset..], 0) catch |err| switch (err) { 29810 error.OutOfMemory => return error.OutOfMemory, 29811 error.ReinterpretDeclRef => unreachable, 29812 }; 29813 } else { 29814 operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) { 29815 error.OutOfMemory => return error.OutOfMemory, 29816 error.ReinterpretDeclRef => unreachable, 29817 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 29818 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}), 29819 }; 29820 } 29821 const val = Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena) catch |err| switch (err) { 29822 error.OutOfMemory => return error.OutOfMemory, 29823 error.IllDefinedMemoryLayout => unreachable, 29824 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), 29825 }; 29826 reinterpret.val_ptr.* = Value.fromInterned((try val.intern(mut_kit.ty, mod))); 29827 }, 29828 .bad_decl_ty, .bad_ptr_ty => { 29829 // TODO show the decl declaration site in a note and explain whether the decl 29830 // or the pointer is the problematic type 29831 return sema.fail( 29832 block, 29833 src, 29834 "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout", 29835 .{mut_kit.ty.fmt(mod)}, 29836 ); 29837 }, 29838 } 29839 } 29840 29841 const ComptimePtrMutationKit = struct { 29842 mut_decl: InternPool.Key.Ptr.Addr.MutDecl, 29843 pointee: union(enum) { 29844 opv, 29845 /// The pointer type matches the actual comptime Value so a direct 29846 /// modification is possible. 29847 direct: *Value, 29848 /// The largest parent Value containing pointee and having a well-defined memory layout. 29849 /// This is used for bitcasting, if direct dereferencing failed. 29850 reinterpret: struct { 29851 val_ptr: *Value, 29852 byte_offset: usize, 29853 /// If set, write the operand to packed memory 29854 write_packed: bool = false, 29855 }, 29856 /// If the root decl could not be used as parent, this means `ty` is the type that 29857 /// caused that by not having a well-defined layout. 29858 /// This one means the Decl that owns the value trying to be modified does not 29859 /// have a well defined memory layout. 29860 bad_decl_ty, 29861 /// If the root decl could not be used as parent, this means `ty` is the type that 29862 /// caused that by not having a well-defined layout. 29863 /// This one means the pointer type that is being stored through does not 29864 /// have a well defined memory layout. 29865 bad_ptr_ty, 29866 }, 29867 ty: Type, 29868 }; 29869 29870 fn beginComptimePtrMutation( 29871 sema: *Sema, 29872 block: *Block, 29873 src: LazySrcLoc, 29874 ptr_val: Value, 29875 ptr_elem_ty: Type, 29876 ) CompileError!ComptimePtrMutationKit { 29877 const mod = sema.mod; 29878 const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr; 29879 switch (ptr.addr) { 29880 .decl, .anon_decl, .int => unreachable, // isComptimeMutablePtr has been checked already 29881 .mut_decl => |mut_decl| { 29882 const decl = mod.declPtr(mut_decl.decl); 29883 return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl); 29884 }, 29885 .comptime_field => |comptime_field| { 29886 const duped = try sema.arena.create(Value); 29887 duped.* = Value.fromInterned(comptime_field); 29888 return sema.beginComptimePtrMutationInner(block, src, Type.fromInterned(mod.intern_pool.typeOf(comptime_field)), duped, ptr_elem_ty, .{ 29889 .decl = undefined, 29890 .runtime_index = .comptime_field_ptr, 29891 }); 29892 }, 29893 .eu_payload => |eu_ptr| { 29894 const eu_ty = Type.fromInterned(mod.intern_pool.typeOf(eu_ptr)).childType(mod); 29895 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(eu_ptr), eu_ty); 29896 switch (parent.pointee) { 29897 .opv => unreachable, 29898 .direct => |val_ptr| { 29899 const payload_ty = parent.ty.errorUnionPayload(mod); 29900 if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) { 29901 return ComptimePtrMutationKit{ 29902 .mut_decl = parent.mut_decl, 29903 .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data }, 29904 .ty = payload_ty, 29905 }; 29906 } else { 29907 // An error union has been initialized to undefined at comptime and now we 29908 // are for the first time setting the payload. We must change the 29909 // representation of the error union from `undef` to `opt_payload`. 29910 29911 const payload = try sema.arena.create(Value.Payload.SubValue); 29912 payload.* = .{ 29913 .base = .{ .tag = .eu_payload }, 29914 .data = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))), 29915 }; 29916 29917 val_ptr.* = Value.initPayload(&payload.base); 29918 29919 return ComptimePtrMutationKit{ 29920 .mut_decl = parent.mut_decl, 29921 .pointee = .{ .direct = &payload.data }, 29922 .ty = payload_ty, 29923 }; 29924 } 29925 }, 29926 .bad_decl_ty, .bad_ptr_ty => return parent, 29927 // Even though the parent value type has well-defined memory layout, our 29928 // pointer type does not. 29929 .reinterpret => return ComptimePtrMutationKit{ 29930 .mut_decl = parent.mut_decl, 29931 .pointee = .bad_ptr_ty, 29932 .ty = eu_ty, 29933 }, 29934 } 29935 }, 29936 .opt_payload => |opt_ptr| { 29937 const opt_ty = Type.fromInterned(mod.intern_pool.typeOf(opt_ptr)).childType(mod); 29938 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(opt_ptr), opt_ty); 29939 switch (parent.pointee) { 29940 .opv => unreachable, 29941 .direct => |val_ptr| { 29942 const payload_ty = parent.ty.optionalChild(mod); 29943 switch (val_ptr.ip_index) { 29944 .none => return ComptimePtrMutationKit{ 29945 .mut_decl = parent.mut_decl, 29946 .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data }, 29947 .ty = payload_ty, 29948 }, 29949 else => { 29950 const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) { 29951 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }), 29952 .opt => |opt| switch (opt.val) { 29953 .none => try mod.intern(.{ .undef = payload_ty.toIntern() }), 29954 else => |payload| payload, 29955 }, 29956 else => unreachable, 29957 }; 29958 29959 // An optional has been initialized to undefined at comptime and now we 29960 // are for the first time setting the payload. We must change the 29961 // representation of the optional from `undef` to `opt_payload`. 29962 29963 const payload = try sema.arena.create(Value.Payload.SubValue); 29964 payload.* = .{ 29965 .base = .{ .tag = .opt_payload }, 29966 .data = Value.fromInterned(payload_val), 29967 }; 29968 29969 val_ptr.* = Value.initPayload(&payload.base); 29970 29971 return ComptimePtrMutationKit{ 29972 .mut_decl = parent.mut_decl, 29973 .pointee = .{ .direct = &payload.data }, 29974 .ty = payload_ty, 29975 }; 29976 }, 29977 } 29978 }, 29979 .bad_decl_ty, .bad_ptr_ty => return parent, 29980 // Even though the parent value type has well-defined memory layout, our 29981 // pointer type does not. 29982 .reinterpret => return ComptimePtrMutationKit{ 29983 .mut_decl = parent.mut_decl, 29984 .pointee = .bad_ptr_ty, 29985 .ty = opt_ty, 29986 }, 29987 } 29988 }, 29989 .elem => |elem_ptr| { 29990 const base_elem_ty = Type.fromInterned(mod.intern_pool.typeOf(elem_ptr.base)).elemType2(mod); 29991 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(elem_ptr.base), base_elem_ty); 29992 29993 switch (parent.pointee) { 29994 .opv => unreachable, 29995 .direct => |val_ptr| switch (parent.ty.zigTypeTag(mod)) { 29996 .Array, .Vector => { 29997 const elem_ty = parent.ty.childType(mod); 29998 const check_len = parent.ty.arrayLenIncludingSentinel(mod); 29999 if ((try sema.typeHasOnePossibleValue(ptr_elem_ty)) != null) { 30000 if (elem_ptr.index > check_len) { 30001 // TODO have the parent include the decl so we can say "declared here" 30002 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{ 30003 elem_ptr.index, check_len, 30004 }); 30005 } 30006 return .{ 30007 .mut_decl = parent.mut_decl, 30008 .pointee = .opv, 30009 .ty = elem_ty, 30010 }; 30011 } 30012 if (elem_ptr.index >= check_len) { 30013 // TODO have the parent include the decl so we can say "declared here" 30014 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{ 30015 elem_ptr.index, check_len, 30016 }); 30017 } 30018 30019 // We might have a pointer to multiple elements of the array (e.g. a pointer 30020 // to a sub-array). In this case, we just have to reinterpret the relevant 30021 // bytes of the whole array rather than any single element. 30022 reinterp_multi_elem: { 30023 if (try sema.typeRequiresComptime(base_elem_ty)) break :reinterp_multi_elem; 30024 if (try sema.typeRequiresComptime(ptr_elem_ty)) break :reinterp_multi_elem; 30025 30026 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty); 30027 if (elem_abi_size_u64 >= try sema.typeAbiSize(ptr_elem_ty)) break :reinterp_multi_elem; 30028 30029 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); 30030 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 30031 return .{ 30032 .mut_decl = parent.mut_decl, 30033 .pointee = .{ .reinterpret = .{ 30034 .val_ptr = val_ptr, 30035 .byte_offset = elem_abi_size * elem_idx, 30036 } }, 30037 .ty = parent.ty, 30038 }; 30039 } 30040 30041 switch (val_ptr.ip_index) { 30042 .none => switch (val_ptr.tag()) { 30043 .bytes => { 30044 // An array is memory-optimized to store a slice of bytes, but we are about 30045 // to modify an individual field and the representation has to change. 30046 // If we wanted to avoid this, there would need to be special detection 30047 // elsewhere to identify when writing a value to an array element that is stored 30048 // using the `bytes` tag, and handle it without making a call to this function. 30049 const arena = mod.tmp_hack_arena.allocator(); 30050 30051 const bytes = val_ptr.castTag(.bytes).?.data; 30052 const dest_len = parent.ty.arrayLenIncludingSentinel(mod); 30053 // bytes.len may be one greater than dest_len because of the case when 30054 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. 30055 assert(bytes.len >= dest_len); 30056 const elems = try arena.alloc(Value, @intCast(dest_len)); 30057 for (elems, 0..) |*elem, i| { 30058 elem.* = try mod.intValue(elem_ty, bytes[i]); 30059 } 30060 30061 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 30062 30063 return beginComptimePtrMutationInner( 30064 sema, 30065 block, 30066 src, 30067 elem_ty, 30068 &elems[@intCast(elem_ptr.index)], 30069 ptr_elem_ty, 30070 parent.mut_decl, 30071 ); 30072 }, 30073 .repeated => { 30074 // An array is memory-optimized to store only a single element value, and 30075 // that value is understood to be the same for the entire length of the array. 30076 // However, now we want to modify an individual field and so the 30077 // representation has to change. If we wanted to avoid this, there would 30078 // need to be special detection elsewhere to identify when writing a value to an 30079 // array element that is stored using the `repeated` tag, and handle it 30080 // without making a call to this function. 30081 const arena = mod.tmp_hack_arena.allocator(); 30082 30083 const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod); 30084 const array_len_including_sentinel = 30085 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); 30086 const elems = try arena.alloc(Value, array_len_including_sentinel); 30087 @memset(elems, Value.fromInterned(repeated_val)); 30088 30089 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 30090 30091 return beginComptimePtrMutationInner( 30092 sema, 30093 block, 30094 src, 30095 elem_ty, 30096 &elems[@intCast(elem_ptr.index)], 30097 ptr_elem_ty, 30098 parent.mut_decl, 30099 ); 30100 }, 30101 30102 .aggregate => return beginComptimePtrMutationInner( 30103 sema, 30104 block, 30105 src, 30106 elem_ty, 30107 &val_ptr.castTag(.aggregate).?.data[@intCast(elem_ptr.index)], 30108 ptr_elem_ty, 30109 parent.mut_decl, 30110 ), 30111 30112 else => unreachable, 30113 }, 30114 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) { 30115 .undef => { 30116 // An array has been initialized to undefined at comptime and now we 30117 // are for the first time setting an element. We must change the representation 30118 // of the array from `undef` to `array`. 30119 const arena = mod.tmp_hack_arena.allocator(); 30120 30121 const array_len_including_sentinel = 30122 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); 30123 const elems = try arena.alloc(Value, array_len_including_sentinel); 30124 @memset(elems, Value.fromInterned((try mod.intern(.{ .undef = elem_ty.toIntern() })))); 30125 30126 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 30127 30128 return beginComptimePtrMutationInner( 30129 sema, 30130 block, 30131 src, 30132 elem_ty, 30133 &elems[@intCast(elem_ptr.index)], 30134 ptr_elem_ty, 30135 parent.mut_decl, 30136 ); 30137 }, 30138 else => unreachable, 30139 }, 30140 } 30141 }, 30142 else => { 30143 if (elem_ptr.index != 0) { 30144 // TODO include a "declared here" note for the decl 30145 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{ 30146 elem_ptr.index, 30147 }); 30148 } 30149 return beginComptimePtrMutationInner( 30150 sema, 30151 block, 30152 src, 30153 parent.ty, 30154 val_ptr, 30155 ptr_elem_ty, 30156 parent.mut_decl, 30157 ); 30158 }, 30159 }, 30160 .reinterpret => |reinterpret| { 30161 if (!base_elem_ty.hasWellDefinedLayout(mod)) { 30162 // Even though the parent value type has well-defined memory layout, our 30163 // pointer type does not. 30164 return ComptimePtrMutationKit{ 30165 .mut_decl = parent.mut_decl, 30166 .pointee = .bad_ptr_ty, 30167 .ty = base_elem_ty, 30168 }; 30169 } 30170 30171 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty); 30172 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); 30173 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 30174 return ComptimePtrMutationKit{ 30175 .mut_decl = parent.mut_decl, 30176 .pointee = .{ .reinterpret = .{ 30177 .val_ptr = reinterpret.val_ptr, 30178 .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_idx, 30179 } }, 30180 .ty = parent.ty, 30181 }; 30182 }, 30183 .bad_decl_ty, .bad_ptr_ty => return parent, 30184 } 30185 }, 30186 .field => |field_ptr| { 30187 const base_child_ty = Type.fromInterned(mod.intern_pool.typeOf(field_ptr.base)).childType(mod); 30188 const field_index: u32 = @intCast(field_ptr.index); 30189 30190 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(field_ptr.base), base_child_ty); 30191 switch (parent.pointee) { 30192 .opv => unreachable, 30193 .direct => |val_ptr| switch (val_ptr.ip_index) { 30194 .empty_struct => { 30195 const duped = try sema.arena.create(Value); 30196 duped.* = val_ptr.*; 30197 return beginComptimePtrMutationInner( 30198 sema, 30199 block, 30200 src, 30201 parent.ty.structFieldType(field_index, mod), 30202 duped, 30203 ptr_elem_ty, 30204 parent.mut_decl, 30205 ); 30206 }, 30207 .none => switch (val_ptr.tag()) { 30208 .aggregate => return beginComptimePtrMutationInner( 30209 sema, 30210 block, 30211 src, 30212 parent.ty.structFieldType(field_index, mod), 30213 &val_ptr.castTag(.aggregate).?.data[field_index], 30214 ptr_elem_ty, 30215 parent.mut_decl, 30216 ), 30217 .repeated => { 30218 const arena = mod.tmp_hack_arena.allocator(); 30219 30220 const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod)); 30221 @memset(elems, val_ptr.castTag(.repeated).?.data); 30222 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 30223 30224 return beginComptimePtrMutationInner( 30225 sema, 30226 block, 30227 src, 30228 parent.ty.structFieldType(field_index, mod), 30229 &elems[field_index], 30230 ptr_elem_ty, 30231 parent.mut_decl, 30232 ); 30233 }, 30234 .@"union" => { 30235 const payload = &val_ptr.castTag(.@"union").?.data; 30236 const layout = base_child_ty.containerLayout(mod); 30237 30238 const tag_type = base_child_ty.unionTagTypeHypothetical(mod); 30239 const hypothetical_tag = try mod.enumValueFieldIndex(tag_type, field_index); 30240 if (layout == .Auto or (payload.tag != null and hypothetical_tag.eql(payload.tag.?, tag_type, mod))) { 30241 // We need to set the active field of the union. 30242 payload.tag = hypothetical_tag; 30243 30244 const field_ty = parent.ty.structFieldType(field_index, mod); 30245 return beginComptimePtrMutationInner( 30246 sema, 30247 block, 30248 src, 30249 field_ty, 30250 &payload.val, 30251 ptr_elem_ty, 30252 parent.mut_decl, 30253 ); 30254 } else { 30255 // Writing to a different field (a different or unknown tag is active) requires reinterpreting 30256 // memory of the entire union, which requires knowing its abiSize. 30257 try sema.resolveTypeLayout(parent.ty); 30258 30259 // This union value no longer has a well-defined tag type. 30260 // The reinterpretation will read it back out as .none. 30261 payload.val = try payload.val.unintern(sema.arena, mod); 30262 return ComptimePtrMutationKit{ 30263 .mut_decl = parent.mut_decl, 30264 .pointee = .{ .reinterpret = .{ 30265 .val_ptr = val_ptr, 30266 .byte_offset = 0, 30267 .write_packed = layout == .Packed, 30268 } }, 30269 .ty = parent.ty, 30270 }; 30271 } 30272 }, 30273 .slice => switch (field_index) { 30274 Value.slice_ptr_index => return beginComptimePtrMutationInner( 30275 sema, 30276 block, 30277 src, 30278 parent.ty.slicePtrFieldType(mod), 30279 &val_ptr.castTag(.slice).?.data.ptr, 30280 ptr_elem_ty, 30281 parent.mut_decl, 30282 ), 30283 30284 Value.slice_len_index => return beginComptimePtrMutationInner( 30285 sema, 30286 block, 30287 src, 30288 Type.usize, 30289 &val_ptr.castTag(.slice).?.data.len, 30290 ptr_elem_ty, 30291 parent.mut_decl, 30292 ), 30293 30294 else => unreachable, 30295 }, 30296 else => unreachable, 30297 }, 30298 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) { 30299 .undef => { 30300 // A struct or union has been initialized to undefined at comptime and now we 30301 // are for the first time setting a field. We must change the representation 30302 // of the struct/union from `undef` to `struct`/`union`. 30303 const arena = mod.tmp_hack_arena.allocator(); 30304 30305 switch (parent.ty.zigTypeTag(mod)) { 30306 .Struct => { 30307 const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod)); 30308 for (fields, 0..) |*field, i| field.* = Value.fromInterned((try mod.intern(.{ 30309 .undef = parent.ty.structFieldType(i, mod).toIntern(), 30310 }))); 30311 30312 val_ptr.* = try Value.Tag.aggregate.create(arena, fields); 30313 30314 return beginComptimePtrMutationInner( 30315 sema, 30316 block, 30317 src, 30318 parent.ty.structFieldType(field_index, mod), 30319 &fields[field_index], 30320 ptr_elem_ty, 30321 parent.mut_decl, 30322 ); 30323 }, 30324 .Union => { 30325 const payload = try arena.create(Value.Payload.Union); 30326 const tag_ty = parent.ty.unionTagTypeHypothetical(mod); 30327 const payload_ty = parent.ty.structFieldType(field_index, mod); 30328 payload.* = .{ .data = .{ 30329 .tag = try mod.enumValueFieldIndex(tag_ty, field_index), 30330 .val = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))), 30331 } }; 30332 30333 val_ptr.* = Value.initPayload(&payload.base); 30334 30335 return beginComptimePtrMutationInner( 30336 sema, 30337 block, 30338 src, 30339 payload_ty, 30340 &payload.data.val, 30341 ptr_elem_ty, 30342 parent.mut_decl, 30343 ); 30344 }, 30345 .Pointer => { 30346 assert(parent.ty.isSlice(mod)); 30347 const ptr_ty = parent.ty.slicePtrFieldType(mod); 30348 val_ptr.* = try Value.Tag.slice.create(arena, .{ 30349 .ptr = Value.fromInterned((try mod.intern(.{ .undef = ptr_ty.toIntern() }))), 30350 .len = Value.fromInterned((try mod.intern(.{ .undef = .usize_type }))), 30351 }); 30352 30353 switch (field_index) { 30354 Value.slice_ptr_index => return beginComptimePtrMutationInner( 30355 sema, 30356 block, 30357 src, 30358 ptr_ty, 30359 &val_ptr.castTag(.slice).?.data.ptr, 30360 ptr_elem_ty, 30361 parent.mut_decl, 30362 ), 30363 Value.slice_len_index => return beginComptimePtrMutationInner( 30364 sema, 30365 block, 30366 src, 30367 Type.usize, 30368 &val_ptr.castTag(.slice).?.data.len, 30369 ptr_elem_ty, 30370 parent.mut_decl, 30371 ), 30372 30373 else => unreachable, 30374 } 30375 }, 30376 else => unreachable, 30377 } 30378 }, 30379 else => unreachable, 30380 }, 30381 }, 30382 .reinterpret => |reinterpret| { 30383 const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod); 30384 const field_offset = try sema.usizeCast(block, src, field_offset_u64); 30385 return ComptimePtrMutationKit{ 30386 .mut_decl = parent.mut_decl, 30387 .pointee = .{ .reinterpret = .{ 30388 .val_ptr = reinterpret.val_ptr, 30389 .byte_offset = reinterpret.byte_offset + field_offset, 30390 } }, 30391 .ty = parent.ty, 30392 }; 30393 }, 30394 .bad_decl_ty, .bad_ptr_ty => return parent, 30395 } 30396 }, 30397 } 30398 } 30399 30400 fn beginComptimePtrMutationInner( 30401 sema: *Sema, 30402 block: *Block, 30403 src: LazySrcLoc, 30404 decl_ty: Type, 30405 decl_val: *Value, 30406 ptr_elem_ty: Type, 30407 mut_decl: InternPool.Key.Ptr.Addr.MutDecl, 30408 ) CompileError!ComptimePtrMutationKit { 30409 const mod = sema.mod; 30410 const target = mod.getTarget(); 30411 const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok; 30412 30413 decl_val.* = try decl_val.unintern(sema.arena, mod); 30414 30415 if (coerce_ok) { 30416 return ComptimePtrMutationKit{ 30417 .mut_decl = mut_decl, 30418 .pointee = .{ .direct = decl_val }, 30419 .ty = decl_ty, 30420 }; 30421 } 30422 30423 // Handle the case that the decl is an array and we're actually trying to point to an element. 30424 if (decl_ty.isArrayOrVector(mod)) { 30425 const decl_elem_ty = decl_ty.childType(mod); 30426 if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) { 30427 return ComptimePtrMutationKit{ 30428 .mut_decl = mut_decl, 30429 .pointee = .{ .direct = decl_val }, 30430 .ty = decl_ty, 30431 }; 30432 } 30433 } 30434 30435 if (!decl_ty.hasWellDefinedLayout(mod)) { 30436 return ComptimePtrMutationKit{ 30437 .mut_decl = mut_decl, 30438 .pointee = .bad_decl_ty, 30439 .ty = decl_ty, 30440 }; 30441 } 30442 if (!ptr_elem_ty.hasWellDefinedLayout(mod)) { 30443 return ComptimePtrMutationKit{ 30444 .mut_decl = mut_decl, 30445 .pointee = .bad_ptr_ty, 30446 .ty = ptr_elem_ty, 30447 }; 30448 } 30449 return ComptimePtrMutationKit{ 30450 .mut_decl = mut_decl, 30451 .pointee = .{ .reinterpret = .{ 30452 .val_ptr = decl_val, 30453 .byte_offset = 0, 30454 } }, 30455 .ty = decl_ty, 30456 }; 30457 } 30458 30459 const TypedValueAndOffset = struct { 30460 tv: TypedValue, 30461 byte_offset: usize, 30462 }; 30463 30464 const ComptimePtrLoadKit = struct { 30465 /// The Value and Type corresponding to the pointee of the provided pointer. 30466 /// If a direct dereference is not possible, this is null. 30467 pointee: ?TypedValue, 30468 /// The largest parent Value containing `pointee` and having a well-defined memory layout. 30469 /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null). 30470 parent: ?TypedValueAndOffset, 30471 /// Whether the `pointee` could be mutated by further 30472 /// semantic analysis and a copy must be performed. 30473 is_mutable: bool, 30474 /// If the root decl could not be used as `parent`, this is the type that 30475 /// caused that by not having a well-defined layout 30476 ty_without_well_defined_layout: ?Type, 30477 }; 30478 30479 const ComptimePtrLoadError = CompileError || error{ 30480 RuntimeLoad, 30481 }; 30482 30483 /// If `maybe_array_ty` is provided, it will be used to directly dereference an 30484 /// .elem_ptr of type T to a value of [N]T, if necessary. 30485 fn beginComptimePtrLoad( 30486 sema: *Sema, 30487 block: *Block, 30488 src: LazySrcLoc, 30489 ptr_val: Value, 30490 maybe_array_ty: ?Type, 30491 ) ComptimePtrLoadError!ComptimePtrLoadKit { 30492 const mod = sema.mod; 30493 const ip = &mod.intern_pool; 30494 const target = mod.getTarget(); 30495 30496 var deref: ComptimePtrLoadKit = switch (ip.indexToKey(ptr_val.toIntern())) { 30497 .ptr => |ptr| switch (ptr.addr) { 30498 .decl, .mut_decl => blk: { 30499 const decl_index = switch (ptr.addr) { 30500 .decl => |decl| decl, 30501 .mut_decl => |mut_decl| mut_decl.decl, 30502 else => unreachable, 30503 }; 30504 const is_mutable = ptr.addr == .mut_decl; 30505 const decl = mod.declPtr(decl_index); 30506 const decl_tv = try decl.typedValue(); 30507 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad; 30508 30509 const layout_defined = decl.ty.hasWellDefinedLayout(mod); 30510 break :blk ComptimePtrLoadKit{ 30511 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, 30512 .pointee = decl_tv, 30513 .is_mutable = is_mutable, 30514 .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, 30515 }; 30516 }, 30517 .anon_decl => |anon_decl| blk: { 30518 const decl_val = anon_decl.val; 30519 if (Value.fromInterned(decl_val).getVariable(mod) != null) return error.RuntimeLoad; 30520 const decl_ty = Type.fromInterned(ip.typeOf(decl_val)); 30521 const decl_tv: TypedValue = .{ .ty = decl_ty, .val = Value.fromInterned(decl_val) }; 30522 const layout_defined = decl_ty.hasWellDefinedLayout(mod); 30523 break :blk ComptimePtrLoadKit{ 30524 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, 30525 .pointee = decl_tv, 30526 .is_mutable = false, 30527 .ty_without_well_defined_layout = if (!layout_defined) decl_ty else null, 30528 }; 30529 }, 30530 .int => return error.RuntimeLoad, 30531 .eu_payload, .opt_payload => |container_ptr| blk: { 30532 const container_ty = Type.fromInterned(ip.typeOf(container_ptr)).childType(mod); 30533 const payload_ty = switch (ptr.addr) { 30534 .eu_payload => container_ty.errorUnionPayload(mod), 30535 .opt_payload => container_ty.optionalChild(mod), 30536 else => unreachable, 30537 }; 30538 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(container_ptr), container_ty); 30539 30540 // eu_payload and opt_payload never have a well-defined layout 30541 if (deref.parent != null) { 30542 deref.parent = null; 30543 deref.ty_without_well_defined_layout = container_ty; 30544 } 30545 30546 if (deref.pointee) |*tv| { 30547 const coerce_in_mem_ok = 30548 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or 30549 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok; 30550 if (coerce_in_mem_ok) { 30551 const payload_val = switch (tv.val.ip_index) { 30552 .none => tv.val.cast(Value.Payload.SubValue).?.data, 30553 .null_value => return sema.fail(block, src, "attempt to use null value", .{}), 30554 else => Value.fromInterned(switch (ip.indexToKey(tv.val.toIntern())) { 30555 .error_union => |error_union| switch (error_union.val) { 30556 .err_name => |err_name| return sema.fail( 30557 block, 30558 src, 30559 "attempt to unwrap error: {}", 30560 .{err_name.fmt(ip)}, 30561 ), 30562 .payload => |payload| payload, 30563 }, 30564 .opt => |opt| switch (opt.val) { 30565 .none => return sema.fail(block, src, "attempt to use null value", .{}), 30566 else => |payload| payload, 30567 }, 30568 else => unreachable, 30569 }), 30570 }; 30571 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; 30572 break :blk deref; 30573 } 30574 } 30575 deref.pointee = null; 30576 break :blk deref; 30577 }, 30578 .comptime_field => |comptime_field| blk: { 30579 const field_ty = Type.fromInterned(ip.typeOf(comptime_field)); 30580 break :blk ComptimePtrLoadKit{ 30581 .parent = null, 30582 .pointee = .{ .ty = field_ty, .val = Value.fromInterned(comptime_field) }, 30583 .is_mutable = false, 30584 .ty_without_well_defined_layout = field_ty, 30585 }; 30586 }, 30587 .elem => |elem_ptr| blk: { 30588 const elem_ty = Type.fromInterned(ip.typeOf(elem_ptr.base)).elemType2(mod); 30589 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(elem_ptr.base), null); 30590 30591 // This code assumes that elem_ptrs have been "flattened" in order for direct dereference 30592 // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that 30593 // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" 30594 switch (ip.indexToKey(elem_ptr.base)) { 30595 .ptr => |base_ptr| switch (base_ptr.addr) { 30596 .elem => |base_elem| assert(!Type.fromInterned(ip.typeOf(base_elem.base)).elemType2(mod).eql(elem_ty, mod)), 30597 else => {}, 30598 }, 30599 else => {}, 30600 } 30601 30602 if (elem_ptr.index != 0) { 30603 if (elem_ty.hasWellDefinedLayout(mod)) { 30604 if (deref.parent) |*parent| { 30605 // Update the byte offset (in-place) 30606 const elem_size = try sema.typeAbiSize(elem_ty); 30607 const offset = parent.byte_offset + elem_size * elem_ptr.index; 30608 parent.byte_offset = try sema.usizeCast(block, src, offset); 30609 } 30610 } else { 30611 deref.parent = null; 30612 deref.ty_without_well_defined_layout = elem_ty; 30613 } 30614 } 30615 30616 // If we're loading an elem that was derived from a different type 30617 // than the true type of the underlying decl, we cannot deref directly 30618 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: { 30619 const deref_elem_ty = deref.pointee.?.ty.childType(mod); 30620 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or 30621 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; 30622 } else false; 30623 if (!ty_matches) { 30624 deref.pointee = null; 30625 break :blk deref; 30626 } 30627 30628 var array_tv = deref.pointee.?; 30629 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod); 30630 if (maybe_array_ty) |load_ty| { 30631 // It's possible that we're loading a [N]T, in which case we'd like to slice 30632 // the pointee array directly from our parent array. 30633 if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) { 30634 const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod)); 30635 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 30636 deref.pointee = if (elem_ptr.index + len <= check_len) TypedValue{ 30637 .ty = try mod.arrayType(.{ 30638 .len = len, 30639 .child = elem_ty.toIntern(), 30640 }), 30641 .val = try array_tv.val.sliceArray(mod, sema.arena, elem_idx, elem_idx + len), 30642 } else null; 30643 break :blk deref; 30644 } 30645 } 30646 30647 if (elem_ptr.index >= check_len) { 30648 deref.pointee = null; 30649 break :blk deref; 30650 } 30651 if (elem_ptr.index == check_len - 1) { 30652 if (array_tv.ty.sentinel(mod)) |sent| { 30653 deref.pointee = TypedValue{ 30654 .ty = elem_ty, 30655 .val = sent, 30656 }; 30657 break :blk deref; 30658 } 30659 } 30660 deref.pointee = TypedValue{ 30661 .ty = elem_ty, 30662 .val = try array_tv.val.elemValue(mod, @intCast(elem_ptr.index)), 30663 }; 30664 break :blk deref; 30665 }, 30666 .field => |field_ptr| blk: { 30667 const field_index: u32 = @intCast(field_ptr.index); 30668 const container_ty = Type.fromInterned(ip.typeOf(field_ptr.base)).childType(mod); 30669 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(field_ptr.base), container_ty); 30670 30671 if (container_ty.hasWellDefinedLayout(mod)) { 30672 const struct_obj = mod.typeToStruct(container_ty); 30673 if (struct_obj != null and struct_obj.?.layout == .Packed) { 30674 // packed structs are not byte addressable 30675 deref.parent = null; 30676 } else if (deref.parent) |*parent| { 30677 // Update the byte offset (in-place) 30678 try sema.resolveTypeLayout(container_ty); 30679 const field_offset = container_ty.structFieldOffset(field_index, mod); 30680 parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset); 30681 } 30682 } else { 30683 deref.parent = null; 30684 deref.ty_without_well_defined_layout = container_ty; 30685 } 30686 30687 const tv = deref.pointee orelse { 30688 deref.pointee = null; 30689 break :blk deref; 30690 }; 30691 const coerce_in_mem_ok = 30692 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or 30693 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok; 30694 if (!coerce_in_mem_ok) { 30695 deref.pointee = null; 30696 break :blk deref; 30697 } 30698 30699 if (container_ty.isSlice(mod)) { 30700 deref.pointee = switch (field_index) { 30701 Value.slice_ptr_index => TypedValue{ 30702 .ty = container_ty.slicePtrFieldType(mod), 30703 .val = tv.val.slicePtr(mod), 30704 }, 30705 Value.slice_len_index => TypedValue{ 30706 .ty = Type.usize, 30707 .val = Value.fromInterned(ip.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len), 30708 }, 30709 else => unreachable, 30710 }; 30711 } else { 30712 const field_ty = container_ty.structFieldType(field_index, mod); 30713 deref.pointee = TypedValue{ 30714 .ty = field_ty, 30715 .val = try tv.val.fieldValue(mod, field_index), 30716 }; 30717 } 30718 break :blk deref; 30719 }, 30720 }, 30721 .opt => |opt| switch (opt.val) { 30722 .none => return sema.fail(block, src, "attempt to use null value", .{}), 30723 else => |payload| try sema.beginComptimePtrLoad(block, src, Value.fromInterned(payload), null), 30724 }, 30725 else => unreachable, 30726 }; 30727 30728 if (deref.pointee) |tv| { 30729 if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) { 30730 deref.parent = .{ .tv = tv, .byte_offset = 0 }; 30731 } 30732 } 30733 return deref; 30734 } 30735 30736 fn bitCast( 30737 sema: *Sema, 30738 block: *Block, 30739 dest_ty: Type, 30740 inst: Air.Inst.Ref, 30741 inst_src: LazySrcLoc, 30742 operand_src: ?LazySrcLoc, 30743 ) CompileError!Air.Inst.Ref { 30744 const mod = sema.mod; 30745 try sema.resolveTypeLayout(dest_ty); 30746 30747 const old_ty = sema.typeOf(inst); 30748 try sema.resolveTypeLayout(old_ty); 30749 30750 const dest_bits = dest_ty.bitSize(mod); 30751 const old_bits = old_ty.bitSize(mod); 30752 30753 if (old_bits != dest_bits) { 30754 return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{ 30755 dest_ty.fmt(mod), 30756 dest_bits, 30757 old_ty.fmt(mod), 30758 old_bits, 30759 }); 30760 } 30761 30762 if (try sema.resolveValue(inst)) |val| { 30763 if (val.isUndef(mod)) 30764 return mod.undefRef(dest_ty); 30765 if (try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0)) |result_val| { 30766 return Air.internedToRef(result_val.toIntern()); 30767 } 30768 } 30769 try sema.requireRuntimeBlock(block, inst_src, operand_src); 30770 return block.addBitCast(dest_ty, inst); 30771 } 30772 30773 fn bitCastVal( 30774 sema: *Sema, 30775 block: *Block, 30776 src: LazySrcLoc, 30777 val: Value, 30778 old_ty: Type, 30779 new_ty: Type, 30780 buffer_offset: usize, 30781 ) !?Value { 30782 const mod = sema.mod; 30783 if (old_ty.eql(new_ty, mod)) return val; 30784 30785 // For types with well-defined memory layouts, we serialize them a byte buffer, 30786 // then deserialize to the new type. 30787 const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(mod)); 30788 30789 const buffer = try sema.gpa.alloc(u8, abi_size); 30790 defer sema.gpa.free(buffer); 30791 val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) { 30792 error.OutOfMemory => return error.OutOfMemory, 30793 error.ReinterpretDeclRef => return null, 30794 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 30795 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}), 30796 }; 30797 30798 return Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena) catch |err| switch (err) { 30799 error.OutOfMemory => return error.OutOfMemory, 30800 error.IllDefinedMemoryLayout => unreachable, 30801 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{new_ty.fmt(mod)}), 30802 }; 30803 } 30804 30805 fn bitCastUnionFieldVal( 30806 sema: *Sema, 30807 block: *Block, 30808 src: LazySrcLoc, 30809 val: Value, 30810 old_ty: Type, 30811 field_ty: Type, 30812 layout: std.builtin.Type.ContainerLayout, 30813 ) !?Value { 30814 const mod = sema.mod; 30815 if (old_ty.eql(field_ty, mod)) return val; 30816 30817 const old_size = try sema.usizeCast(block, src, old_ty.abiSize(mod)); 30818 const field_size = try sema.usizeCast(block, src, field_ty.abiSize(mod)); 30819 const endian = mod.getTarget().cpu.arch.endian(); 30820 30821 const buffer = try sema.gpa.alloc(u8, @max(old_size, field_size)); 30822 defer sema.gpa.free(buffer); 30823 30824 // Reading a larger value means we need to reinterpret from undefined bytes. 30825 const offset = switch (layout) { 30826 .Extern => offset: { 30827 if (field_size > old_size) @memset(buffer[old_size..], 0xaa); 30828 val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) { 30829 error.OutOfMemory => return error.OutOfMemory, 30830 error.ReinterpretDeclRef => return null, 30831 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 30832 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}), 30833 }; 30834 break :offset 0; 30835 }, 30836 .Packed => offset: { 30837 if (field_size > old_size) { 30838 const min_size = @max(old_size, 1); 30839 switch (endian) { 30840 .little => @memset(buffer[min_size - 1 ..], 0xaa), 30841 .big => @memset(buffer[0 .. buffer.len - min_size + 1], 0xaa), 30842 } 30843 } 30844 30845 val.writeToPackedMemory(old_ty, mod, buffer, 0) catch |err| switch (err) { 30846 error.OutOfMemory => return error.OutOfMemory, 30847 error.ReinterpretDeclRef => return null, 30848 }; 30849 30850 break :offset if (endian == .big) buffer.len - field_size else 0; 30851 }, 30852 .Auto => unreachable, 30853 }; 30854 30855 return Value.readFromMemory(field_ty, mod, buffer[offset..], sema.arena) catch |err| switch (err) { 30856 error.OutOfMemory => return error.OutOfMemory, 30857 error.IllDefinedMemoryLayout => unreachable, 30858 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{field_ty.fmt(mod)}), 30859 }; 30860 } 30861 30862 fn coerceArrayPtrToSlice( 30863 sema: *Sema, 30864 block: *Block, 30865 dest_ty: Type, 30866 inst: Air.Inst.Ref, 30867 inst_src: LazySrcLoc, 30868 ) CompileError!Air.Inst.Ref { 30869 const mod = sema.mod; 30870 if (try sema.resolveValue(inst)) |val| { 30871 const ptr_array_ty = sema.typeOf(inst); 30872 const array_ty = ptr_array_ty.childType(mod); 30873 const slice_val = try mod.intern(.{ .ptr = .{ 30874 .ty = dest_ty.toIntern(), 30875 .addr = switch (mod.intern_pool.indexToKey(val.toIntern())) { 30876 .undef => .{ .int = try mod.intern(.{ .undef = .usize_type }) }, 30877 .ptr => |ptr| ptr.addr, 30878 else => unreachable, 30879 }, 30880 .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).toIntern(), 30881 } }); 30882 return Air.internedToRef(slice_val); 30883 } 30884 try sema.requireRuntimeBlock(block, inst_src, null); 30885 return block.addTyOp(.array_to_slice, dest_ty, inst); 30886 } 30887 30888 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool { 30889 const mod = sema.mod; 30890 const dest_info = dest_ty.ptrInfo(mod); 30891 const inst_info = inst_ty.ptrInfo(mod); 30892 const len0 = (Type.fromInterned(inst_info.child).zigTypeTag(mod) == .Array and (Type.fromInterned(inst_info.child).arrayLenIncludingSentinel(mod) == 0 or 30893 (Type.fromInterned(inst_info.child).arrayLen(mod) == 0 and dest_info.sentinel == .none and dest_info.flags.size != .C and dest_info.flags.size != .Many))) or 30894 (Type.fromInterned(inst_info.child).isTuple(mod) and Type.fromInterned(inst_info.child).structFieldCount(mod) == 0); 30895 30896 const ok_cv_qualifiers = 30897 ((!inst_info.flags.is_const or dest_info.flags.is_const) or len0) and 30898 (!inst_info.flags.is_volatile or dest_info.flags.is_volatile); 30899 30900 if (!ok_cv_qualifiers) { 30901 in_memory_result.* = .{ .ptr_qualifiers = .{ 30902 .actual_const = inst_info.flags.is_const, 30903 .wanted_const = dest_info.flags.is_const, 30904 .actual_volatile = inst_info.flags.is_volatile, 30905 .wanted_volatile = dest_info.flags.is_volatile, 30906 } }; 30907 return false; 30908 } 30909 if (dest_info.flags.address_space != inst_info.flags.address_space) { 30910 in_memory_result.* = .{ .ptr_addrspace = .{ 30911 .actual = inst_info.flags.address_space, 30912 .wanted = dest_info.flags.address_space, 30913 } }; 30914 return false; 30915 } 30916 if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true; 30917 if (len0) return true; 30918 30919 const inst_align = if (inst_info.flags.alignment != .none) 30920 inst_info.flags.alignment 30921 else 30922 Type.fromInterned(inst_info.child).abiAlignment(mod); 30923 30924 const dest_align = if (dest_info.flags.alignment != .none) 30925 dest_info.flags.alignment 30926 else 30927 Type.fromInterned(dest_info.child).abiAlignment(mod); 30928 30929 if (dest_align.compare(.gt, inst_align)) { 30930 in_memory_result.* = .{ .ptr_alignment = .{ 30931 .actual = inst_align, 30932 .wanted = dest_align, 30933 } }; 30934 return false; 30935 } 30936 return true; 30937 } 30938 30939 fn coerceCompatiblePtrs( 30940 sema: *Sema, 30941 block: *Block, 30942 dest_ty: Type, 30943 inst: Air.Inst.Ref, 30944 inst_src: LazySrcLoc, 30945 ) !Air.Inst.Ref { 30946 const mod = sema.mod; 30947 const inst_ty = sema.typeOf(inst); 30948 if (try sema.resolveValue(inst)) |val| { 30949 if (!val.isUndef(mod) and val.isNull(mod) and !dest_ty.isAllowzeroPtr(mod)) { 30950 return sema.fail(block, inst_src, "null pointer casted to type '{}'", .{dest_ty.fmt(sema.mod)}); 30951 } 30952 // The comptime Value representation is compatible with both types. 30953 return Air.internedToRef( 30954 (try mod.getCoerced(Value.fromInterned((try val.intern(inst_ty, mod))), dest_ty)).toIntern(), 30955 ); 30956 } 30957 try sema.requireRuntimeBlock(block, inst_src, null); 30958 const inst_allows_zero = inst_ty.zigTypeTag(mod) != .Pointer or inst_ty.ptrAllowsZero(mod); 30959 if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(mod) and 30960 (try sema.typeHasRuntimeBits(dest_ty.elemType2(mod)) or dest_ty.elemType2(mod).zigTypeTag(mod) == .Fn)) 30961 { 30962 const actual_ptr = if (inst_ty.isSlice(mod)) 30963 try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) 30964 else 30965 inst; 30966 const ptr_int = try block.addUnOp(.int_from_ptr, actual_ptr); 30967 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); 30968 const ok = if (inst_ty.isSlice(mod)) ok: { 30969 const len = try sema.analyzeSliceLen(block, inst_src, inst); 30970 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 30971 break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero); 30972 } else is_non_zero; 30973 try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null); 30974 } 30975 const new_ptr = try sema.bitCast(block, dest_ty, inst, inst_src, null); 30976 try sema.checkKnownAllocPtr(inst, new_ptr); 30977 return new_ptr; 30978 } 30979 30980 fn coerceEnumToUnion( 30981 sema: *Sema, 30982 block: *Block, 30983 union_ty: Type, 30984 union_ty_src: LazySrcLoc, 30985 inst: Air.Inst.Ref, 30986 inst_src: LazySrcLoc, 30987 ) !Air.Inst.Ref { 30988 const mod = sema.mod; 30989 const ip = &mod.intern_pool; 30990 const inst_ty = sema.typeOf(inst); 30991 30992 const tag_ty = union_ty.unionTagType(mod) orelse { 30993 const msg = msg: { 30994 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 30995 union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), 30996 }); 30997 errdefer msg.destroy(sema.gpa); 30998 try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{}); 30999 try sema.addDeclaredHereNote(msg, union_ty); 31000 break :msg msg; 31001 }; 31002 return sema.failWithOwnedErrorMsg(block, msg); 31003 }; 31004 31005 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src); 31006 if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { 31007 const field_index = union_ty.unionTagFieldIndex(val, sema.mod) orelse { 31008 const msg = msg: { 31009 const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{ 31010 union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod), 31011 }); 31012 errdefer msg.destroy(sema.gpa); 31013 try sema.addDeclaredHereNote(msg, union_ty); 31014 break :msg msg; 31015 }; 31016 return sema.failWithOwnedErrorMsg(block, msg); 31017 }; 31018 31019 const union_obj = mod.typeToUnion(union_ty).?; 31020 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 31021 try sema.resolveTypeFields(field_ty); 31022 if (field_ty.zigTypeTag(mod) == .NoReturn) { 31023 const msg = msg: { 31024 const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{}); 31025 errdefer msg.destroy(sema.gpa); 31026 31027 const field_name = union_obj.field_names.get(ip)[field_index]; 31028 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 31029 field_name.fmt(ip), 31030 }); 31031 try sema.addDeclaredHereNote(msg, union_ty); 31032 break :msg msg; 31033 }; 31034 return sema.failWithOwnedErrorMsg(block, msg); 31035 } 31036 const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { 31037 const msg = msg: { 31038 const field_name = union_obj.field_names.get(ip)[field_index]; 31039 const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{ 31040 inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), 31041 field_ty.fmt(sema.mod), field_name.fmt(ip), 31042 }); 31043 errdefer msg.destroy(sema.gpa); 31044 31045 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 31046 field_name.fmt(ip), 31047 }); 31048 try sema.addDeclaredHereNote(msg, union_ty); 31049 break :msg msg; 31050 }; 31051 return sema.failWithOwnedErrorMsg(block, msg); 31052 }; 31053 31054 return Air.internedToRef((try mod.unionValue(union_ty, val, opv)).toIntern()); 31055 } 31056 31057 try sema.requireRuntimeBlock(block, inst_src, null); 31058 31059 if (tag_ty.isNonexhaustiveEnum(mod)) { 31060 const msg = msg: { 31061 const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{ 31062 union_ty.fmt(sema.mod), 31063 }); 31064 errdefer msg.destroy(sema.gpa); 31065 try sema.addDeclaredHereNote(msg, tag_ty); 31066 break :msg msg; 31067 }; 31068 return sema.failWithOwnedErrorMsg(block, msg); 31069 } 31070 31071 const union_obj = mod.typeToUnion(union_ty).?; 31072 { 31073 var msg: ?*Module.ErrorMsg = null; 31074 errdefer if (msg) |some| some.destroy(sema.gpa); 31075 31076 for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { 31077 if (Type.fromInterned(field_ty).zigTypeTag(mod) == .NoReturn) { 31078 const err_msg = msg orelse try sema.errMsg( 31079 block, 31080 inst_src, 31081 "runtime coercion from enum '{}' to union '{}' which has a 'noreturn' field", 31082 .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) }, 31083 ); 31084 msg = err_msg; 31085 31086 try sema.addFieldErrNote(union_ty, field_index, err_msg, "'noreturn' field here", .{}); 31087 } 31088 } 31089 if (msg) |some| { 31090 msg = null; 31091 try sema.addDeclaredHereNote(some, union_ty); 31092 return sema.failWithOwnedErrorMsg(block, some); 31093 } 31094 } 31095 31096 // If the union has all fields 0 bits, the union value is just the enum value. 31097 if (union_ty.unionHasAllZeroBitFieldTypes(mod)) { 31098 return block.addBitCast(union_ty, enum_tag); 31099 } 31100 31101 const msg = msg: { 31102 const msg = try sema.errMsg( 31103 block, 31104 inst_src, 31105 "runtime coercion from enum '{}' to union '{}' which has non-void fields", 31106 .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) }, 31107 ); 31108 errdefer msg.destroy(sema.gpa); 31109 31110 for (0..union_obj.field_names.len) |field_index| { 31111 const field_name = union_obj.field_names.get(ip)[field_index]; 31112 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 31113 if (!(try sema.typeHasRuntimeBits(field_ty))) continue; 31114 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{ 31115 field_name.fmt(ip), 31116 field_ty.fmt(sema.mod), 31117 }); 31118 } 31119 try sema.addDeclaredHereNote(msg, union_ty); 31120 break :msg msg; 31121 }; 31122 return sema.failWithOwnedErrorMsg(block, msg); 31123 } 31124 31125 fn coerceAnonStructToUnion( 31126 sema: *Sema, 31127 block: *Block, 31128 union_ty: Type, 31129 union_ty_src: LazySrcLoc, 31130 inst: Air.Inst.Ref, 31131 inst_src: LazySrcLoc, 31132 ) !Air.Inst.Ref { 31133 const mod = sema.mod; 31134 const ip = &mod.intern_pool; 31135 const inst_ty = sema.typeOf(inst); 31136 const field_info: union(enum) { 31137 name: InternPool.NullTerminatedString, 31138 count: usize, 31139 } = switch (ip.indexToKey(inst_ty.toIntern())) { 31140 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1) 31141 .{ .name = anon_struct_type.names.get(ip)[0] } 31142 else 31143 .{ .count = anon_struct_type.names.len }, 31144 .struct_type => |struct_type| name: { 31145 const field_names = struct_type.field_names.get(ip); 31146 break :name if (field_names.len == 1) 31147 .{ .name = field_names[0] } 31148 else 31149 .{ .count = field_names.len }; 31150 }, 31151 else => unreachable, 31152 }; 31153 switch (field_info) { 31154 .name => |field_name| { 31155 const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty); 31156 return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src); 31157 }, 31158 .count => |field_count| { 31159 assert(field_count != 1); 31160 const msg = msg: { 31161 const msg = if (field_count > 1) try sema.errMsg( 31162 block, 31163 inst_src, 31164 "cannot initialize multiple union fields at once; unions can only have one active field", 31165 .{}, 31166 ) else try sema.errMsg( 31167 block, 31168 inst_src, 31169 "union initializer must initialize one field", 31170 .{}, 31171 ); 31172 errdefer msg.destroy(sema.gpa); 31173 31174 // TODO add notes for where the anon struct was created to point out 31175 // the extra fields. 31176 31177 try sema.addDeclaredHereNote(msg, union_ty); 31178 break :msg msg; 31179 }; 31180 return sema.failWithOwnedErrorMsg(block, msg); 31181 }, 31182 } 31183 } 31184 31185 fn coerceAnonStructToUnionPtrs( 31186 sema: *Sema, 31187 block: *Block, 31188 ptr_union_ty: Type, 31189 union_ty_src: LazySrcLoc, 31190 ptr_anon_struct: Air.Inst.Ref, 31191 anon_struct_src: LazySrcLoc, 31192 ) !Air.Inst.Ref { 31193 const mod = sema.mod; 31194 const union_ty = ptr_union_ty.childType(mod); 31195 const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); 31196 const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src); 31197 return sema.analyzeRef(block, union_ty_src, union_inst); 31198 } 31199 31200 fn coerceAnonStructToStructPtrs( 31201 sema: *Sema, 31202 block: *Block, 31203 ptr_struct_ty: Type, 31204 struct_ty_src: LazySrcLoc, 31205 ptr_anon_struct: Air.Inst.Ref, 31206 anon_struct_src: LazySrcLoc, 31207 ) !Air.Inst.Ref { 31208 const mod = sema.mod; 31209 const struct_ty = ptr_struct_ty.childType(mod); 31210 const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); 31211 const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, anon_struct, anon_struct_src); 31212 return sema.analyzeRef(block, struct_ty_src, struct_inst); 31213 } 31214 31215 /// If the lengths match, coerces element-wise. 31216 fn coerceArrayLike( 31217 sema: *Sema, 31218 block: *Block, 31219 dest_ty: Type, 31220 dest_ty_src: LazySrcLoc, 31221 inst: Air.Inst.Ref, 31222 inst_src: LazySrcLoc, 31223 ) !Air.Inst.Ref { 31224 const mod = sema.mod; 31225 const inst_ty = sema.typeOf(inst); 31226 const target = mod.getTarget(); 31227 31228 // try coercion of the whole array 31229 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); 31230 if (in_memory_result == .ok) { 31231 if (try sema.resolveValue(inst)) |inst_val| { 31232 // These types share the same comptime value representation. 31233 return sema.coerceInMemory(inst_val, dest_ty); 31234 } 31235 try sema.requireRuntimeBlock(block, inst_src, null); 31236 return block.addBitCast(dest_ty, inst); 31237 } 31238 31239 // otherwise, try element by element 31240 const inst_len = inst_ty.arrayLen(mod); 31241 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod)); 31242 if (dest_len != inst_len) { 31243 const msg = msg: { 31244 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 31245 dest_ty.fmt(mod), inst_ty.fmt(mod), 31246 }); 31247 errdefer msg.destroy(sema.gpa); 31248 try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); 31249 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len}); 31250 break :msg msg; 31251 }; 31252 return sema.failWithOwnedErrorMsg(block, msg); 31253 } 31254 31255 const dest_elem_ty = dest_ty.childType(mod); 31256 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); 31257 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); 31258 var runtime_src: ?LazySrcLoc = null; 31259 31260 for (element_vals, element_refs, 0..) |*val, *ref, i| { 31261 const index_ref = Air.internedToRef((try mod.intValue(Type.usize, i)).toIntern()); 31262 const src = inst_src; // TODO better source location 31263 const elem_src = inst_src; // TODO better source location 31264 const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true); 31265 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 31266 ref.* = coerced; 31267 if (runtime_src == null) { 31268 if (try sema.resolveValue(coerced)) |elem_val| { 31269 val.* = try elem_val.intern(dest_elem_ty, mod); 31270 } else { 31271 runtime_src = elem_src; 31272 } 31273 } 31274 } 31275 31276 if (runtime_src) |rs| { 31277 try sema.requireRuntimeBlock(block, inst_src, rs); 31278 return block.addAggregateInit(dest_ty, element_refs); 31279 } 31280 31281 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 31282 .ty = dest_ty.toIntern(), 31283 .storage = .{ .elems = element_vals }, 31284 } }))); 31285 } 31286 31287 /// If the lengths match, coerces element-wise. 31288 fn coerceTupleToArray( 31289 sema: *Sema, 31290 block: *Block, 31291 dest_ty: Type, 31292 dest_ty_src: LazySrcLoc, 31293 inst: Air.Inst.Ref, 31294 inst_src: LazySrcLoc, 31295 ) !Air.Inst.Ref { 31296 const mod = sema.mod; 31297 const inst_ty = sema.typeOf(inst); 31298 const inst_len = inst_ty.arrayLen(mod); 31299 const dest_len = dest_ty.arrayLen(mod); 31300 31301 if (dest_len != inst_len) { 31302 const msg = msg: { 31303 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 31304 dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), 31305 }); 31306 errdefer msg.destroy(sema.gpa); 31307 try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); 31308 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len}); 31309 break :msg msg; 31310 }; 31311 return sema.failWithOwnedErrorMsg(block, msg); 31312 } 31313 31314 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len); 31315 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems); 31316 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); 31317 const dest_elem_ty = dest_ty.childType(mod); 31318 31319 var runtime_src: ?LazySrcLoc = null; 31320 for (element_vals, element_refs, 0..) |*val, *ref, i_usize| { 31321 const i: u32 = @intCast(i_usize); 31322 if (i_usize == inst_len) { 31323 const sentinel_val = dest_ty.sentinel(mod).?; 31324 val.* = sentinel_val.toIntern(); 31325 ref.* = Air.internedToRef(sentinel_val.toIntern()); 31326 break; 31327 } 31328 const elem_src = inst_src; // TODO better source location 31329 const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i); 31330 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 31331 ref.* = coerced; 31332 if (runtime_src == null) { 31333 if (try sema.resolveValue(coerced)) |elem_val| { 31334 val.* = try elem_val.intern(dest_elem_ty, mod); 31335 } else { 31336 runtime_src = elem_src; 31337 } 31338 } 31339 } 31340 31341 if (runtime_src) |rs| { 31342 try sema.requireRuntimeBlock(block, inst_src, rs); 31343 return block.addAggregateInit(dest_ty, element_refs); 31344 } 31345 31346 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 31347 .ty = dest_ty.toIntern(), 31348 .storage = .{ .elems = element_vals }, 31349 } }))); 31350 } 31351 31352 /// If the lengths match, coerces element-wise. 31353 fn coerceTupleToSlicePtrs( 31354 sema: *Sema, 31355 block: *Block, 31356 slice_ty: Type, 31357 slice_ty_src: LazySrcLoc, 31358 ptr_tuple: Air.Inst.Ref, 31359 tuple_src: LazySrcLoc, 31360 ) !Air.Inst.Ref { 31361 const mod = sema.mod; 31362 const tuple_ty = sema.typeOf(ptr_tuple).childType(mod); 31363 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 31364 const slice_info = slice_ty.ptrInfo(mod); 31365 const array_ty = try mod.arrayType(.{ 31366 .len = tuple_ty.structFieldCount(mod), 31367 .sentinel = slice_info.sentinel, 31368 .child = slice_info.child, 31369 }); 31370 const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); 31371 if (slice_info.flags.alignment != .none) { 31372 return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 31373 } 31374 const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst); 31375 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src); 31376 } 31377 31378 /// If the lengths match, coerces element-wise. 31379 fn coerceTupleToArrayPtrs( 31380 sema: *Sema, 31381 block: *Block, 31382 ptr_array_ty: Type, 31383 array_ty_src: LazySrcLoc, 31384 ptr_tuple: Air.Inst.Ref, 31385 tuple_src: LazySrcLoc, 31386 ) !Air.Inst.Ref { 31387 const mod = sema.mod; 31388 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 31389 const ptr_info = ptr_array_ty.ptrInfo(mod); 31390 const array_ty = Type.fromInterned(ptr_info.child); 31391 const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src); 31392 if (ptr_info.flags.alignment != .none) { 31393 return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 31394 } 31395 const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst); 31396 return ptr_array; 31397 } 31398 31399 /// Handles both tuples and anon struct literals. Coerces field-wise. Reports 31400 /// errors for both extra fields and missing fields. 31401 fn coerceTupleToStruct( 31402 sema: *Sema, 31403 block: *Block, 31404 struct_ty: Type, 31405 inst: Air.Inst.Ref, 31406 inst_src: LazySrcLoc, 31407 ) !Air.Inst.Ref { 31408 const mod = sema.mod; 31409 const ip = &mod.intern_pool; 31410 try sema.resolveTypeFields(struct_ty); 31411 try sema.resolveStructFieldInits(struct_ty); 31412 31413 if (struct_ty.isTupleOrAnonStruct(mod)) { 31414 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src); 31415 } 31416 31417 const struct_type = mod.typeToStruct(struct_ty).?; 31418 const field_vals = try sema.arena.alloc(InternPool.Index, struct_type.field_types.len); 31419 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); 31420 @memset(field_refs, .none); 31421 31422 const inst_ty = sema.typeOf(inst); 31423 var runtime_src: ?LazySrcLoc = null; 31424 const field_count = switch (ip.indexToKey(inst_ty.toIntern())) { 31425 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 31426 .struct_type => |s| s.field_types.len, 31427 else => unreachable, 31428 }; 31429 for (0..field_count) |field_index_usize| { 31430 const field_i: u32 = @intCast(field_index_usize); 31431 const field_src = inst_src; // TODO better source location 31432 // https://github.com/ziglang/zig/issues/15709 31433 const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { 31434 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) 31435 anon_struct_type.names.get(ip)[field_i] 31436 else 31437 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), 31438 .struct_type => |s| s.field_names.get(ip)[field_i], 31439 else => unreachable, 31440 }; 31441 const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); 31442 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); 31443 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); 31444 const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); 31445 field_refs[field_index] = coerced; 31446 if (struct_type.fieldIsComptime(ip, field_index)) { 31447 const init_val = (try sema.resolveValue(coerced)) orelse { 31448 return sema.failWithNeededComptime(block, field_src, .{ 31449 .needed_comptime_reason = "value stored in comptime field must be comptime-known", 31450 }); 31451 }; 31452 31453 const field_init = Value.fromInterned(struct_type.field_inits.get(ip)[field_index]); 31454 if (!init_val.eql(field_init, field_ty, sema.mod)) { 31455 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); 31456 } 31457 } 31458 if (runtime_src == null) { 31459 if (try sema.resolveValue(coerced)) |field_val| { 31460 field_vals[field_index] = field_val.toIntern(); 31461 } else { 31462 runtime_src = field_src; 31463 } 31464 } 31465 } 31466 31467 // Populate default field values and report errors for missing fields. 31468 var root_msg: ?*Module.ErrorMsg = null; 31469 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 31470 31471 for (field_refs, 0..) |*field_ref, i| { 31472 if (field_ref.* != .none) continue; 31473 31474 const field_name = struct_type.field_names.get(ip)[i]; 31475 const field_default_val = struct_type.fieldInit(ip, i); 31476 const field_src = inst_src; // TODO better source location 31477 if (field_default_val == .none) { 31478 const template = "missing struct field: {}"; 31479 const args = .{field_name.fmt(ip)}; 31480 if (root_msg) |msg| { 31481 try sema.errNote(block, field_src, msg, template, args); 31482 } else { 31483 root_msg = try sema.errMsg(block, field_src, template, args); 31484 } 31485 continue; 31486 } 31487 if (runtime_src == null) { 31488 field_vals[i] = field_default_val; 31489 } else { 31490 field_ref.* = Air.internedToRef(field_default_val); 31491 } 31492 } 31493 31494 if (root_msg) |msg| { 31495 try sema.addDeclaredHereNote(msg, struct_ty); 31496 root_msg = null; 31497 return sema.failWithOwnedErrorMsg(block, msg); 31498 } 31499 31500 if (runtime_src) |rs| { 31501 try sema.requireRuntimeBlock(block, inst_src, rs); 31502 return block.addAggregateInit(struct_ty, field_refs); 31503 } 31504 31505 const struct_val = try mod.intern(.{ .aggregate = .{ 31506 .ty = struct_ty.toIntern(), 31507 .storage = .{ .elems = field_vals }, 31508 } }); 31509 // TODO: figure out InternPool removals for incremental compilation 31510 //errdefer ip.remove(struct_val); 31511 31512 return Air.internedToRef(struct_val); 31513 } 31514 31515 fn coerceTupleToTuple( 31516 sema: *Sema, 31517 block: *Block, 31518 tuple_ty: Type, 31519 inst: Air.Inst.Ref, 31520 inst_src: LazySrcLoc, 31521 ) !Air.Inst.Ref { 31522 const mod = sema.mod; 31523 const ip = &mod.intern_pool; 31524 const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) { 31525 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 31526 .struct_type => |struct_type| struct_type.field_types.len, 31527 else => unreachable, 31528 }; 31529 const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); 31530 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); 31531 @memset(field_refs, .none); 31532 31533 const inst_ty = sema.typeOf(inst); 31534 const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) { 31535 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 31536 .struct_type => |struct_type| struct_type.field_types.len, 31537 else => unreachable, 31538 }; 31539 if (src_field_count > dest_field_count) return error.NotCoercible; 31540 31541 var runtime_src: ?LazySrcLoc = null; 31542 for (0..dest_field_count) |field_index_usize| { 31543 const field_i: u32 = @intCast(field_index_usize); 31544 const field_src = inst_src; // TODO better source location 31545 // https://github.com/ziglang/zig/issues/15709 31546 const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { 31547 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) 31548 anon_struct_type.names.get(ip)[field_i] 31549 else 31550 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), 31551 .struct_type => |struct_type| if (struct_type.field_names.len > 0) 31552 struct_type.field_names.get(ip)[field_i] 31553 else 31554 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), 31555 else => unreachable, 31556 }; 31557 31558 if (ip.stringEqlSlice(field_name, "len")) 31559 return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{}); 31560 31561 const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { 31562 .anon_struct_type => |anon_struct_type| anon_struct_type.types.get(ip)[field_index_usize], 31563 .struct_type => |struct_type| struct_type.field_types.get(ip)[field_index_usize], 31564 else => unreachable, 31565 }; 31566 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 31567 .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[field_index_usize], 31568 .struct_type => |struct_type| struct_type.fieldInit(ip, field_index_usize), 31569 else => unreachable, 31570 }; 31571 31572 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src); 31573 31574 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); 31575 const coerced = try sema.coerce(block, Type.fromInterned(field_ty), elem_ref, field_src); 31576 field_refs[field_index] = coerced; 31577 if (default_val != .none) { 31578 const init_val = (try sema.resolveValue(coerced)) orelse { 31579 return sema.failWithNeededComptime(block, field_src, .{ 31580 .needed_comptime_reason = "value stored in comptime field must be comptime-known", 31581 }); 31582 }; 31583 31584 if (!init_val.eql(Value.fromInterned(default_val), Type.fromInterned(field_ty), sema.mod)) { 31585 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); 31586 } 31587 } 31588 if (runtime_src == null) { 31589 if (try sema.resolveValue(coerced)) |field_val| { 31590 field_vals[field_index] = field_val.toIntern(); 31591 } else { 31592 runtime_src = field_src; 31593 } 31594 } 31595 } 31596 31597 // Populate default field values and report errors for missing fields. 31598 var root_msg: ?*Module.ErrorMsg = null; 31599 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 31600 31601 for (field_refs, 0..) |*field_ref, i_usize| { 31602 const i: u32 = @intCast(i_usize); 31603 if (field_ref.* != .none) continue; 31604 31605 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 31606 .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[i], 31607 .struct_type => |struct_type| struct_type.fieldInit(ip, i), 31608 else => unreachable, 31609 }; 31610 31611 const field_src = inst_src; // TODO better source location 31612 if (default_val == .none) { 31613 const field_name = tuple_ty.structFieldName(i, mod).unwrap() orelse { 31614 const template = "missing tuple field: {d}"; 31615 if (root_msg) |msg| { 31616 try sema.errNote(block, field_src, msg, template, .{i}); 31617 } else { 31618 root_msg = try sema.errMsg(block, field_src, template, .{i}); 31619 } 31620 continue; 31621 }; 31622 const template = "missing struct field: {}"; 31623 const args = .{field_name.fmt(ip)}; 31624 if (root_msg) |msg| { 31625 try sema.errNote(block, field_src, msg, template, args); 31626 } else { 31627 root_msg = try sema.errMsg(block, field_src, template, args); 31628 } 31629 continue; 31630 } 31631 if (runtime_src == null) { 31632 field_vals[i] = default_val; 31633 } else { 31634 field_ref.* = Air.internedToRef(default_val); 31635 } 31636 } 31637 31638 if (root_msg) |msg| { 31639 try sema.addDeclaredHereNote(msg, tuple_ty); 31640 root_msg = null; 31641 return sema.failWithOwnedErrorMsg(block, msg); 31642 } 31643 31644 if (runtime_src) |rs| { 31645 try sema.requireRuntimeBlock(block, inst_src, rs); 31646 return block.addAggregateInit(tuple_ty, field_refs); 31647 } 31648 31649 return Air.internedToRef((try mod.intern(.{ .aggregate = .{ 31650 .ty = tuple_ty.toIntern(), 31651 .storage = .{ .elems = field_vals }, 31652 } }))); 31653 } 31654 31655 fn analyzeDeclVal( 31656 sema: *Sema, 31657 block: *Block, 31658 src: LazySrcLoc, 31659 decl_index: InternPool.DeclIndex, 31660 ) CompileError!Air.Inst.Ref { 31661 try sema.addReferencedBy(block, src, decl_index); 31662 if (sema.decl_val_table.get(decl_index)) |result| { 31663 return result; 31664 } 31665 const decl_ref = try sema.analyzeDeclRefInner(decl_index, false); 31666 const result = try sema.analyzeLoad(block, src, decl_ref, src); 31667 if (result.toInterned() != null) { 31668 if (!block.is_typeof) { 31669 try sema.decl_val_table.put(sema.gpa, decl_index, result); 31670 } 31671 } 31672 return result; 31673 } 31674 31675 fn addReferencedBy( 31676 sema: *Sema, 31677 block: *Block, 31678 src: LazySrcLoc, 31679 decl_index: InternPool.DeclIndex, 31680 ) !void { 31681 if (sema.mod.comp.reference_trace == 0) return; 31682 if (src == .unneeded) { 31683 // We can't use NeededSourceLocation, since sites handling that assume it means a compile 31684 // error. Our long-term strategy here is to gradually transition from NeededSourceLocation 31685 // into having more LazySrcLoc tags. In the meantime, let release compilers just ignore this 31686 // reference (a slightly-incomplete error is better than a crash!), but trigger a panic in 31687 // debug so we can fix this case. 31688 if (std.debug.runtime_safety) unreachable else return; 31689 } 31690 try sema.mod.reference_table.put(sema.gpa, decl_index, .{ 31691 .referencer = block.src_decl, 31692 .src = src, 31693 }); 31694 } 31695 31696 fn ensureDeclAnalyzed(sema: *Sema, decl_index: InternPool.DeclIndex) CompileError!void { 31697 const mod = sema.mod; 31698 const ip = &mod.intern_pool; 31699 const decl = mod.declPtr(decl_index); 31700 if (decl.analysis == .in_progress) { 31701 const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(mod), "dependency loop detected", .{}); 31702 return sema.failWithOwnedErrorMsg(null, msg); 31703 } 31704 31705 mod.ensureDeclAnalyzed(decl_index) catch |err| { 31706 if (sema.owner_func_index != .none) { 31707 ip.funcAnalysis(sema.owner_func_index).state = .dependency_failure; 31708 } else { 31709 sema.owner_decl.analysis = .dependency_failure; 31710 } 31711 return err; 31712 }; 31713 } 31714 31715 fn ensureFuncBodyAnalyzed(sema: *Sema, func: InternPool.Index) CompileError!void { 31716 const mod = sema.mod; 31717 const ip = &mod.intern_pool; 31718 mod.ensureFuncBodyAnalyzed(func) catch |err| { 31719 if (sema.owner_func_index != .none) { 31720 ip.funcAnalysis(sema.owner_func_index).state = .dependency_failure; 31721 } else { 31722 sema.owner_decl.analysis = .dependency_failure; 31723 } 31724 return err; 31725 }; 31726 } 31727 31728 fn optRefValue(sema: *Sema, opt_val: ?Value) !Value { 31729 const mod = sema.mod; 31730 const ptr_anyopaque_ty = try mod.singleConstPtrType(Type.anyopaque); 31731 return Value.fromInterned((try mod.intern(.{ .opt = .{ 31732 .ty = (try mod.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(), 31733 .val = if (opt_val) |val| (try mod.getCoerced( 31734 Value.fromInterned((try sema.refValue(val.toIntern()))), 31735 ptr_anyopaque_ty, 31736 )).toIntern() else .none, 31737 } }))); 31738 } 31739 31740 fn analyzeDeclRef(sema: *Sema, decl_index: InternPool.DeclIndex) CompileError!Air.Inst.Ref { 31741 return sema.analyzeDeclRefInner(decl_index, true); 31742 } 31743 31744 /// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but 31745 /// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a 31746 /// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps 31747 /// this function with `analyze_fn_body` set to true. 31748 fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn_body: bool) CompileError!Air.Inst.Ref { 31749 const mod = sema.mod; 31750 try sema.ensureDeclAnalyzed(decl_index); 31751 31752 const decl = mod.declPtr(decl_index); 31753 const decl_tv = try decl.typedValue(); 31754 const ptr_ty = try sema.ptrType(.{ 31755 .child = decl_tv.ty.toIntern(), 31756 .flags = .{ 31757 .alignment = decl.alignment, 31758 .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true, 31759 .address_space = decl.@"addrspace", 31760 }, 31761 }); 31762 if (analyze_fn_body) { 31763 try sema.maybeQueueFuncBodyAnalysis(decl_index); 31764 } 31765 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 31766 .ty = ptr_ty.toIntern(), 31767 .addr = .{ .decl = decl_index }, 31768 } }))); 31769 } 31770 31771 fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: InternPool.DeclIndex) !void { 31772 const mod = sema.mod; 31773 const decl = mod.declPtr(decl_index); 31774 const tv = try decl.typedValue(); 31775 if (tv.ty.zigTypeTag(mod) != .Fn) return; 31776 if (!try sema.fnHasRuntimeBits(tv.ty)) return; 31777 const func_index = tv.val.toIntern(); 31778 if (!mod.intern_pool.isFuncBody(func_index)) return; // undef or extern function 31779 try mod.ensureFuncBodyAnalysisQueued(func_index); 31780 } 31781 31782 fn analyzeRef( 31783 sema: *Sema, 31784 block: *Block, 31785 src: LazySrcLoc, 31786 operand: Air.Inst.Ref, 31787 ) CompileError!Air.Inst.Ref { 31788 const mod = sema.mod; 31789 const operand_ty = sema.typeOf(operand); 31790 31791 if (try sema.resolveValue(operand)) |val| { 31792 switch (mod.intern_pool.indexToKey(val.toIntern())) { 31793 .extern_func => |extern_func| return sema.analyzeDeclRef(extern_func.decl), 31794 .func => |func| return sema.analyzeDeclRef(func.owner_decl), 31795 else => return anonDeclRef(sema, val.toIntern()), 31796 } 31797 } 31798 31799 try sema.requireRuntimeBlock(block, src, null); 31800 const address_space = target_util.defaultAddressSpace(mod.getTarget(), .local); 31801 const ptr_type = try sema.ptrType(.{ 31802 .child = operand_ty.toIntern(), 31803 .flags = .{ 31804 .is_const = true, 31805 .address_space = address_space, 31806 }, 31807 }); 31808 const mut_ptr_type = try sema.ptrType(.{ 31809 .child = operand_ty.toIntern(), 31810 .flags = .{ .address_space = address_space }, 31811 }); 31812 const alloc = try block.addTy(.alloc, mut_ptr_type); 31813 try sema.storePtr(block, src, alloc, operand); 31814 31815 // TODO: Replace with sema.coerce when that supports adding pointer constness. 31816 return sema.bitCast(block, ptr_type, alloc, src, null); 31817 } 31818 31819 fn analyzeLoad( 31820 sema: *Sema, 31821 block: *Block, 31822 src: LazySrcLoc, 31823 ptr: Air.Inst.Ref, 31824 ptr_src: LazySrcLoc, 31825 ) CompileError!Air.Inst.Ref { 31826 const mod = sema.mod; 31827 const ptr_ty = sema.typeOf(ptr); 31828 const elem_ty = switch (ptr_ty.zigTypeTag(mod)) { 31829 .Pointer => ptr_ty.childType(mod), 31830 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}), 31831 }; 31832 if (elem_ty.zigTypeTag(mod) == .Opaque) { 31833 return sema.fail(block, ptr_src, "cannot load opaque type '{}'", .{elem_ty.fmt(mod)}); 31834 } 31835 31836 if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| { 31837 return Air.internedToRef(opv.toIntern()); 31838 } 31839 31840 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 31841 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| { 31842 return Air.internedToRef(elem_val.toIntern()); 31843 } 31844 } 31845 31846 if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) { 31847 const ptr_inst = ptr.toIndex().?; 31848 const air_tags = sema.air_instructions.items(.tag); 31849 if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) { 31850 const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl; 31851 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 31852 return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs); 31853 } 31854 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{ 31855 ptr_ty.fmt(sema.mod), 31856 }); 31857 } 31858 31859 return block.addTyOp(.load, elem_ty, ptr); 31860 } 31861 31862 fn analyzeSlicePtr( 31863 sema: *Sema, 31864 block: *Block, 31865 slice_src: LazySrcLoc, 31866 slice: Air.Inst.Ref, 31867 slice_ty: Type, 31868 ) CompileError!Air.Inst.Ref { 31869 const mod = sema.mod; 31870 const result_ty = slice_ty.slicePtrFieldType(mod); 31871 if (try sema.resolveValue(slice)) |val| { 31872 if (val.isUndef(mod)) return mod.undefRef(result_ty); 31873 return Air.internedToRef(val.slicePtr(mod).toIntern()); 31874 } 31875 try sema.requireRuntimeBlock(block, slice_src, null); 31876 return block.addTyOp(.slice_ptr, result_ty, slice); 31877 } 31878 31879 fn analyzeSliceLen( 31880 sema: *Sema, 31881 block: *Block, 31882 src: LazySrcLoc, 31883 slice_inst: Air.Inst.Ref, 31884 ) CompileError!Air.Inst.Ref { 31885 const mod = sema.mod; 31886 if (try sema.resolveValue(slice_inst)) |slice_val| { 31887 if (slice_val.isUndef(mod)) { 31888 return mod.undefRef(Type.usize); 31889 } 31890 return mod.intRef(Type.usize, slice_val.sliceLen(sema.mod)); 31891 } 31892 try sema.requireRuntimeBlock(block, src, null); 31893 return block.addTyOp(.slice_len, Type.usize, slice_inst); 31894 } 31895 31896 fn analyzeIsNull( 31897 sema: *Sema, 31898 block: *Block, 31899 src: LazySrcLoc, 31900 operand: Air.Inst.Ref, 31901 invert_logic: bool, 31902 ) CompileError!Air.Inst.Ref { 31903 const mod = sema.mod; 31904 const result_ty = Type.bool; 31905 if (try sema.resolveValue(operand)) |opt_val| { 31906 if (opt_val.isUndef(mod)) { 31907 return mod.undefRef(result_ty); 31908 } 31909 const is_null = opt_val.isNull(mod); 31910 const bool_value = if (invert_logic) !is_null else is_null; 31911 return if (bool_value) .bool_true else .bool_false; 31912 } 31913 31914 const inverted_non_null_res: Air.Inst.Ref = if (invert_logic) .bool_true else .bool_false; 31915 const operand_ty = sema.typeOf(operand); 31916 if (operand_ty.zigTypeTag(mod) == .Optional and operand_ty.optionalChild(mod).zigTypeTag(mod) == .NoReturn) { 31917 return inverted_non_null_res; 31918 } 31919 if (operand_ty.zigTypeTag(mod) != .Optional and !operand_ty.isPtrLikeOptional(mod)) { 31920 return inverted_non_null_res; 31921 } 31922 try sema.requireRuntimeBlock(block, src, null); 31923 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; 31924 return block.addUnOp(air_tag, operand); 31925 } 31926 31927 fn analyzePtrIsNonErrComptimeOnly( 31928 sema: *Sema, 31929 block: *Block, 31930 src: LazySrcLoc, 31931 operand: Air.Inst.Ref, 31932 ) CompileError!Air.Inst.Ref { 31933 const mod = sema.mod; 31934 const ptr_ty = sema.typeOf(operand); 31935 assert(ptr_ty.zigTypeTag(mod) == .Pointer); 31936 const child_ty = ptr_ty.childType(mod); 31937 31938 const child_tag = child_ty.zigTypeTag(mod); 31939 if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return .bool_true; 31940 if (child_tag == .ErrorSet) return .bool_false; 31941 assert(child_tag == .ErrorUnion); 31942 31943 _ = block; 31944 _ = src; 31945 31946 return .none; 31947 } 31948 31949 fn analyzeIsNonErrComptimeOnly( 31950 sema: *Sema, 31951 block: *Block, 31952 src: LazySrcLoc, 31953 operand: Air.Inst.Ref, 31954 ) CompileError!Air.Inst.Ref { 31955 const mod = sema.mod; 31956 const ip = &mod.intern_pool; 31957 const operand_ty = sema.typeOf(operand); 31958 const ot = operand_ty.zigTypeTag(mod); 31959 if (ot != .ErrorSet and ot != .ErrorUnion) return .bool_true; 31960 if (ot == .ErrorSet) return .bool_false; 31961 assert(ot == .ErrorUnion); 31962 31963 const payload_ty = operand_ty.errorUnionPayload(mod); 31964 if (payload_ty.zigTypeTag(mod) == .NoReturn) { 31965 return .bool_false; 31966 } 31967 31968 if (operand.toIndex()) |operand_inst| { 31969 switch (sema.air_instructions.items(.tag)[@intFromEnum(operand_inst)]) { 31970 .wrap_errunion_payload => return .bool_true, 31971 .wrap_errunion_err => return .bool_false, 31972 else => {}, 31973 } 31974 } else if (operand == .undef) { 31975 return mod.undefRef(Type.bool); 31976 } else if (@intFromEnum(operand) < InternPool.static_len) { 31977 // None of the ref tags can be errors. 31978 return .bool_true; 31979 } 31980 31981 const maybe_operand_val = try sema.resolveValue(operand); 31982 31983 // exception if the error union error set is known to be empty, 31984 // we allow the comparison but always make it comptime-known. 31985 const set_ty = ip.errorUnionSet(operand_ty.toIntern()); 31986 switch (set_ty) { 31987 .anyerror_type => {}, 31988 .adhoc_inferred_error_set_type => if (sema.fn_ret_ty_ies) |ies| blk: { 31989 // If the error set is empty, we must return a comptime true or false. 31990 // However we want to avoid unnecessarily resolving an inferred error set 31991 // in case it is already non-empty. 31992 switch (ies.resolved) { 31993 .anyerror_type => break :blk, 31994 .none => {}, 31995 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, 31996 } 31997 31998 if (maybe_operand_val != null) break :blk; 31999 32000 // Try to avoid resolving inferred error set if possible. 32001 if (ies.errors.count() != 0) return .none; 32002 switch (ies.resolved) { 32003 .anyerror_type => return .none, 32004 .none => {}, 32005 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) { 32006 0 => return .bool_true, 32007 else => return .none, 32008 }, 32009 } 32010 // We do not have a comptime answer because this inferred error 32011 // set is not resolved, and an instruction later in this function 32012 // body may or may not cause an error to be added to this set. 32013 return .none; 32014 }, 32015 else => switch (ip.indexToKey(set_ty)) { 32016 .error_set_type => |error_set_type| { 32017 if (error_set_type.names.len == 0) return .bool_true; 32018 }, 32019 .inferred_error_set_type => |func_index| blk: { 32020 // If the error set is empty, we must return a comptime true or false. 32021 // However we want to avoid unnecessarily resolving an inferred error set 32022 // in case it is already non-empty. 32023 switch (ip.funcIesResolved(func_index).*) { 32024 .anyerror_type => break :blk, 32025 .none => {}, 32026 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, 32027 } 32028 if (maybe_operand_val != null) break :blk; 32029 if (sema.fn_ret_ty_ies) |ies| { 32030 if (ies.func == func_index) { 32031 // Try to avoid resolving inferred error set if possible. 32032 if (ies.errors.count() != 0) return .none; 32033 switch (ies.resolved) { 32034 .anyerror_type => return .none, 32035 .none => {}, 32036 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) { 32037 0 => return .bool_true, 32038 else => return .none, 32039 }, 32040 } 32041 // We do not have a comptime answer because this inferred error 32042 // set is not resolved, and an instruction later in this function 32043 // body may or may not cause an error to be added to this set. 32044 return .none; 32045 } 32046 } 32047 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty); 32048 if (resolved_ty == .anyerror_type) 32049 break :blk; 32050 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0) 32051 return .bool_true; 32052 }, 32053 else => unreachable, 32054 }, 32055 } 32056 32057 if (maybe_operand_val) |err_union| { 32058 if (err_union.isUndef(mod)) { 32059 return mod.undefRef(Type.bool); 32060 } 32061 if (err_union.getErrorName(mod) == .none) { 32062 return .bool_true; 32063 } else { 32064 return .bool_false; 32065 } 32066 } 32067 return .none; 32068 } 32069 32070 fn analyzeIsNonErr( 32071 sema: *Sema, 32072 block: *Block, 32073 src: LazySrcLoc, 32074 operand: Air.Inst.Ref, 32075 ) CompileError!Air.Inst.Ref { 32076 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); 32077 if (result == .none) { 32078 try sema.requireRuntimeBlock(block, src, null); 32079 return block.addUnOp(.is_non_err, operand); 32080 } else { 32081 return result; 32082 } 32083 } 32084 32085 fn analyzePtrIsNonErr( 32086 sema: *Sema, 32087 block: *Block, 32088 src: LazySrcLoc, 32089 operand: Air.Inst.Ref, 32090 ) CompileError!Air.Inst.Ref { 32091 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand); 32092 if (result == .none) { 32093 try sema.requireRuntimeBlock(block, src, null); 32094 return block.addUnOp(.is_non_err_ptr, operand); 32095 } else { 32096 return result; 32097 } 32098 } 32099 32100 fn analyzeSlice( 32101 sema: *Sema, 32102 block: *Block, 32103 src: LazySrcLoc, 32104 ptr_ptr: Air.Inst.Ref, 32105 uncasted_start: Air.Inst.Ref, 32106 uncasted_end_opt: Air.Inst.Ref, 32107 sentinel_opt: Air.Inst.Ref, 32108 sentinel_src: LazySrcLoc, 32109 ptr_src: LazySrcLoc, 32110 start_src: LazySrcLoc, 32111 end_src: LazySrcLoc, 32112 by_length: bool, 32113 ) CompileError!Air.Inst.Ref { 32114 const mod = sema.mod; 32115 // Slice expressions can operate on a variable whose type is an array. This requires 32116 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. 32117 const ptr_ptr_ty = sema.typeOf(ptr_ptr); 32118 const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(mod)) { 32119 .Pointer => ptr_ptr_ty.childType(mod), 32120 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(mod)}), 32121 }; 32122 32123 var array_ty = ptr_ptr_child_ty; 32124 var slice_ty = ptr_ptr_ty; 32125 var ptr_or_slice = ptr_ptr; 32126 var elem_ty: Type = undefined; 32127 var ptr_sentinel: ?Value = null; 32128 switch (ptr_ptr_child_ty.zigTypeTag(mod)) { 32129 .Array => { 32130 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 32131 elem_ty = ptr_ptr_child_ty.childType(mod); 32132 }, 32133 .Pointer => switch (ptr_ptr_child_ty.ptrSize(mod)) { 32134 .One => { 32135 const double_child_ty = ptr_ptr_child_ty.childType(mod); 32136 if (double_child_ty.zigTypeTag(mod) == .Array) { 32137 ptr_sentinel = double_child_ty.sentinel(mod); 32138 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 32139 slice_ty = ptr_ptr_child_ty; 32140 array_ty = double_child_ty; 32141 elem_ty = double_child_ty.childType(mod); 32142 } else { 32143 return sema.fail(block, src, "slice of single-item pointer", .{}); 32144 } 32145 }, 32146 .Many, .C => { 32147 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 32148 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 32149 slice_ty = ptr_ptr_child_ty; 32150 array_ty = ptr_ptr_child_ty; 32151 elem_ty = ptr_ptr_child_ty.childType(mod); 32152 32153 if (ptr_ptr_child_ty.ptrSize(mod) == .C) { 32154 if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| { 32155 if (ptr_val.isNull(mod)) { 32156 return sema.fail(block, src, "slice of null pointer", .{}); 32157 } 32158 } 32159 } 32160 }, 32161 .Slice => { 32162 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 32163 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 32164 slice_ty = ptr_ptr_child_ty; 32165 array_ty = ptr_ptr_child_ty; 32166 elem_ty = ptr_ptr_child_ty.childType(mod); 32167 }, 32168 }, 32169 else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}), 32170 } 32171 32172 const ptr = if (slice_ty.isSlice(mod)) 32173 try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) 32174 else if (array_ty.zigTypeTag(mod) == .Array) ptr: { 32175 var manyptr_ty_key = mod.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type; 32176 assert(manyptr_ty_key.child == array_ty.toIntern()); 32177 assert(manyptr_ty_key.flags.size == .One); 32178 manyptr_ty_key.child = elem_ty.toIntern(); 32179 manyptr_ty_key.flags.size = .Many; 32180 break :ptr try sema.coerceCompatiblePtrs(block, try sema.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src); 32181 } else ptr_or_slice; 32182 32183 const start = try sema.coerce(block, Type.usize, uncasted_start, start_src); 32184 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); 32185 const new_ptr_ty = sema.typeOf(new_ptr); 32186 32187 // true if and only if the end index of the slice, implicitly or explicitly, equals 32188 // the length of the underlying object being sliced. we might learn the length of the 32189 // underlying object because it is an array (which has the length in the type), or 32190 // we might learn of the length because it is a comptime-known slice value. 32191 var end_is_len = uncasted_end_opt == .none; 32192 const end = e: { 32193 if (array_ty.zigTypeTag(mod) == .Array) { 32194 const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod)); 32195 32196 if (!end_is_len) { 32197 const end = if (by_length) end: { 32198 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 32199 if (try sema.resolveValue(len)) |slice_len_val| { 32200 const len_s_val = try mod.intValue( 32201 Type.usize, 32202 array_ty.arrayLenIncludingSentinel(mod), 32203 ); 32204 if (!(try sema.compareScalar(slice_len_val, .lte, len_s_val, Type.usize))) { 32205 const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null) 32206 " +1 (sentinel)" 32207 else 32208 ""; 32209 32210 return sema.fail( 32211 block, 32212 end_src, 32213 "length {} out of bounds for array of length {}{s}", 32214 .{ 32215 slice_len_val.fmtValue(Type.usize, mod), 32216 len_val.fmtValue(Type.usize, mod), 32217 sentinel_label, 32218 }, 32219 ); 32220 } 32221 } 32222 // check len is less than array size if comptime known 32223 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 32224 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); 32225 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 32226 if (try sema.resolveValue(end)) |end_val| { 32227 const len_s_val = try mod.intValue( 32228 Type.usize, 32229 array_ty.arrayLenIncludingSentinel(mod), 32230 ); 32231 if (!(try sema.compareAll(end_val, .lte, len_s_val, Type.usize))) { 32232 const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null) 32233 " +1 (sentinel)" 32234 else 32235 ""; 32236 32237 return sema.fail( 32238 block, 32239 end_src, 32240 "end index {} out of bounds for array of length {}{s}", 32241 .{ 32242 end_val.fmtValue(Type.usize, mod), 32243 len_val.fmtValue(Type.usize, mod), 32244 sentinel_label, 32245 }, 32246 ); 32247 } 32248 32249 // end_is_len is only true if we are NOT using the sentinel 32250 // length. For sentinel-length, we don't want the type to 32251 // contain the sentinel. 32252 if (end_val.eql(len_val, Type.usize, mod)) { 32253 end_is_len = true; 32254 } 32255 } 32256 break :e end; 32257 } 32258 32259 break :e Air.internedToRef(len_val.toIntern()); 32260 } else if (slice_ty.isSlice(mod)) { 32261 if (!end_is_len) { 32262 const end = if (by_length) end: { 32263 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 32264 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 32265 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); 32266 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 32267 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 32268 if (try sema.resolveValue(ptr_or_slice)) |slice_val| { 32269 if (slice_val.isUndef(mod)) { 32270 return sema.fail(block, src, "slice of undefined", .{}); 32271 } 32272 const has_sentinel = slice_ty.sentinel(mod) != null; 32273 const slice_len = slice_val.sliceLen(mod); 32274 const len_plus_sent = slice_len + @intFromBool(has_sentinel); 32275 const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent); 32276 if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) { 32277 const sentinel_label: []const u8 = if (has_sentinel) 32278 " +1 (sentinel)" 32279 else 32280 ""; 32281 32282 return sema.fail( 32283 block, 32284 end_src, 32285 "end index {} out of bounds for slice of length {d}{s}", 32286 .{ 32287 end_val.fmtValue(Type.usize, mod), 32288 slice_val.sliceLen(mod), 32289 sentinel_label, 32290 }, 32291 ); 32292 } 32293 32294 // If the slice has a sentinel, we consider end_is_len 32295 // is only true if it equals the length WITHOUT the 32296 // sentinel, so we don't add a sentinel type. 32297 const slice_len_val = try mod.intValue(Type.usize, slice_len); 32298 if (end_val.eql(slice_len_val, Type.usize, mod)) { 32299 end_is_len = true; 32300 } 32301 } 32302 } 32303 break :e end; 32304 } 32305 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); 32306 } 32307 if (!end_is_len) { 32308 if (by_length) { 32309 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 32310 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 32311 break :e try sema.coerce(block, Type.usize, uncasted_end, end_src); 32312 } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 32313 } 32314 return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); 32315 }; 32316 32317 const sentinel = s: { 32318 if (sentinel_opt != .none) { 32319 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); 32320 break :s try sema.resolveConstDefinedValue(block, sentinel_src, casted, .{ 32321 .needed_comptime_reason = "slice sentinel must be comptime-known", 32322 }); 32323 } 32324 // If we are slicing to the end of something that is sentinel-terminated 32325 // then the resulting slice type is also sentinel-terminated. 32326 if (end_is_len) { 32327 if (ptr_sentinel) |sent| { 32328 break :s sent; 32329 } 32330 } 32331 break :s null; 32332 }; 32333 const slice_sentinel = if (sentinel_opt != .none) sentinel else null; 32334 32335 var checked_start_lte_end = by_length; 32336 var runtime_src: ?LazySrcLoc = null; 32337 32338 // requirement: start <= end 32339 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 32340 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { 32341 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) { 32342 return sema.fail( 32343 block, 32344 start_src, 32345 "start index {} is larger than end index {}", 32346 .{ 32347 start_val.fmtValue(Type.usize, mod), 32348 end_val.fmtValue(Type.usize, mod), 32349 }, 32350 ); 32351 } 32352 checked_start_lte_end = true; 32353 if (try sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: { 32354 const expected_sentinel = sentinel orelse break :sentinel_check; 32355 const start_int = start_val.getUnsignedInt(mod).?; 32356 const end_int = end_val.getUnsignedInt(mod).?; 32357 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); 32358 32359 const many_ptr_ty = try mod.manyConstPtrType(elem_ty); 32360 const many_ptr_val = try mod.getCoerced(ptr_val, many_ptr_ty); 32361 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty); 32362 const elem_ptr = try many_ptr_val.elemPtr(elem_ptr_ty, sentinel_index, mod); 32363 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty); 32364 const actual_sentinel = switch (res) { 32365 .runtime_load => break :sentinel_check, 32366 .val => |v| v, 32367 .needed_well_defined => |ty| return sema.fail( 32368 block, 32369 src, 32370 "comptime dereference requires '{}' to have a well-defined layout, but it does not.", 32371 .{ty.fmt(mod)}, 32372 ), 32373 .out_of_bounds => |ty| return sema.fail( 32374 block, 32375 end_src, 32376 "slice end index {d} exceeds bounds of containing decl of type '{}'", 32377 .{ end_int, ty.fmt(mod) }, 32378 ), 32379 }; 32380 32381 if (!actual_sentinel.eql(expected_sentinel, elem_ty, mod)) { 32382 const msg = msg: { 32383 const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{}); 32384 errdefer msg.destroy(sema.gpa); 32385 try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{ 32386 expected_sentinel.fmtValue(elem_ty, mod), 32387 actual_sentinel.fmtValue(elem_ty, mod), 32388 }); 32389 32390 break :msg msg; 32391 }; 32392 return sema.failWithOwnedErrorMsg(block, msg); 32393 } 32394 } else { 32395 runtime_src = ptr_src; 32396 } 32397 } else { 32398 runtime_src = start_src; 32399 } 32400 } else { 32401 runtime_src = end_src; 32402 } 32403 32404 if (!checked_start_lte_end and block.wantSafety() and !block.is_comptime) { 32405 // requirement: start <= end 32406 assert(!block.is_comptime); 32407 try sema.requireRuntimeBlock(block, src, runtime_src.?); 32408 const ok = try block.addBinOp(.cmp_lte, start, end); 32409 if (!sema.mod.comp.formatted_panics) { 32410 try sema.addSafetyCheck(block, src, ok, .start_index_greater_than_end); 32411 } else { 32412 try sema.safetyCheckFormatted(block, src, ok, "panicStartGreaterThanEnd", &.{ start, end }); 32413 } 32414 } 32415 const new_len = if (by_length) 32416 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src) 32417 else 32418 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); 32419 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); 32420 32421 const new_ptr_ty_info = new_ptr_ty.ptrInfo(mod); 32422 const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(mod) != .C; 32423 32424 if (opt_new_len_val) |new_len_val| { 32425 const new_len_int = new_len_val.toUnsignedInt(mod); 32426 32427 const return_ty = try sema.ptrType(.{ 32428 .child = (try mod.arrayType(.{ 32429 .len = new_len_int, 32430 .sentinel = if (sentinel) |s| s.toIntern() else .none, 32431 .child = elem_ty.toIntern(), 32432 })).toIntern(), 32433 .flags = .{ 32434 .alignment = new_ptr_ty_info.flags.alignment, 32435 .is_const = new_ptr_ty_info.flags.is_const, 32436 .is_allowzero = new_allowzero, 32437 .is_volatile = new_ptr_ty_info.flags.is_volatile, 32438 .address_space = new_ptr_ty_info.flags.address_space, 32439 }, 32440 }); 32441 32442 const opt_new_ptr_val = try sema.resolveValue(new_ptr); 32443 const new_ptr_val = opt_new_ptr_val orelse { 32444 const result = try block.addBitCast(return_ty, new_ptr); 32445 if (block.wantSafety()) { 32446 // requirement: slicing C ptr is non-null 32447 if (ptr_ptr_child_ty.isCPtr(mod)) { 32448 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); 32449 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 32450 } 32451 32452 if (slice_ty.isSlice(mod)) { 32453 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); 32454 const actual_len = if (slice_ty.sentinel(mod) == null) 32455 slice_len_inst 32456 else 32457 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); 32458 32459 const actual_end = if (slice_sentinel != null) 32460 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 32461 else 32462 end; 32463 32464 try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte); 32465 } 32466 32467 // requirement: result[new_len] == slice_sentinel 32468 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); 32469 } 32470 return result; 32471 }; 32472 32473 if (!new_ptr_val.isUndef(mod)) { 32474 return Air.internedToRef((try mod.getCoerced( 32475 Value.fromInterned((try new_ptr_val.intern(new_ptr_ty, mod))), 32476 return_ty, 32477 )).toIntern()); 32478 } 32479 32480 // Special case: @as([]i32, undefined)[x..x] 32481 if (new_len_int == 0) { 32482 return mod.undefRef(return_ty); 32483 } 32484 32485 return sema.fail(block, src, "non-zero length slice of undefined pointer", .{}); 32486 } 32487 32488 const return_ty = try sema.ptrType(.{ 32489 .child = elem_ty.toIntern(), 32490 .sentinel = if (sentinel) |s| s.toIntern() else .none, 32491 .flags = .{ 32492 .size = .Slice, 32493 .alignment = new_ptr_ty_info.flags.alignment, 32494 .is_const = new_ptr_ty_info.flags.is_const, 32495 .is_volatile = new_ptr_ty_info.flags.is_volatile, 32496 .is_allowzero = new_allowzero, 32497 .address_space = new_ptr_ty_info.flags.address_space, 32498 }, 32499 }); 32500 32501 try sema.requireRuntimeBlock(block, src, runtime_src.?); 32502 if (block.wantSafety()) { 32503 // requirement: slicing C ptr is non-null 32504 if (ptr_ptr_child_ty.isCPtr(mod)) { 32505 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); 32506 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); 32507 } 32508 32509 // requirement: end <= len 32510 const opt_len_inst = if (array_ty.zigTypeTag(mod) == .Array) 32511 try mod.intRef(Type.usize, array_ty.arrayLenIncludingSentinel(mod)) 32512 else if (slice_ty.isSlice(mod)) blk: { 32513 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { 32514 // we don't need to add one for sentinels because the 32515 // underlying value data includes the sentinel 32516 break :blk try mod.intRef(Type.usize, slice_val.sliceLen(mod)); 32517 } 32518 32519 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); 32520 if (slice_ty.sentinel(mod) == null) break :blk slice_len_inst; 32521 32522 // we have to add one because slice lengths don't include the sentinel 32523 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); 32524 } else null; 32525 if (opt_len_inst) |len_inst| { 32526 const actual_end = if (slice_sentinel != null) 32527 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 32528 else 32529 end; 32530 try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte); 32531 } 32532 32533 // requirement: start <= end 32534 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte); 32535 } 32536 const result = try block.addInst(.{ 32537 .tag = .slice, 32538 .data = .{ .ty_pl = .{ 32539 .ty = Air.internedToRef(return_ty.toIntern()), 32540 .payload = try sema.addExtra(Air.Bin{ 32541 .lhs = new_ptr, 32542 .rhs = new_len, 32543 }), 32544 } }, 32545 }); 32546 if (block.wantSafety()) { 32547 // requirement: result[new_len] == slice_sentinel 32548 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); 32549 } 32550 return result; 32551 } 32552 32553 /// Asserts that lhs and rhs types are both numeric. 32554 fn cmpNumeric( 32555 sema: *Sema, 32556 block: *Block, 32557 src: LazySrcLoc, 32558 uncasted_lhs: Air.Inst.Ref, 32559 uncasted_rhs: Air.Inst.Ref, 32560 op: std.math.CompareOperator, 32561 lhs_src: LazySrcLoc, 32562 rhs_src: LazySrcLoc, 32563 ) CompileError!Air.Inst.Ref { 32564 const mod = sema.mod; 32565 const lhs_ty = sema.typeOf(uncasted_lhs); 32566 const rhs_ty = sema.typeOf(uncasted_rhs); 32567 32568 assert(lhs_ty.isNumeric(mod)); 32569 assert(rhs_ty.isNumeric(mod)); 32570 32571 const lhs_ty_tag = lhs_ty.zigTypeTag(mod); 32572 const rhs_ty_tag = rhs_ty.zigTypeTag(mod); 32573 const target = mod.getTarget(); 32574 32575 // One exception to heterogeneous comparison: comptime_float needs to 32576 // coerce to fixed-width float. 32577 32578 const lhs = if (lhs_ty_tag == .ComptimeFloat and rhs_ty_tag == .Float) 32579 try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src) 32580 else 32581 uncasted_lhs; 32582 32583 const rhs = if (lhs_ty_tag == .Float and rhs_ty_tag == .ComptimeFloat) 32584 try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src) 32585 else 32586 uncasted_rhs; 32587 32588 const runtime_src: LazySrcLoc = src: { 32589 if (try sema.resolveValue(lhs)) |lhs_val| { 32590 if (try sema.resolveValue(rhs)) |rhs_val| { 32591 // Compare ints: const vs. undefined (or vice versa) 32592 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod) and rhs_val.isUndef(mod)) { 32593 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| { 32594 return if (res) .bool_true else .bool_false; 32595 } 32596 } else if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod) and lhs_val.isUndef(mod)) { 32597 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| { 32598 return if (res) .bool_true else .bool_false; 32599 } 32600 } 32601 32602 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 32603 return mod.undefRef(Type.bool); 32604 } 32605 if (lhs_val.isNan(mod) or rhs_val.isNan(mod)) { 32606 return if (op == std.math.CompareOperator.neq) .bool_true else .bool_false; 32607 } 32608 return if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, mod, sema)) 32609 .bool_true 32610 else 32611 .bool_false; 32612 } else { 32613 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod)) { 32614 // Compare ints: const vs. var 32615 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| { 32616 return if (res) .bool_true else .bool_false; 32617 } 32618 } 32619 break :src rhs_src; 32620 } 32621 } else { 32622 if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| { 32623 if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod)) { 32624 // Compare ints: var vs. const 32625 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| { 32626 return if (res) .bool_true else .bool_false; 32627 } 32628 } 32629 } 32630 break :src lhs_src; 32631 } 32632 }; 32633 32634 // TODO handle comparisons against lazy zero values 32635 // Some values can be compared against zero without being runtime-known or without forcing 32636 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to 32637 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout 32638 // of this function if we don't need to. 32639 try sema.requireRuntimeBlock(block, src, runtime_src); 32640 32641 // For floats, emit a float comparison instruction. 32642 const lhs_is_float = switch (lhs_ty_tag) { 32643 .Float, .ComptimeFloat => true, 32644 else => false, 32645 }; 32646 const rhs_is_float = switch (rhs_ty_tag) { 32647 .Float, .ComptimeFloat => true, 32648 else => false, 32649 }; 32650 32651 if (lhs_is_float and rhs_is_float) { 32652 // Smaller fixed-width floats coerce to larger fixed-width floats. 32653 // comptime_float coerces to fixed-width float. 32654 const dest_ty = x: { 32655 if (lhs_ty_tag == .ComptimeFloat) { 32656 break :x rhs_ty; 32657 } else if (rhs_ty_tag == .ComptimeFloat) { 32658 break :x lhs_ty; 32659 } 32660 if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) { 32661 break :x lhs_ty; 32662 } else { 32663 break :x rhs_ty; 32664 } 32665 }; 32666 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 32667 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 32668 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs); 32669 } 32670 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. 32671 // For mixed signed and unsigned integers, implicit cast both operands to a signed 32672 // integer with + 1 bit. 32673 // For mixed floats and integers, extract the integer part from the float, cast that to 32674 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, 32675 // add/subtract 1. 32676 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| 32677 !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema)) 32678 else 32679 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(mod)); 32680 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| 32681 !(try rhs_val.compareAllWithZeroAdvanced(.gte, sema)) 32682 else 32683 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(mod)); 32684 const dest_int_is_signed = lhs_is_signed or rhs_is_signed; 32685 32686 var dest_float_type: ?Type = null; 32687 32688 var lhs_bits: usize = undefined; 32689 if (try sema.resolveValueResolveLazy(lhs)) |lhs_val| { 32690 if (lhs_val.isUndef(mod)) 32691 return mod.undefRef(Type.bool); 32692 if (lhs_val.isNan(mod)) switch (op) { 32693 .neq => return .bool_true, 32694 else => return .bool_false, 32695 }; 32696 if (lhs_val.isInf(mod)) switch (op) { 32697 .neq => return .bool_true, 32698 .eq => return .bool_false, 32699 .gt, .gte => return if (lhs_val.isNegativeInf(mod)) .bool_false else .bool_true, 32700 .lt, .lte => return if (lhs_val.isNegativeInf(mod)) .bool_true else .bool_false, 32701 }; 32702 if (!rhs_is_signed) { 32703 switch (lhs_val.orderAgainstZero(mod)) { 32704 .gt => {}, 32705 .eq => switch (op) { // LHS = 0, RHS is unsigned 32706 .lte => return .bool_true, 32707 .gt => return .bool_false, 32708 else => {}, 32709 }, 32710 .lt => switch (op) { // LHS < 0, RHS is unsigned 32711 .neq, .lt, .lte => return .bool_true, 32712 .eq, .gt, .gte => return .bool_false, 32713 }, 32714 } 32715 } 32716 if (lhs_is_float) { 32717 if (lhs_val.floatHasFraction(mod)) { 32718 switch (op) { 32719 .eq => return .bool_false, 32720 .neq => return .bool_true, 32721 else => {}, 32722 } 32723 } 32724 32725 var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, mod)); 32726 defer bigint.deinit(); 32727 if (lhs_val.floatHasFraction(mod)) { 32728 if (lhs_is_signed) { 32729 try bigint.addScalar(&bigint, -1); 32730 } else { 32731 try bigint.addScalar(&bigint, 1); 32732 } 32733 } 32734 lhs_bits = bigint.toConst().bitCountTwosComp(); 32735 } else { 32736 lhs_bits = lhs_val.intBitCountTwosComp(mod); 32737 } 32738 lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed); 32739 } else if (lhs_is_float) { 32740 dest_float_type = lhs_ty; 32741 } else { 32742 const int_info = lhs_ty.intInfo(mod); 32743 lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 32744 } 32745 32746 var rhs_bits: usize = undefined; 32747 if (try sema.resolveValueResolveLazy(rhs)) |rhs_val| { 32748 if (rhs_val.isUndef(mod)) 32749 return mod.undefRef(Type.bool); 32750 if (rhs_val.isNan(mod)) switch (op) { 32751 .neq => return .bool_true, 32752 else => return .bool_false, 32753 }; 32754 if (rhs_val.isInf(mod)) switch (op) { 32755 .neq => return .bool_true, 32756 .eq => return .bool_false, 32757 .gt, .gte => return if (rhs_val.isNegativeInf(mod)) .bool_true else .bool_false, 32758 .lt, .lte => return if (rhs_val.isNegativeInf(mod)) .bool_false else .bool_true, 32759 }; 32760 if (!lhs_is_signed) { 32761 switch (rhs_val.orderAgainstZero(mod)) { 32762 .gt => {}, 32763 .eq => switch (op) { // RHS = 0, LHS is unsigned 32764 .gte => return .bool_true, 32765 .lt => return .bool_false, 32766 else => {}, 32767 }, 32768 .lt => switch (op) { // RHS < 0, LHS is unsigned 32769 .neq, .gt, .gte => return .bool_true, 32770 .eq, .lt, .lte => return .bool_false, 32771 }, 32772 } 32773 } 32774 if (rhs_is_float) { 32775 if (rhs_val.floatHasFraction(mod)) { 32776 switch (op) { 32777 .eq => return .bool_false, 32778 .neq => return .bool_true, 32779 else => {}, 32780 } 32781 } 32782 32783 var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, mod)); 32784 defer bigint.deinit(); 32785 if (rhs_val.floatHasFraction(mod)) { 32786 if (rhs_is_signed) { 32787 try bigint.addScalar(&bigint, -1); 32788 } else { 32789 try bigint.addScalar(&bigint, 1); 32790 } 32791 } 32792 rhs_bits = bigint.toConst().bitCountTwosComp(); 32793 } else { 32794 rhs_bits = rhs_val.intBitCountTwosComp(mod); 32795 } 32796 rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed); 32797 } else if (rhs_is_float) { 32798 dest_float_type = rhs_ty; 32799 } else { 32800 const int_info = rhs_ty.intInfo(mod); 32801 rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 32802 } 32803 32804 const dest_ty = if (dest_float_type) |ft| ft else blk: { 32805 const max_bits = @max(lhs_bits, rhs_bits); 32806 const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}); 32807 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned; 32808 break :blk try mod.intType(signedness, casted_bits); 32809 }; 32810 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 32811 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 32812 32813 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs); 32814 } 32815 32816 /// Asserts that LHS value is an int or comptime int and not undefined, and 32817 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to 32818 /// determine whether `op` has a guaranteed result. 32819 /// If it cannot be determined, returns null. 32820 /// Otherwise returns a bool for the guaranteed comparison operation. 32821 fn compareIntsOnlyPossibleResult( 32822 sema: *Sema, 32823 lhs_val: Value, 32824 op: std.math.CompareOperator, 32825 rhs_ty: Type, 32826 ) Allocator.Error!?bool { 32827 const mod = sema.mod; 32828 const rhs_info = rhs_ty.intInfo(mod); 32829 const vs_zero = lhs_val.orderAgainstZeroAdvanced(mod, sema) catch unreachable; 32830 const is_zero = vs_zero == .eq; 32831 const is_negative = vs_zero == .lt; 32832 const is_positive = vs_zero == .gt; 32833 32834 // Anything vs. zero-sized type has guaranteed outcome. 32835 if (rhs_info.bits == 0) return switch (op) { 32836 .eq, .lte, .gte => is_zero, 32837 .neq, .lt, .gt => !is_zero, 32838 }; 32839 32840 // Special case for i1, which can only be 0 or -1. 32841 // Zero and positive ints have guaranteed outcome. 32842 if (rhs_info.bits == 1 and rhs_info.signedness == .signed) { 32843 if (is_positive) return switch (op) { 32844 .gt, .gte, .neq => true, 32845 .lt, .lte, .eq => false, 32846 }; 32847 if (is_zero) return switch (op) { 32848 .gte => true, 32849 .lt => false, 32850 .gt, .lte, .eq, .neq => null, 32851 }; 32852 } 32853 32854 // Negative vs. unsigned has guaranteed outcome. 32855 if (rhs_info.signedness == .unsigned and is_negative) return switch (op) { 32856 .eq, .gt, .gte => false, 32857 .neq, .lt, .lte => true, 32858 }; 32859 32860 const sign_adj = @intFromBool(!is_negative and rhs_info.signedness == .signed); 32861 const req_bits = lhs_val.intBitCountTwosComp(mod) + sign_adj; 32862 32863 // No sized type can have more than 65535 bits. 32864 // The RHS type operand is either a runtime value or sized (but undefined) constant. 32865 if (req_bits > 65535) return switch (op) { 32866 .lt, .lte => is_negative, 32867 .gt, .gte => is_positive, 32868 .eq => false, 32869 .neq => true, 32870 }; 32871 const fits = req_bits <= rhs_info.bits; 32872 32873 // Oversized int has guaranteed outcome. 32874 switch (op) { 32875 .eq => return if (!fits) false else null, 32876 .neq => return if (!fits) true else null, 32877 .lt, .lte => if (!fits) return is_negative, 32878 .gt, .gte => if (!fits) return !is_negative, 32879 } 32880 32881 // For any other comparison, we need to know if the LHS value is 32882 // equal to the maximum or minimum possible value of the RHS type. 32883 const is_min, const is_max = edge: { 32884 if (is_zero and rhs_info.signedness == .unsigned) break :edge .{ true, false }; 32885 32886 if (req_bits != rhs_info.bits) break :edge .{ false, false }; 32887 32888 const ty = try mod.intType( 32889 if (is_negative) .signed else .unsigned, 32890 @intCast(req_bits), 32891 ); 32892 const pop_count = lhs_val.popCount(ty, mod); 32893 32894 if (is_negative) { 32895 break :edge .{ pop_count == 1, false }; 32896 } else { 32897 break :edge .{ false, pop_count == req_bits - sign_adj }; 32898 } 32899 }; 32900 32901 assert(fits); 32902 return switch (op) { 32903 .lt => if (is_max) false else null, 32904 .lte => if (is_min) true else null, 32905 .gt => if (is_min) false else null, 32906 .gte => if (is_max) true else null, 32907 .eq, .neq => unreachable, 32908 }; 32909 } 32910 32911 /// Asserts that lhs and rhs types are both vectors. 32912 fn cmpVector( 32913 sema: *Sema, 32914 block: *Block, 32915 src: LazySrcLoc, 32916 lhs: Air.Inst.Ref, 32917 rhs: Air.Inst.Ref, 32918 op: std.math.CompareOperator, 32919 lhs_src: LazySrcLoc, 32920 rhs_src: LazySrcLoc, 32921 ) CompileError!Air.Inst.Ref { 32922 const mod = sema.mod; 32923 const lhs_ty = sema.typeOf(lhs); 32924 const rhs_ty = sema.typeOf(rhs); 32925 assert(lhs_ty.zigTypeTag(mod) == .Vector); 32926 assert(rhs_ty.zigTypeTag(mod) == .Vector); 32927 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 32928 32929 const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } }); 32930 const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src); 32931 const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src); 32932 32933 const result_ty = try mod.vectorType(.{ 32934 .len = lhs_ty.vectorLen(mod), 32935 .child = .bool_type, 32936 }); 32937 32938 const runtime_src: LazySrcLoc = src: { 32939 if (try sema.resolveValue(casted_lhs)) |lhs_val| { 32940 if (try sema.resolveValue(casted_rhs)) |rhs_val| { 32941 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 32942 return mod.undefRef(result_ty); 32943 } 32944 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty); 32945 return Air.internedToRef(cmp_val.toIntern()); 32946 } else { 32947 break :src rhs_src; 32948 } 32949 } else { 32950 break :src lhs_src; 32951 } 32952 }; 32953 32954 try sema.requireRuntimeBlock(block, src, runtime_src); 32955 return block.addCmpVector(casted_lhs, casted_rhs, op); 32956 } 32957 32958 fn wrapOptional( 32959 sema: *Sema, 32960 block: *Block, 32961 dest_ty: Type, 32962 inst: Air.Inst.Ref, 32963 inst_src: LazySrcLoc, 32964 ) !Air.Inst.Ref { 32965 if (try sema.resolveValue(inst)) |val| { 32966 return Air.internedToRef((try sema.mod.intern(.{ .opt = .{ 32967 .ty = dest_ty.toIntern(), 32968 .val = val.toIntern(), 32969 } }))); 32970 } 32971 32972 try sema.requireRuntimeBlock(block, inst_src, null); 32973 return block.addTyOp(.wrap_optional, dest_ty, inst); 32974 } 32975 32976 fn wrapErrorUnionPayload( 32977 sema: *Sema, 32978 block: *Block, 32979 dest_ty: Type, 32980 inst: Air.Inst.Ref, 32981 inst_src: LazySrcLoc, 32982 ) !Air.Inst.Ref { 32983 const mod = sema.mod; 32984 const dest_payload_ty = dest_ty.errorUnionPayload(mod); 32985 const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); 32986 if (try sema.resolveValue(coerced)) |val| { 32987 return Air.internedToRef((try mod.intern(.{ .error_union = .{ 32988 .ty = dest_ty.toIntern(), 32989 .val = .{ .payload = try val.intern(dest_payload_ty, mod) }, 32990 } }))); 32991 } 32992 try sema.requireRuntimeBlock(block, inst_src, null); 32993 try sema.queueFullTypeResolution(dest_payload_ty); 32994 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); 32995 } 32996 32997 fn wrapErrorUnionSet( 32998 sema: *Sema, 32999 block: *Block, 33000 dest_ty: Type, 33001 inst: Air.Inst.Ref, 33002 inst_src: LazySrcLoc, 33003 ) !Air.Inst.Ref { 33004 const mod = sema.mod; 33005 const ip = &mod.intern_pool; 33006 const inst_ty = sema.typeOf(inst); 33007 const dest_err_set_ty = dest_ty.errorUnionSet(mod); 33008 if (try sema.resolveValue(inst)) |val| { 33009 const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; 33010 switch (dest_err_set_ty.toIntern()) { 33011 .anyerror_type => {}, 33012 .adhoc_inferred_error_set_type => ok: { 33013 const ies = sema.fn_ret_ty_ies.?; 33014 switch (ies.resolved) { 33015 .anyerror_type => break :ok, 33016 .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { 33017 break :ok; 33018 }, 33019 else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) { 33020 break :ok; 33021 }, 33022 } 33023 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 33024 }, 33025 else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) { 33026 .error_set_type => |error_set_type| ok: { 33027 if (error_set_type.nameIndex(ip, expected_name) != null) break :ok; 33028 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 33029 }, 33030 .inferred_error_set_type => |func_index| ok: { 33031 // We carefully do this in an order that avoids unnecessarily 33032 // resolving the destination error set type. 33033 switch (ip.funcIesResolved(func_index).*) { 33034 .anyerror_type => break :ok, 33035 .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) { 33036 break :ok; 33037 }, 33038 else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) { 33039 break :ok; 33040 }, 33041 } 33042 33043 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 33044 }, 33045 else => unreachable, 33046 }, 33047 } 33048 return Air.internedToRef((try mod.intern(.{ .error_union = .{ 33049 .ty = dest_ty.toIntern(), 33050 .val = .{ .err_name = expected_name }, 33051 } }))); 33052 } 33053 33054 try sema.requireRuntimeBlock(block, inst_src, null); 33055 const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src); 33056 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); 33057 } 33058 33059 fn unionToTag( 33060 sema: *Sema, 33061 block: *Block, 33062 enum_ty: Type, 33063 un: Air.Inst.Ref, 33064 un_src: LazySrcLoc, 33065 ) !Air.Inst.Ref { 33066 const mod = sema.mod; 33067 if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| { 33068 return Air.internedToRef(opv.toIntern()); 33069 } 33070 if (try sema.resolveValue(un)) |un_val| { 33071 return Air.internedToRef(un_val.unionTag(mod).?.toIntern()); 33072 } 33073 try sema.requireRuntimeBlock(block, un_src, null); 33074 return block.addTyOp(.get_union_tag, enum_ty, un); 33075 } 33076 33077 const PeerResolveStrategy = enum { 33078 /// The type is not known. 33079 /// If refined no further, this is equivalent to `exact`. 33080 unknown, 33081 /// The type may be an error set or error union. 33082 /// If refined no further, it is an error set. 33083 error_set, 33084 /// The type must be some error union. 33085 error_union, 33086 /// The type may be @TypeOf(null), an optional or a C pointer. 33087 /// If refined no further, it is @TypeOf(null). 33088 nullable, 33089 /// The type must be some optional or a C pointer. 33090 /// If refined no further, it is an optional. 33091 optional, 33092 /// The type must be either an array or a vector. 33093 /// If refined no further, it is an array. 33094 array, 33095 /// The type must be a vector. 33096 vector, 33097 /// The type must be a C pointer. 33098 c_ptr, 33099 /// The type must be a pointer (C or not). 33100 /// If refined no further, it is a non-C pointer. 33101 ptr, 33102 /// The type must be a function or a pointer to a function. 33103 /// If refined no further, it is a function. 33104 func, 33105 /// The type must be an enum literal, or some specific enum or union. Which one is decided 33106 /// afterwards based on the types in question. 33107 enum_or_union, 33108 /// The type must be some integer or float type. 33109 /// If refined no further, it is `comptime_int`. 33110 comptime_int, 33111 /// The type must be some float type. 33112 /// If refined no further, it is `comptime_float`. 33113 comptime_float, 33114 /// The type must be some float or fixed-width integer type. 33115 /// If refined no further, it is some fixed-width integer type. 33116 fixed_int, 33117 /// The type must be some fixed-width float type. 33118 fixed_float, 33119 /// The type must be a struct literal or tuple type. 33120 coercible_struct, 33121 /// The peers must all be of the same type. 33122 exact, 33123 33124 /// Given two strategies, find a strategy that satisfies both, if one exists. If no such 33125 /// strategy exists, any strategy may be returned; an error will be emitted when the caller 33126 /// attempts to use the strategy to resolve the type. 33127 /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at 33128 /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy. 33129 fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy { 33130 // Our merging should be order-independent. Thus, even though the union order is arbitrary, 33131 // by sorting the tags and switching first on the smaller, we have half as many cases to 33132 // worry about (since we avoid the duplicates). 33133 const s0_is_a = @intFromEnum(a) <= @intFromEnum(b); 33134 const s0 = if (s0_is_a) a else b; 33135 const s1 = if (s0_is_a) b else a; 33136 33137 const ReasonMethod = enum { 33138 all_s0, 33139 all_s1, 33140 either, 33141 }; 33142 33143 const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) { 33144 .unknown => .{ .all_s1, s1 }, 33145 .error_set => switch (s1) { 33146 .error_set => .{ .either, .error_set }, 33147 else => .{ .all_s0, .error_union }, 33148 }, 33149 .error_union => switch (s1) { 33150 .error_union => .{ .either, .error_union }, 33151 else => .{ .all_s0, .error_union }, 33152 }, 33153 .nullable => switch (s1) { 33154 .nullable => .{ .either, .nullable }, 33155 .c_ptr => .{ .all_s1, .c_ptr }, 33156 else => .{ .all_s0, .optional }, 33157 }, 33158 .optional => switch (s1) { 33159 .optional => .{ .either, .optional }, 33160 .c_ptr => .{ .all_s1, .c_ptr }, 33161 else => .{ .all_s0, .optional }, 33162 }, 33163 .array => switch (s1) { 33164 .array => .{ .either, .array }, 33165 .vector => .{ .all_s1, .vector }, 33166 else => .{ .all_s0, .array }, 33167 }, 33168 .vector => switch (s1) { 33169 .vector => .{ .either, .vector }, 33170 else => .{ .all_s0, .vector }, 33171 }, 33172 .c_ptr => switch (s1) { 33173 .c_ptr => .{ .either, .c_ptr }, 33174 else => .{ .all_s0, .c_ptr }, 33175 }, 33176 .ptr => switch (s1) { 33177 .ptr => .{ .either, .ptr }, 33178 else => .{ .all_s0, .ptr }, 33179 }, 33180 .func => switch (s1) { 33181 .func => .{ .either, .func }, 33182 else => .{ .all_s1, s1 }, // doesn't override anything later 33183 }, 33184 .enum_or_union => switch (s1) { 33185 .enum_or_union => .{ .either, .enum_or_union }, 33186 else => .{ .all_s0, .enum_or_union }, 33187 }, 33188 .comptime_int => switch (s1) { 33189 .comptime_int => .{ .either, .comptime_int }, 33190 else => .{ .all_s1, s1 }, // doesn't override anything later 33191 }, 33192 .comptime_float => switch (s1) { 33193 .comptime_float => .{ .either, .comptime_float }, 33194 else => .{ .all_s1, s1 }, // doesn't override anything later 33195 }, 33196 .fixed_int => switch (s1) { 33197 .fixed_int => .{ .either, .fixed_int }, 33198 else => .{ .all_s1, s1 }, // doesn't override anything later 33199 }, 33200 .fixed_float => switch (s1) { 33201 .fixed_float => .{ .either, .fixed_float }, 33202 else => .{ .all_s1, s1 }, // doesn't override anything later 33203 }, 33204 .coercible_struct => switch (s1) { 33205 .exact => .{ .all_s1, .exact }, 33206 else => .{ .all_s0, .coercible_struct }, 33207 }, 33208 .exact => .{ .all_s0, .exact }, 33209 }; 33210 33211 switch (reason_method) { 33212 .all_s0 => { 33213 if (!s0_is_a) { 33214 reason_peer.* = b_peer_idx; 33215 } 33216 }, 33217 .all_s1 => { 33218 if (s0_is_a) { 33219 reason_peer.* = b_peer_idx; 33220 } 33221 }, 33222 .either => { 33223 // Prefer the earliest peer 33224 reason_peer.* = @min(reason_peer.*, b_peer_idx); 33225 }, 33226 } 33227 33228 return strat; 33229 } 33230 33231 fn select(ty: Type, mod: *Module) PeerResolveStrategy { 33232 return switch (ty.zigTypeTag(mod)) { 33233 .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact, 33234 .NoReturn, .Undefined => .unknown, 33235 .Null => .nullable, 33236 .ComptimeInt => .comptime_int, 33237 .Int => .fixed_int, 33238 .ComptimeFloat => .comptime_float, 33239 .Float => .fixed_float, 33240 .Pointer => if (ty.ptrInfo(mod).flags.size == .C) .c_ptr else .ptr, 33241 .Array => .array, 33242 .Vector => .vector, 33243 .Optional => .optional, 33244 .ErrorSet => .error_set, 33245 .ErrorUnion => .error_union, 33246 .EnumLiteral, .Enum, .Union => .enum_or_union, 33247 .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact, 33248 .Fn => .func, 33249 }; 33250 } 33251 }; 33252 33253 const PeerResolveResult = union(enum) { 33254 /// The peer type resolution was successful, and resulted in the given type. 33255 success: Type, 33256 /// There was some generic conflict between two peers. 33257 conflict: struct { 33258 peer_idx_a: usize, 33259 peer_idx_b: usize, 33260 }, 33261 /// There was an error when resolving the type of a struct or tuple field. 33262 field_error: struct { 33263 /// The name of the field which caused the failure. 33264 field_name: []const u8, 33265 /// The type of this field in each peer. 33266 field_types: []Type, 33267 /// The error from resolving the field type. Guaranteed not to be `success`. 33268 sub_result: *PeerResolveResult, 33269 }, 33270 33271 fn report( 33272 result: PeerResolveResult, 33273 sema: *Sema, 33274 block: *Block, 33275 src: LazySrcLoc, 33276 instructions: []const Air.Inst.Ref, 33277 candidate_srcs: Module.PeerTypeCandidateSrc, 33278 ) !*Module.ErrorMsg { 33279 const mod = sema.mod; 33280 const decl_ptr = mod.declPtr(block.src_decl); 33281 33282 var opt_msg: ?*Module.ErrorMsg = null; 33283 errdefer if (opt_msg) |msg| msg.destroy(sema.gpa); 33284 33285 // If we mention fields we'll want to include field types, so put peer types in a buffer 33286 var peer_tys = try sema.arena.alloc(Type, instructions.len); 33287 for (peer_tys, instructions) |*ty, inst| { 33288 ty.* = sema.typeOf(inst); 33289 } 33290 33291 var cur = result; 33292 while (true) { 33293 var conflict_idx: [2]usize = undefined; 33294 33295 switch (cur) { 33296 .success => unreachable, 33297 .conflict => |conflict| { 33298 // Fall through to two-peer conflict handling below 33299 conflict_idx = .{ 33300 conflict.peer_idx_a, 33301 conflict.peer_idx_b, 33302 }; 33303 }, 33304 .field_error => |field_error| { 33305 const fmt = "struct field '{s}' has conflicting types"; 33306 const args = .{field_error.field_name}; 33307 if (opt_msg) |msg| { 33308 try sema.errNote(block, src, msg, fmt, args); 33309 } else { 33310 opt_msg = try sema.errMsg(block, src, fmt, args); 33311 } 33312 33313 // Continue on to child error 33314 cur = field_error.sub_result.*; 33315 peer_tys = field_error.field_types; 33316 continue; 33317 }, 33318 } 33319 33320 // This is the path for reporting a generic conflict between two peers. 33321 33322 if (conflict_idx[1] < conflict_idx[0]) { 33323 // b comes first in source, so it's better if it comes first in the error 33324 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]); 33325 } 33326 33327 const conflict_tys: [2]Type = .{ 33328 peer_tys[conflict_idx[0]], 33329 peer_tys[conflict_idx[1]], 33330 }; 33331 const conflict_srcs: [2]?LazySrcLoc = .{ 33332 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]), 33333 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]), 33334 }; 33335 33336 const fmt = "incompatible types: '{}' and '{}'"; 33337 const args = .{ 33338 conflict_tys[0].fmt(mod), 33339 conflict_tys[1].fmt(mod), 33340 }; 33341 const msg = if (opt_msg) |msg| msg: { 33342 try sema.errNote(block, src, msg, fmt, args); 33343 break :msg msg; 33344 } else msg: { 33345 const msg = try sema.errMsg(block, src, fmt, args); 33346 opt_msg = msg; 33347 break :msg msg; 33348 }; 33349 33350 if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)}); 33351 if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)}); 33352 33353 // No child error 33354 break; 33355 } 33356 33357 return opt_msg.?; 33358 } 33359 }; 33360 33361 fn resolvePeerTypes( 33362 sema: *Sema, 33363 block: *Block, 33364 src: LazySrcLoc, 33365 instructions: []const Air.Inst.Ref, 33366 candidate_srcs: Module.PeerTypeCandidateSrc, 33367 ) !Type { 33368 switch (instructions.len) { 33369 0 => return Type.noreturn, 33370 1 => return sema.typeOf(instructions[0]), 33371 else => {}, 33372 } 33373 33374 const peer_tys = try sema.arena.alloc(?Type, instructions.len); 33375 const peer_vals = try sema.arena.alloc(?Value, instructions.len); 33376 33377 for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { 33378 ty.* = sema.typeOf(inst); 33379 val.* = try sema.resolveValue(inst); 33380 } 33381 33382 switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { 33383 .success => |ty| return ty, 33384 else => |result| { 33385 const msg = try result.report(sema, block, src, instructions, candidate_srcs); 33386 return sema.failWithOwnedErrorMsg(block, msg); 33387 }, 33388 } 33389 } 33390 33391 fn resolvePeerTypesInner( 33392 sema: *Sema, 33393 block: *Block, 33394 src: LazySrcLoc, 33395 peer_tys: []?Type, 33396 peer_vals: []?Value, 33397 ) !PeerResolveResult { 33398 const mod = sema.mod; 33399 const ip = &mod.intern_pool; 33400 33401 var strat_reason: usize = 0; 33402 var s: PeerResolveStrategy = .unknown; 33403 for (peer_tys, 0..) |opt_ty, i| { 33404 const ty = opt_ty orelse continue; 33405 s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i); 33406 } 33407 33408 if (s == .unknown) { 33409 // The whole thing was noreturn or undefined - try to do an exact match 33410 s = .exact; 33411 } else { 33412 // There was something other than noreturn and undefined, so we can ignore those peers 33413 for (peer_tys) |*ty_ptr| { 33414 const ty = ty_ptr.* orelse continue; 33415 switch (ty.zigTypeTag(mod)) { 33416 .NoReturn, .Undefined => ty_ptr.* = null, 33417 else => {}, 33418 } 33419 } 33420 } 33421 33422 const target = mod.getTarget(); 33423 33424 switch (s) { 33425 .unknown => unreachable, 33426 33427 .error_set => { 33428 var final_set: ?Type = null; 33429 for (peer_tys, 0..) |opt_ty, i| { 33430 const ty = opt_ty orelse continue; 33431 if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{ 33432 .peer_idx_a = strat_reason, 33433 .peer_idx_b = i, 33434 } }; 33435 if (final_set) |cur_set| { 33436 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty); 33437 } else { 33438 final_set = ty; 33439 } 33440 } 33441 return .{ .success = final_set.? }; 33442 }, 33443 33444 .error_union => { 33445 var final_set: ?Type = null; 33446 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 33447 const ty = ty_ptr.* orelse continue; 33448 const set_ty = switch (ty.zigTypeTag(mod)) { 33449 .ErrorSet => blk: { 33450 ty_ptr.* = null; // no payload to decide on 33451 val_ptr.* = null; 33452 break :blk ty; 33453 }, 33454 .ErrorUnion => blk: { 33455 const set_ty = ty.errorUnionSet(mod); 33456 ty_ptr.* = ty.errorUnionPayload(mod); 33457 if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) { 33458 .error_union => |eu| switch (eu.val) { 33459 .payload => |payload_ip| val_ptr.* = Value.fromInterned(payload_ip), 33460 .err_name => val_ptr.* = null, 33461 }, 33462 .undef => val_ptr.* = Value.fromInterned((try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() }))), 33463 else => unreachable, 33464 }; 33465 break :blk set_ty; 33466 }, 33467 else => continue, // whole type is the payload 33468 }; 33469 if (final_set) |cur_set| { 33470 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty); 33471 } else { 33472 final_set = set_ty; 33473 } 33474 } 33475 assert(final_set != null); 33476 const final_payload = switch (try sema.resolvePeerTypesInner( 33477 block, 33478 src, 33479 peer_tys, 33480 peer_vals, 33481 )) { 33482 .success => |ty| ty, 33483 else => |result| return result, 33484 }; 33485 return .{ .success = try mod.errorUnionType(final_set.?, final_payload) }; 33486 }, 33487 33488 .nullable => { 33489 for (peer_tys, 0..) |opt_ty, i| { 33490 const ty = opt_ty orelse continue; 33491 if (!ty.eql(Type.null, mod)) return .{ .conflict = .{ 33492 .peer_idx_a = strat_reason, 33493 .peer_idx_b = i, 33494 } }; 33495 } 33496 return .{ .success = Type.null }; 33497 }, 33498 33499 .optional => { 33500 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 33501 const ty = ty_ptr.* orelse continue; 33502 switch (ty.zigTypeTag(mod)) { 33503 .Null => { 33504 ty_ptr.* = null; 33505 val_ptr.* = null; 33506 }, 33507 .Optional => { 33508 ty_ptr.* = ty.optionalChild(mod); 33509 if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null; 33510 }, 33511 else => {}, 33512 } 33513 } 33514 const child_ty = switch (try sema.resolvePeerTypesInner( 33515 block, 33516 src, 33517 peer_tys, 33518 peer_vals, 33519 )) { 33520 .success => |ty| ty, 33521 else => |result| return result, 33522 }; 33523 return .{ .success = try mod.optionalType(child_ty.toIntern()) }; 33524 }, 33525 33526 .array => { 33527 // Index of the first non-null peer 33528 var opt_first_idx: ?usize = null; 33529 // Index of the first array or vector peer (i.e. not a tuple) 33530 var opt_first_arr_idx: ?usize = null; 33531 // Set to non-null once we see any peer, even a tuple 33532 var len: u64 = undefined; 33533 var sentinel: ?Value = undefined; 33534 // Only set once we see a non-tuple peer 33535 var elem_ty: Type = undefined; 33536 33537 for (peer_tys, 0..) |*ty_ptr, i| { 33538 const ty = ty_ptr.* orelse continue; 33539 33540 if (!ty.isArrayOrVector(mod)) { 33541 // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced. 33542 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 33543 .peer_idx_a = strat_reason, 33544 .peer_idx_b = i, 33545 } }; 33546 33547 if (opt_first_idx) |first_idx| { 33548 if (arr_like.len != len) return .{ .conflict = .{ 33549 .peer_idx_a = first_idx, 33550 .peer_idx_b = i, 33551 } }; 33552 } else { 33553 opt_first_idx = i; 33554 len = arr_like.len; 33555 } 33556 33557 sentinel = null; 33558 33559 continue; 33560 } 33561 33562 const first_arr_idx = opt_first_arr_idx orelse { 33563 if (opt_first_idx == null) { 33564 opt_first_idx = i; 33565 len = ty.arrayLen(mod); 33566 sentinel = ty.sentinel(mod); 33567 } 33568 opt_first_arr_idx = i; 33569 elem_ty = ty.childType(mod); 33570 continue; 33571 }; 33572 33573 if (ty.arrayLen(mod) != len) return .{ .conflict = .{ 33574 .peer_idx_a = first_arr_idx, 33575 .peer_idx_b = i, 33576 } }; 33577 33578 if (!ty.childType(mod).eql(elem_ty, mod)) { 33579 return .{ .conflict = .{ 33580 .peer_idx_a = first_arr_idx, 33581 .peer_idx_b = i, 33582 } }; 33583 } 33584 33585 if (sentinel) |cur_sent| { 33586 if (ty.sentinel(mod)) |peer_sent| { 33587 if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null; 33588 } else { 33589 sentinel = null; 33590 } 33591 } 33592 } 33593 33594 // There should always be at least one array or vector peer 33595 assert(opt_first_arr_idx != null); 33596 33597 return .{ .success = try mod.arrayType(.{ 33598 .len = len, 33599 .child = elem_ty.toIntern(), 33600 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none, 33601 }) }; 33602 }, 33603 33604 .vector => { 33605 var len: ?u64 = null; 33606 var first_idx: usize = undefined; 33607 for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| { 33608 const ty = ty_ptr.* orelse continue; 33609 33610 if (!ty.isArrayOrVector(mod)) { 33611 // Allow tuples of the correct length 33612 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 33613 .peer_idx_a = strat_reason, 33614 .peer_idx_b = i, 33615 } }; 33616 33617 if (len) |expect_len| { 33618 if (arr_like.len != expect_len) return .{ .conflict = .{ 33619 .peer_idx_a = first_idx, 33620 .peer_idx_b = i, 33621 } }; 33622 } else { 33623 len = arr_like.len; 33624 first_idx = i; 33625 } 33626 33627 // Tuples won't participate in the child type resolution. We'll resolve without 33628 // them, and if the tuples have a bad type, we'll get a coercion error later. 33629 ty_ptr.* = null; 33630 val_ptr.* = null; 33631 33632 continue; 33633 } 33634 33635 if (len) |expect_len| { 33636 if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{ 33637 .peer_idx_a = first_idx, 33638 .peer_idx_b = i, 33639 } }; 33640 } else { 33641 len = ty.arrayLen(mod); 33642 first_idx = i; 33643 } 33644 33645 ty_ptr.* = ty.childType(mod); 33646 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR 33647 } 33648 33649 const child_ty = switch (try sema.resolvePeerTypesInner( 33650 block, 33651 src, 33652 peer_tys, 33653 peer_vals, 33654 )) { 33655 .success => |ty| ty, 33656 else => |result| return result, 33657 }; 33658 33659 return .{ .success = try mod.vectorType(.{ 33660 .len = @intCast(len.?), 33661 .child = child_ty.toIntern(), 33662 }) }; 33663 }, 33664 33665 .c_ptr => { 33666 var opt_ptr_info: ?InternPool.Key.PtrType = null; 33667 var first_idx: usize = undefined; 33668 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 33669 const ty = opt_ty orelse continue; 33670 switch (ty.zigTypeTag(mod)) { 33671 .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers 33672 .Int => { 33673 if (opt_val != null) { 33674 // Always allow the coercion for comptime-known ints 33675 continue; 33676 } else { 33677 // Runtime-known, so check if the type is no bigger than a usize 33678 const ptr_bits = target.ptrBitWidth(); 33679 const bits = ty.intInfo(mod).bits; 33680 if (bits <= ptr_bits) continue; 33681 } 33682 }, 33683 .Null => continue, 33684 else => {}, 33685 } 33686 33687 if (!ty.isPtrAtRuntime(mod)) return .{ .conflict = .{ 33688 .peer_idx_a = strat_reason, 33689 .peer_idx_b = i, 33690 } }; 33691 33692 // Goes through optionals 33693 const peer_info = ty.ptrInfo(mod); 33694 33695 var ptr_info = opt_ptr_info orelse { 33696 opt_ptr_info = peer_info; 33697 opt_ptr_info.?.flags.size = .C; 33698 first_idx = i; 33699 continue; 33700 }; 33701 33702 // Try peer -> cur, then cur -> peer 33703 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) orelse { 33704 return .{ .conflict = .{ 33705 .peer_idx_a = first_idx, 33706 .peer_idx_b = i, 33707 } }; 33708 }).toIntern(); 33709 33710 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) { 33711 const peer_sent = try ip.getCoerced(sema.gpa, ptr_info.sentinel, ptr_info.child); 33712 const ptr_sent = try ip.getCoerced(sema.gpa, peer_info.sentinel, ptr_info.child); 33713 if (ptr_sent == peer_sent) { 33714 ptr_info.sentinel = ptr_sent; 33715 } else { 33716 ptr_info.sentinel = .none; 33717 } 33718 } else { 33719 ptr_info.sentinel = .none; 33720 } 33721 33722 // Note that the align can be always non-zero; Module.ptrType will canonicalize it 33723 ptr_info.flags.alignment = InternPool.Alignment.min( 33724 if (ptr_info.flags.alignment != .none) 33725 ptr_info.flags.alignment 33726 else 33727 Type.fromInterned(ptr_info.child).abiAlignment(mod), 33728 33729 if (peer_info.flags.alignment != .none) 33730 peer_info.flags.alignment 33731 else 33732 Type.fromInterned(peer_info.child).abiAlignment(mod), 33733 ); 33734 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 33735 return .{ .conflict = .{ 33736 .peer_idx_a = first_idx, 33737 .peer_idx_b = i, 33738 } }; 33739 } 33740 33741 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 33742 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 33743 { 33744 return .{ .conflict = .{ 33745 .peer_idx_a = first_idx, 33746 .peer_idx_b = i, 33747 } }; 33748 } 33749 33750 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 33751 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 33752 33753 opt_ptr_info = ptr_info; 33754 } 33755 return .{ .success = try sema.ptrType(opt_ptr_info.?) }; 33756 }, 33757 33758 .ptr => { 33759 // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only 33760 // if there were no actual slices. Else, we want the slice index to report a conflict. 33761 var opt_slice_idx: ?usize = null; 33762 33763 var opt_ptr_info: ?InternPool.Key.PtrType = null; 33764 var first_idx: usize = undefined; 33765 var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error 33766 33767 for (peer_tys, 0..) |opt_ty, i| { 33768 const ty = opt_ty orelse continue; 33769 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(mod)) { 33770 .Pointer => ty.ptrInfo(mod), 33771 .Fn => .{ 33772 .child = ty.toIntern(), 33773 .flags = .{ 33774 .address_space = target_util.defaultAddressSpace(target, .global_constant), 33775 }, 33776 }, 33777 else => return .{ .conflict = .{ 33778 .peer_idx_a = strat_reason, 33779 .peer_idx_b = i, 33780 } }, 33781 }; 33782 33783 switch (peer_info.flags.size) { 33784 .One, .Many => {}, 33785 .Slice => opt_slice_idx = i, 33786 .C => return .{ .conflict = .{ 33787 .peer_idx_a = strat_reason, 33788 .peer_idx_b = i, 33789 } }, 33790 } 33791 33792 var ptr_info = opt_ptr_info orelse { 33793 opt_ptr_info = peer_info; 33794 first_idx = i; 33795 continue; 33796 }; 33797 33798 other_idx = i; 33799 33800 // We want to return this in a lot of cases, so alias it here for convenience 33801 const generic_err: PeerResolveResult = .{ .conflict = .{ 33802 .peer_idx_a = first_idx, 33803 .peer_idx_b = i, 33804 } }; 33805 33806 // Note that the align can be always non-zero; Type.ptr will canonicalize it 33807 ptr_info.flags.alignment = Alignment.min( 33808 if (ptr_info.flags.alignment != .none) 33809 ptr_info.flags.alignment 33810 else 33811 try sema.typeAbiAlignment(Type.fromInterned(ptr_info.child)), 33812 33813 if (peer_info.flags.alignment != .none) 33814 peer_info.flags.alignment 33815 else 33816 try sema.typeAbiAlignment(Type.fromInterned(peer_info.child)), 33817 ); 33818 33819 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 33820 return generic_err; 33821 } 33822 33823 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 33824 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 33825 { 33826 return generic_err; 33827 } 33828 33829 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 33830 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 33831 33832 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) { 33833 .One => switch (ip.indexToKey(peer_info.child)) { 33834 .array_type => |array_type| array_type.sentinel, 33835 else => .none, 33836 }, 33837 .Many, .Slice => peer_info.sentinel, 33838 .C => unreachable, 33839 }; 33840 33841 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) { 33842 .One => switch (ip.indexToKey(ptr_info.child)) { 33843 .array_type => |array_type| array_type.sentinel, 33844 else => .none, 33845 }, 33846 .Many, .Slice => ptr_info.sentinel, 33847 .C => unreachable, 33848 }; 33849 33850 // We abstract array handling slightly so that tuple pointers can work like array pointers 33851 const peer_pointee_array = sema.typeIsArrayLike(Type.fromInterned(peer_info.child)); 33852 const cur_pointee_array = sema.typeIsArrayLike(Type.fromInterned(ptr_info.child)); 33853 33854 // This switch is just responsible for deciding the size and pointee (not including 33855 // single-pointer array sentinel). 33856 good: { 33857 switch (peer_info.flags.size) { 33858 .One => switch (ptr_info.flags.size) { 33859 .One => { 33860 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) |pointee| { 33861 ptr_info.child = pointee.toIntern(); 33862 break :good; 33863 } 33864 33865 const cur_arr = cur_pointee_array orelse return generic_err; 33866 const peer_arr = peer_pointee_array orelse return generic_err; 33867 33868 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| { 33869 // *[n:x]T + *[n:y]T = *[n]T 33870 if (cur_arr.len == peer_arr.len) { 33871 ptr_info.child = (try mod.arrayType(.{ 33872 .len = cur_arr.len, 33873 .child = elem_ty.toIntern(), 33874 })).toIntern(); 33875 break :good; 33876 } 33877 // *[a]T + *[b]T = []T 33878 ptr_info.flags.size = .Slice; 33879 ptr_info.child = elem_ty.toIntern(); 33880 break :good; 33881 } 33882 33883 if (peer_arr.elem_ty.toIntern() == .noreturn_type) { 33884 // *struct{} + *[a]T = []T 33885 ptr_info.flags.size = .Slice; 33886 ptr_info.child = cur_arr.elem_ty.toIntern(); 33887 break :good; 33888 } 33889 33890 if (cur_arr.elem_ty.toIntern() == .noreturn_type) { 33891 // *[a]T + *struct{} = []T 33892 ptr_info.flags.size = .Slice; 33893 ptr_info.child = peer_arr.elem_ty.toIntern(); 33894 break :good; 33895 } 33896 33897 return generic_err; 33898 }, 33899 .Many => { 33900 // Only works for *[n]T + [*]T -> [*]T 33901 const arr = peer_pointee_array orelse return generic_err; 33902 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), arr.elem_ty)) |pointee| { 33903 ptr_info.child = pointee.toIntern(); 33904 break :good; 33905 } 33906 if (arr.elem_ty.toIntern() == .noreturn_type) { 33907 // *struct{} + [*]T -> [*]T 33908 break :good; 33909 } 33910 return generic_err; 33911 }, 33912 .Slice => { 33913 // Only works for *[n]T + []T -> []T 33914 const arr = peer_pointee_array orelse return generic_err; 33915 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), arr.elem_ty)) |pointee| { 33916 ptr_info.child = pointee.toIntern(); 33917 break :good; 33918 } 33919 if (arr.elem_ty.toIntern() == .noreturn_type) { 33920 // *struct{} + []T -> []T 33921 break :good; 33922 } 33923 return generic_err; 33924 }, 33925 .C => unreachable, 33926 }, 33927 .Many => switch (ptr_info.flags.size) { 33928 .One => { 33929 // Only works for [*]T + *[n]T -> [*]T 33930 const arr = cur_pointee_array orelse return generic_err; 33931 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, Type.fromInterned(peer_info.child))) |pointee| { 33932 ptr_info.flags.size = .Many; 33933 ptr_info.child = pointee.toIntern(); 33934 break :good; 33935 } 33936 if (arr.elem_ty.toIntern() == .noreturn_type) { 33937 // [*]T + *struct{} -> [*]T 33938 ptr_info.flags.size = .Many; 33939 ptr_info.child = peer_info.child; 33940 break :good; 33941 } 33942 return generic_err; 33943 }, 33944 .Many => { 33945 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) |pointee| { 33946 ptr_info.child = pointee.toIntern(); 33947 break :good; 33948 } 33949 return generic_err; 33950 }, 33951 .Slice => { 33952 // Only works if no peers are actually slices 33953 if (opt_slice_idx) |slice_idx| { 33954 return .{ .conflict = .{ 33955 .peer_idx_a = slice_idx, 33956 .peer_idx_b = i, 33957 } }; 33958 } 33959 // Okay, then works for [*]T + "[]T" -> [*]T 33960 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) |pointee| { 33961 ptr_info.flags.size = .Many; 33962 ptr_info.child = pointee.toIntern(); 33963 break :good; 33964 } 33965 return generic_err; 33966 }, 33967 .C => unreachable, 33968 }, 33969 .Slice => switch (ptr_info.flags.size) { 33970 .One => { 33971 // Only works for []T + *[n]T -> []T 33972 const arr = cur_pointee_array orelse return generic_err; 33973 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, Type.fromInterned(peer_info.child))) |pointee| { 33974 ptr_info.flags.size = .Slice; 33975 ptr_info.child = pointee.toIntern(); 33976 break :good; 33977 } 33978 if (arr.elem_ty.toIntern() == .noreturn_type) { 33979 // []T + *struct{} -> []T 33980 ptr_info.flags.size = .Slice; 33981 ptr_info.child = peer_info.child; 33982 break :good; 33983 } 33984 return generic_err; 33985 }, 33986 .Many => { 33987 // Impossible! (current peer is an actual slice) 33988 return generic_err; 33989 }, 33990 .Slice => { 33991 if (try sema.resolvePairInMemoryCoercible(block, src, Type.fromInterned(ptr_info.child), Type.fromInterned(peer_info.child))) |pointee| { 33992 ptr_info.child = pointee.toIntern(); 33993 break :good; 33994 } 33995 return generic_err; 33996 }, 33997 .C => unreachable, 33998 }, 33999 .C => unreachable, 34000 } 34001 } 34002 34003 const sentinel_ty = switch (ptr_info.flags.size) { 34004 .One => switch (ip.indexToKey(ptr_info.child)) { 34005 .array_type => |array_type| array_type.child, 34006 else => ptr_info.child, 34007 }, 34008 .Many, .Slice, .C => ptr_info.child, 34009 }; 34010 34011 sentinel: { 34012 no_sentinel: { 34013 if (peer_sentinel == .none) break :no_sentinel; 34014 if (cur_sentinel == .none) break :no_sentinel; 34015 const peer_sent_coerced = try ip.getCoerced(sema.gpa, peer_sentinel, sentinel_ty); 34016 const cur_sent_coerced = try ip.getCoerced(sema.gpa, cur_sentinel, sentinel_ty); 34017 if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel; 34018 // Sentinels match 34019 if (ptr_info.flags.size == .One) switch (ip.indexToKey(ptr_info.child)) { 34020 .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{ 34021 .len = array_type.len, 34022 .child = array_type.child, 34023 .sentinel = cur_sent_coerced, 34024 })).toIntern(), 34025 else => unreachable, 34026 } else { 34027 ptr_info.sentinel = cur_sent_coerced; 34028 } 34029 break :sentinel; 34030 } 34031 // Clear existing sentinel 34032 ptr_info.sentinel = .none; 34033 switch (ip.indexToKey(ptr_info.child)) { 34034 .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{ 34035 .len = array_type.len, 34036 .child = array_type.child, 34037 .sentinel = .none, 34038 })).toIntern(), 34039 else => {}, 34040 } 34041 } 34042 34043 opt_ptr_info = ptr_info; 34044 } 34045 34046 // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance) 34047 // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to 34048 // coerce the empty struct to a specific type, but no peer provided one. We need to 34049 // detect this case and emit an error. 34050 const pointee = opt_ptr_info.?.child; 34051 switch (pointee) { 34052 .noreturn_type => return .{ .conflict = .{ 34053 .peer_idx_a = first_idx, 34054 .peer_idx_b = other_idx, 34055 } }, 34056 else => switch (ip.indexToKey(pointee)) { 34057 .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{ 34058 .peer_idx_a = first_idx, 34059 .peer_idx_b = other_idx, 34060 } }, 34061 else => {}, 34062 }, 34063 } 34064 34065 return .{ .success = try sema.ptrType(opt_ptr_info.?) }; 34066 }, 34067 34068 .func => { 34069 var opt_cur_ty: ?Type = null; 34070 var first_idx: usize = undefined; 34071 for (peer_tys, 0..) |opt_ty, i| { 34072 const ty = opt_ty orelse continue; 34073 const cur_ty = opt_cur_ty orelse { 34074 opt_cur_ty = ty; 34075 first_idx = i; 34076 continue; 34077 }; 34078 if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{ 34079 .peer_idx_a = strat_reason, 34080 .peer_idx_b = i, 34081 } }; 34082 // ty -> cur_ty 34083 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) { 34084 continue; 34085 } 34086 // cur_ty -> ty 34087 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) { 34088 opt_cur_ty = ty; 34089 continue; 34090 } 34091 return .{ .conflict = .{ 34092 .peer_idx_a = first_idx, 34093 .peer_idx_b = i, 34094 } }; 34095 } 34096 return .{ .success = opt_cur_ty.? }; 34097 }, 34098 34099 .enum_or_union => { 34100 var opt_cur_ty: ?Type = null; 34101 // The peer index which gave the current type 34102 var cur_ty_idx: usize = undefined; 34103 34104 for (peer_tys, 0..) |opt_ty, i| { 34105 const ty = opt_ty orelse continue; 34106 switch (ty.zigTypeTag(mod)) { 34107 .EnumLiteral, .Enum, .Union => {}, 34108 else => return .{ .conflict = .{ 34109 .peer_idx_a = strat_reason, 34110 .peer_idx_b = i, 34111 } }, 34112 } 34113 const cur_ty = opt_cur_ty orelse { 34114 opt_cur_ty = ty; 34115 cur_ty_idx = i; 34116 continue; 34117 }; 34118 34119 // We want to return this in a lot of cases, so alias it here for convenience 34120 const generic_err: PeerResolveResult = .{ .conflict = .{ 34121 .peer_idx_a = cur_ty_idx, 34122 .peer_idx_b = i, 34123 } }; 34124 34125 switch (cur_ty.zigTypeTag(mod)) { 34126 .EnumLiteral => { 34127 opt_cur_ty = ty; 34128 cur_ty_idx = i; 34129 }, 34130 .Enum => switch (ty.zigTypeTag(mod)) { 34131 .EnumLiteral => {}, 34132 .Enum => { 34133 if (!ty.eql(cur_ty, mod)) return generic_err; 34134 }, 34135 .Union => { 34136 const tag_ty = ty.unionTagTypeHypothetical(mod); 34137 if (!tag_ty.eql(cur_ty, mod)) return generic_err; 34138 opt_cur_ty = ty; 34139 cur_ty_idx = i; 34140 }, 34141 else => unreachable, 34142 }, 34143 .Union => switch (ty.zigTypeTag(mod)) { 34144 .EnumLiteral => {}, 34145 .Enum => { 34146 const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod); 34147 if (!ty.eql(cur_tag_ty, mod)) return generic_err; 34148 }, 34149 .Union => { 34150 if (!ty.eql(cur_ty, mod)) return generic_err; 34151 }, 34152 else => unreachable, 34153 }, 34154 else => unreachable, 34155 } 34156 } 34157 return .{ .success = opt_cur_ty.? }; 34158 }, 34159 34160 .comptime_int => { 34161 for (peer_tys, 0..) |opt_ty, i| { 34162 const ty = opt_ty orelse continue; 34163 switch (ty.zigTypeTag(mod)) { 34164 .ComptimeInt => {}, 34165 else => return .{ .conflict = .{ 34166 .peer_idx_a = strat_reason, 34167 .peer_idx_b = i, 34168 } }, 34169 } 34170 } 34171 return .{ .success = Type.comptime_int }; 34172 }, 34173 34174 .comptime_float => { 34175 for (peer_tys, 0..) |opt_ty, i| { 34176 const ty = opt_ty orelse continue; 34177 switch (ty.zigTypeTag(mod)) { 34178 .ComptimeInt, .ComptimeFloat => {}, 34179 else => return .{ .conflict = .{ 34180 .peer_idx_a = strat_reason, 34181 .peer_idx_b = i, 34182 } }, 34183 } 34184 } 34185 return .{ .success = Type.comptime_float }; 34186 }, 34187 34188 .fixed_int => { 34189 var idx_unsigned: ?usize = null; 34190 var idx_signed: ?usize = null; 34191 34192 // TODO: this is for compatibility with legacy behavior. See beneath the loop. 34193 var any_comptime_known = false; 34194 34195 for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| { 34196 const ty = opt_ty orelse continue; 34197 const opt_val = ptr_opt_val.*; 34198 34199 const peer_tag = ty.zigTypeTag(mod); 34200 switch (peer_tag) { 34201 .ComptimeInt => { 34202 // If the value is undefined, we can't refine to a fixed-width int 34203 if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .conflict = .{ 34204 .peer_idx_a = strat_reason, 34205 .peer_idx_b = i, 34206 } }; 34207 any_comptime_known = true; 34208 ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?); 34209 continue; 34210 }, 34211 .Int => {}, 34212 else => return .{ .conflict = .{ 34213 .peer_idx_a = strat_reason, 34214 .peer_idx_b = i, 34215 } }, 34216 } 34217 34218 if (opt_val != null) any_comptime_known = true; 34219 34220 const info = ty.intInfo(mod); 34221 34222 const idx_ptr = switch (info.signedness) { 34223 .unsigned => &idx_unsigned, 34224 .signed => &idx_signed, 34225 }; 34226 34227 const largest_idx = idx_ptr.* orelse { 34228 idx_ptr.* = i; 34229 continue; 34230 }; 34231 34232 const cur_info = peer_tys[largest_idx].?.intInfo(mod); 34233 if (info.bits > cur_info.bits) { 34234 idx_ptr.* = i; 34235 } 34236 } 34237 34238 if (idx_signed == null) { 34239 return .{ .success = peer_tys[idx_unsigned.?].? }; 34240 } 34241 34242 if (idx_unsigned == null) { 34243 return .{ .success = peer_tys[idx_signed.?].? }; 34244 } 34245 34246 const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod); 34247 const signed_info = peer_tys[idx_signed.?].?.intInfo(mod); 34248 if (signed_info.bits > unsigned_info.bits) { 34249 return .{ .success = peer_tys[idx_signed.?].? }; 34250 } 34251 34252 // TODO: this is for compatibility with legacy behavior. Before this version of PTR was 34253 // implemented, the algorithm very often returned false positives, with the expectation 34254 // that you'd just hit a coercion error later. One of these was that for integers, the 34255 // largest type would always be returned, even if it couldn't fit everything. This had 34256 // an unintentional consequence to semantics, which is that if values were known at 34257 // comptime, they would be coerced down to the smallest type where possible. This 34258 // behavior is unintuitive and order-dependent, so in my opinion should be eliminated, 34259 // but for now we'll retain compatibility. 34260 if (any_comptime_known) { 34261 if (unsigned_info.bits > signed_info.bits) { 34262 return .{ .success = peer_tys[idx_unsigned.?].? }; 34263 } 34264 const idx = @min(idx_unsigned.?, idx_signed.?); 34265 return .{ .success = peer_tys[idx].? }; 34266 } 34267 34268 return .{ .conflict = .{ 34269 .peer_idx_a = idx_unsigned.?, 34270 .peer_idx_b = idx_signed.?, 34271 } }; 34272 }, 34273 34274 .fixed_float => { 34275 var opt_cur_ty: ?Type = null; 34276 34277 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 34278 const ty = opt_ty orelse continue; 34279 switch (ty.zigTypeTag(mod)) { 34280 .ComptimeFloat, .ComptimeInt => {}, 34281 .Int => { 34282 if (opt_val == null) return .{ .conflict = .{ 34283 .peer_idx_a = strat_reason, 34284 .peer_idx_b = i, 34285 } }; 34286 }, 34287 .Float => { 34288 if (opt_cur_ty) |cur_ty| { 34289 if (cur_ty.eql(ty, mod)) continue; 34290 // Recreate the type so we eliminate any c_longdouble 34291 const bits = @max(cur_ty.floatBits(target), ty.floatBits(target)); 34292 opt_cur_ty = switch (bits) { 34293 16 => Type.f16, 34294 32 => Type.f32, 34295 64 => Type.f64, 34296 80 => Type.f80, 34297 128 => Type.f128, 34298 else => unreachable, 34299 }; 34300 } else { 34301 opt_cur_ty = ty; 34302 } 34303 }, 34304 else => return .{ .conflict = .{ 34305 .peer_idx_a = strat_reason, 34306 .peer_idx_b = i, 34307 } }, 34308 } 34309 } 34310 34311 // Note that fixed_float is only chosen if there is at least one fixed-width float peer, 34312 // so opt_cur_ty must be non-null. 34313 return .{ .success = opt_cur_ty.? }; 34314 }, 34315 34316 .coercible_struct => { 34317 // First, check that every peer has the same approximate structure (field count and names) 34318 34319 var opt_first_idx: ?usize = null; 34320 var is_tuple: bool = undefined; 34321 var field_count: usize = undefined; 34322 // Only defined for non-tuples. 34323 var field_names: []InternPool.NullTerminatedString = undefined; 34324 34325 for (peer_tys, 0..) |opt_ty, i| { 34326 const ty = opt_ty orelse continue; 34327 34328 if (!ty.isTupleOrAnonStruct(mod)) { 34329 return .{ .conflict = .{ 34330 .peer_idx_a = strat_reason, 34331 .peer_idx_b = i, 34332 } }; 34333 } 34334 34335 const first_idx = opt_first_idx orelse { 34336 opt_first_idx = i; 34337 is_tuple = ty.isTuple(mod); 34338 field_count = ty.structFieldCount(mod); 34339 if (!is_tuple) { 34340 const names = ip.indexToKey(ty.toIntern()).anon_struct_type.names.get(ip); 34341 field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names); 34342 } 34343 continue; 34344 }; 34345 34346 if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) { 34347 return .{ .conflict = .{ 34348 .peer_idx_a = first_idx, 34349 .peer_idx_b = i, 34350 } }; 34351 } 34352 34353 if (!is_tuple) { 34354 for (field_names, 0..) |expected, field_index_usize| { 34355 const field_index: u32 = @intCast(field_index_usize); 34356 const actual = ty.structFieldName(field_index, mod).unwrap().?; 34357 if (actual == expected) continue; 34358 return .{ .conflict = .{ 34359 .peer_idx_a = first_idx, 34360 .peer_idx_b = i, 34361 } }; 34362 } 34363 } 34364 } 34365 34366 assert(opt_first_idx != null); 34367 34368 // Now, we'll recursively resolve the field types 34369 const field_types = try sema.arena.alloc(InternPool.Index, field_count); 34370 // Values for `comptime` fields - `.none` used for non-comptime fields 34371 const field_vals = try sema.arena.alloc(InternPool.Index, field_count); 34372 const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); 34373 const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); 34374 34375 for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| { 34376 // Fill buffers with types and values of the field 34377 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { 34378 const ty = opt_ty orelse { 34379 peer_field_ty.* = null; 34380 peer_field_val.* = null; 34381 continue; 34382 }; 34383 peer_field_ty.* = ty.structFieldType(field_idx, mod); 34384 peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null; 34385 } 34386 34387 // Resolve field type recursively 34388 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) { 34389 .success => |ty| ty.toIntern(), 34390 else => |result| { 34391 const result_buf = try sema.arena.create(PeerResolveResult); 34392 result_buf.* = result; 34393 const field_name = if (is_tuple) name: { 34394 break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx}); 34395 } else try sema.arena.dupe(u8, ip.stringToSlice(field_names[field_idx])); 34396 34397 // The error info needs the field types, but we can't reuse sub_peer_tys 34398 // since the recursive call may have clobbered it. 34399 const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len); 34400 for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| { 34401 // Already-resolved types won't be referenced by the error so it's fine 34402 // to leave them undefined. 34403 const ty = opt_ty orelse continue; 34404 peer_field_ty.* = ty.structFieldType(field_idx, mod); 34405 } 34406 34407 return .{ .field_error = .{ 34408 .field_name = field_name, 34409 .field_types = peer_field_tys, 34410 .sub_result = result_buf, 34411 } }; 34412 }, 34413 }; 34414 34415 // Decide if this is a comptime field. If it is comptime in all peers, and the 34416 // coerced comptime values are all the same, we say it is comptime, else not. 34417 34418 var comptime_val: ?Value = null; 34419 for (peer_tys) |opt_ty| { 34420 const struct_ty = opt_ty orelse continue; 34421 try sema.resolveStructFieldInits(struct_ty); 34422 34423 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse { 34424 comptime_val = null; 34425 break; 34426 }; 34427 const uncoerced_field = Air.internedToRef(uncoerced_field_val.toIntern()); 34428 const coerced_inst = sema.coerceExtra(block, Type.fromInterned(field_ty.*), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) { 34429 // 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 34430 error.NotCoercible => { 34431 comptime_val = null; 34432 break; 34433 }, 34434 else => |e| return e, 34435 }; 34436 const coerced_val = (try sema.resolveValue(coerced_inst)) orelse continue; 34437 const existing = comptime_val orelse { 34438 comptime_val = coerced_val; 34439 continue; 34440 }; 34441 if (!coerced_val.eql(existing, Type.fromInterned(field_ty.*), mod)) { 34442 comptime_val = null; 34443 break; 34444 } 34445 } 34446 34447 field_val.* = if (comptime_val) |v| v.toIntern() else .none; 34448 } 34449 34450 const final_ty = try ip.getAnonStructType(mod.gpa, .{ 34451 .types = field_types, 34452 .names = if (is_tuple) &.{} else field_names, 34453 .values = field_vals, 34454 }); 34455 34456 return .{ .success = Type.fromInterned(final_ty) }; 34457 }, 34458 34459 .exact => { 34460 var expect_ty: ?Type = null; 34461 var first_idx: usize = undefined; 34462 for (peer_tys, 0..) |opt_ty, i| { 34463 const ty = opt_ty orelse continue; 34464 if (expect_ty) |expect| { 34465 if (!ty.eql(expect, mod)) return .{ .conflict = .{ 34466 .peer_idx_a = first_idx, 34467 .peer_idx_b = i, 34468 } }; 34469 } else { 34470 expect_ty = ty; 34471 first_idx = i; 34472 } 34473 } 34474 return .{ .success = expect_ty.? }; 34475 }, 34476 } 34477 } 34478 34479 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { 34480 // e0 -> e1 34481 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { 34482 return e1; 34483 } 34484 34485 // e1 -> e0 34486 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { 34487 return e0; 34488 } 34489 34490 return sema.errorSetMerge(e0, e1); 34491 } 34492 34493 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { 34494 // ty_b -> ty_a 34495 if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) { 34496 return ty_a; 34497 } 34498 34499 // ty_a -> ty_b 34500 if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) { 34501 return ty_b; 34502 } 34503 34504 return null; 34505 } 34506 34507 const ArrayLike = struct { 34508 len: u64, 34509 /// `noreturn` indicates that this type is `struct{}` so can coerce to anything 34510 elem_ty: Type, 34511 }; 34512 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { 34513 const mod = sema.mod; 34514 return switch (ty.zigTypeTag(mod)) { 34515 .Array => .{ 34516 .len = ty.arrayLen(mod), 34517 .elem_ty = ty.childType(mod), 34518 }, 34519 .Struct => { 34520 const field_count = ty.structFieldCount(mod); 34521 if (field_count == 0) return .{ 34522 .len = 0, 34523 .elem_ty = Type.noreturn, 34524 }; 34525 if (!ty.isTuple(mod)) return null; 34526 const elem_ty = ty.structFieldType(0, mod); 34527 for (1..field_count) |i| { 34528 if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) { 34529 return null; 34530 } 34531 } 34532 return .{ 34533 .len = field_count, 34534 .elem_ty = elem_ty, 34535 }; 34536 }, 34537 else => null, 34538 }; 34539 } 34540 34541 pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void { 34542 const mod = sema.mod; 34543 const ip = &mod.intern_pool; 34544 34545 if (sema.fn_ret_ty_ies) |ies| { 34546 try sema.resolveInferredErrorSetPtr(block, src, ies); 34547 assert(ies.resolved != .none); 34548 ip.funcIesResolved(sema.func_index).* = ies.resolved; 34549 } 34550 } 34551 34552 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { 34553 const mod = sema.mod; 34554 const ip = &mod.intern_pool; 34555 const fn_ty_info = mod.typeToFunc(fn_ty).?; 34556 34557 try sema.resolveTypeFully(Type.fromInterned(fn_ty_info.return_type)); 34558 34559 if (mod.comp.config.any_error_tracing and 34560 Type.fromInterned(fn_ty_info.return_type).isError(mod)) 34561 { 34562 // Ensure the type exists so that backends can assume that. 34563 _ = try sema.getBuiltinType("StackTrace"); 34564 } 34565 34566 for (0..fn_ty_info.param_types.len) |i| { 34567 try sema.resolveTypeFully(Type.fromInterned(fn_ty_info.param_types.get(ip)[i])); 34568 } 34569 } 34570 34571 /// Make it so that calling hash() and eql() on `val` will not assert due 34572 /// to a type not having its layout resolved. 34573 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { 34574 const mod = sema.mod; 34575 switch (mod.intern_pool.indexToKey(val.toIntern())) { 34576 .int => |int| switch (int.storage) { 34577 .u64, .i64, .big_int => return val, 34578 .lazy_align, .lazy_size => return mod.intValue( 34579 Type.fromInterned(int.ty), 34580 (try val.getUnsignedIntAdvanced(mod, sema)).?, 34581 ), 34582 }, 34583 .ptr => |ptr| { 34584 const resolved_len = switch (ptr.len) { 34585 .none => .none, 34586 else => (try sema.resolveLazyValue(Value.fromInterned(ptr.len))).toIntern(), 34587 }; 34588 switch (ptr.addr) { 34589 .decl, .mut_decl, .anon_decl => return if (resolved_len == ptr.len) 34590 val 34591 else 34592 Value.fromInterned((try mod.intern(.{ .ptr = .{ 34593 .ty = ptr.ty, 34594 .addr = switch (ptr.addr) { 34595 .decl => |decl| .{ .decl = decl }, 34596 .mut_decl => |mut_decl| .{ .mut_decl = mut_decl }, 34597 .anon_decl => |anon_decl| .{ .anon_decl = anon_decl }, 34598 else => unreachable, 34599 }, 34600 .len = resolved_len, 34601 } }))), 34602 .comptime_field => |field_val| { 34603 const resolved_field_val = 34604 (try sema.resolveLazyValue(Value.fromInterned(field_val))).toIntern(); 34605 return if (resolved_field_val == field_val and resolved_len == ptr.len) 34606 val 34607 else 34608 Value.fromInterned((try mod.intern(.{ .ptr = .{ 34609 .ty = ptr.ty, 34610 .addr = .{ .comptime_field = resolved_field_val }, 34611 .len = resolved_len, 34612 } }))); 34613 }, 34614 .int => |int| { 34615 const resolved_int = (try sema.resolveLazyValue(Value.fromInterned(int))).toIntern(); 34616 return if (resolved_int == int and resolved_len == ptr.len) 34617 val 34618 else 34619 Value.fromInterned((try mod.intern(.{ .ptr = .{ 34620 .ty = ptr.ty, 34621 .addr = .{ .int = resolved_int }, 34622 .len = resolved_len, 34623 } }))); 34624 }, 34625 .eu_payload, .opt_payload => |base| { 34626 const resolved_base = (try sema.resolveLazyValue(Value.fromInterned(base))).toIntern(); 34627 return if (resolved_base == base and resolved_len == ptr.len) 34628 val 34629 else 34630 Value.fromInterned((try mod.intern(.{ .ptr = .{ 34631 .ty = ptr.ty, 34632 .addr = switch (ptr.addr) { 34633 .eu_payload => .{ .eu_payload = resolved_base }, 34634 .opt_payload => .{ .opt_payload = resolved_base }, 34635 else => unreachable, 34636 }, 34637 .len = ptr.len, 34638 } }))); 34639 }, 34640 .elem, .field => |base_index| { 34641 const resolved_base = (try sema.resolveLazyValue(Value.fromInterned(base_index.base))).toIntern(); 34642 return if (resolved_base == base_index.base and resolved_len == ptr.len) 34643 val 34644 else 34645 Value.fromInterned((try mod.intern(.{ .ptr = .{ 34646 .ty = ptr.ty, 34647 .addr = switch (ptr.addr) { 34648 .elem => .{ .elem = .{ 34649 .base = resolved_base, 34650 .index = base_index.index, 34651 } }, 34652 .field => .{ .field = .{ 34653 .base = resolved_base, 34654 .index = base_index.index, 34655 } }, 34656 else => unreachable, 34657 }, 34658 .len = ptr.len, 34659 } }))); 34660 }, 34661 } 34662 }, 34663 .aggregate => |aggregate| switch (aggregate.storage) { 34664 .bytes => return val, 34665 .elems => |elems| { 34666 var resolved_elems: []InternPool.Index = &.{}; 34667 for (elems, 0..) |elem, i| { 34668 const resolved_elem = (try sema.resolveLazyValue(Value.fromInterned(elem))).toIntern(); 34669 if (resolved_elems.len == 0 and resolved_elem != elem) { 34670 resolved_elems = try sema.arena.alloc(InternPool.Index, elems.len); 34671 @memcpy(resolved_elems[0..i], elems[0..i]); 34672 } 34673 if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem; 34674 } 34675 return if (resolved_elems.len == 0) val else Value.fromInterned((try mod.intern(.{ .aggregate = .{ 34676 .ty = aggregate.ty, 34677 .storage = .{ .elems = resolved_elems }, 34678 } }))); 34679 }, 34680 .repeated_elem => |elem| { 34681 const resolved_elem = (try sema.resolveLazyValue(Value.fromInterned(elem))).toIntern(); 34682 return if (resolved_elem == elem) val else Value.fromInterned((try mod.intern(.{ .aggregate = .{ 34683 .ty = aggregate.ty, 34684 .storage = .{ .repeated_elem = resolved_elem }, 34685 } }))); 34686 }, 34687 }, 34688 .un => |un| { 34689 const resolved_tag = (try sema.resolveLazyValue(Value.fromInterned(un.tag))).toIntern(); 34690 const resolved_val = (try sema.resolveLazyValue(Value.fromInterned(un.val))).toIntern(); 34691 return if (resolved_tag == un.tag and resolved_val == un.val) 34692 val 34693 else 34694 Value.fromInterned((try mod.intern(.{ .un = .{ 34695 .ty = un.ty, 34696 .tag = resolved_tag, 34697 .val = resolved_val, 34698 } }))); 34699 }, 34700 else => return val, 34701 } 34702 } 34703 34704 pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void { 34705 const mod = sema.mod; 34706 switch (mod.intern_pool.indexToKey(ty.toIntern())) { 34707 .simple_type => |simple_type| return sema.resolveSimpleType(simple_type), 34708 else => {}, 34709 } 34710 switch (ty.zigTypeTag(mod)) { 34711 .Struct => return sema.resolveStructLayout(ty), 34712 .Union => return sema.resolveUnionLayout(ty), 34713 .Array => { 34714 if (ty.arrayLenIncludingSentinel(mod) == 0) return; 34715 const elem_ty = ty.childType(mod); 34716 return sema.resolveTypeLayout(elem_ty); 34717 }, 34718 .Optional => { 34719 const payload_ty = ty.optionalChild(mod); 34720 // In case of querying the ABI alignment of this optional, we will ask 34721 // for hasRuntimeBits() of the payload type, so we need "requires comptime" 34722 // to be known already before this function returns. 34723 _ = try sema.typeRequiresComptime(payload_ty); 34724 return sema.resolveTypeLayout(payload_ty); 34725 }, 34726 .ErrorUnion => { 34727 const payload_ty = ty.errorUnionPayload(mod); 34728 return sema.resolveTypeLayout(payload_ty); 34729 }, 34730 .Fn => { 34731 const info = mod.typeToFunc(ty).?; 34732 if (info.is_generic) { 34733 // Resolving of generic function types is deferred to when 34734 // the function is instantiated. 34735 return; 34736 } 34737 const ip = &mod.intern_pool; 34738 for (0..info.param_types.len) |i| { 34739 const param_ty = info.param_types.get(ip)[i]; 34740 try sema.resolveTypeLayout(Type.fromInterned(param_ty)); 34741 } 34742 try sema.resolveTypeLayout(Type.fromInterned(info.return_type)); 34743 }, 34744 else => {}, 34745 } 34746 } 34747 34748 /// Resolve a struct's alignment only without triggering resolution of its layout. 34749 /// Asserts that the alignment is not yet resolved and the layout is non-packed. 34750 pub fn resolveStructAlignment( 34751 sema: *Sema, 34752 ty: InternPool.Index, 34753 struct_type: InternPool.Key.StructType, 34754 ) CompileError!Alignment { 34755 const mod = sema.mod; 34756 const ip = &mod.intern_pool; 34757 const target = mod.getTarget(); 34758 34759 assert(struct_type.flagsPtr(ip).alignment == .none); 34760 assert(struct_type.layout != .Packed); 34761 34762 if (struct_type.flagsPtr(ip).field_types_wip) { 34763 // We'll guess "pointer-aligned", if the struct has an 34764 // underaligned pointer field then some allocations 34765 // might require explicit alignment. 34766 struct_type.flagsPtr(ip).assumed_pointer_aligned = true; 34767 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); 34768 struct_type.flagsPtr(ip).alignment = result; 34769 return result; 34770 } 34771 34772 try sema.resolveTypeFieldsStruct(ty, struct_type); 34773 34774 if (struct_type.setAlignmentWip(ip)) { 34775 // We'll guess "pointer-aligned", if the struct has an 34776 // underaligned pointer field then some allocations 34777 // might require explicit alignment. 34778 struct_type.flagsPtr(ip).assumed_pointer_aligned = true; 34779 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); 34780 struct_type.flagsPtr(ip).alignment = result; 34781 return result; 34782 } 34783 defer struct_type.clearAlignmentWip(ip); 34784 34785 var result: Alignment = .@"1"; 34786 34787 for (0..struct_type.field_types.len) |i| { 34788 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 34789 if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) 34790 continue; 34791 const field_align = try sema.structFieldAlignment( 34792 struct_type.fieldAlign(ip, i), 34793 field_ty, 34794 struct_type.layout, 34795 ); 34796 result = result.maxStrict(field_align); 34797 } 34798 34799 struct_type.flagsPtr(ip).alignment = result; 34800 return result; 34801 } 34802 34803 fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { 34804 const mod = sema.mod; 34805 const ip = &mod.intern_pool; 34806 const struct_type = mod.typeToStruct(ty) orelse return; 34807 34808 if (struct_type.haveLayout(ip)) 34809 return; 34810 34811 try sema.resolveTypeFields(ty); 34812 34813 if (struct_type.layout == .Packed) { 34814 try semaBackingIntType(mod, struct_type); 34815 return; 34816 } 34817 34818 if (struct_type.setLayoutWip(ip)) { 34819 const msg = try Module.ErrorMsg.create( 34820 sema.gpa, 34821 mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod), 34822 "struct '{}' depends on itself", 34823 .{ty.fmt(mod)}, 34824 ); 34825 return sema.failWithOwnedErrorMsg(null, msg); 34826 } 34827 defer struct_type.clearLayoutWip(ip); 34828 34829 const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len); 34830 const sizes = try sema.arena.alloc(u64, struct_type.field_types.len); 34831 34832 var big_align: Alignment = .@"1"; 34833 34834 for (aligns, sizes, 0..) |*field_align, *field_size, i| { 34835 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 34836 if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) { 34837 struct_type.offsets.get(ip)[i] = 0; 34838 field_size.* = 0; 34839 field_align.* = .none; 34840 continue; 34841 } 34842 34843 field_size.* = sema.typeAbiSize(field_ty) catch |err| switch (err) { 34844 error.AnalysisFail => { 34845 const msg = sema.err orelse return err; 34846 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); 34847 return err; 34848 }, 34849 else => return err, 34850 }; 34851 field_align.* = try sema.structFieldAlignment( 34852 struct_type.fieldAlign(ip, i), 34853 field_ty, 34854 struct_type.layout, 34855 ); 34856 big_align = big_align.maxStrict(field_align.*); 34857 } 34858 34859 if (struct_type.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { 34860 const msg = try Module.ErrorMsg.create( 34861 sema.gpa, 34862 mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod), 34863 "struct layout depends on it having runtime bits", 34864 .{}, 34865 ); 34866 return sema.failWithOwnedErrorMsg(null, msg); 34867 } 34868 34869 if (struct_type.flagsPtr(ip).assumed_pointer_aligned and 34870 big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(mod.getTarget().ptrBitWidth(), 8)))) 34871 { 34872 const msg = try Module.ErrorMsg.create( 34873 sema.gpa, 34874 mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod), 34875 "struct layout depends on being pointer aligned", 34876 .{}, 34877 ); 34878 return sema.failWithOwnedErrorMsg(null, msg); 34879 } 34880 34881 if (struct_type.hasReorderedFields()) { 34882 const runtime_order = struct_type.runtime_order.get(ip); 34883 34884 for (runtime_order, 0..) |*ro, i| { 34885 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 34886 if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) { 34887 ro.* = .omitted; 34888 } else { 34889 ro.* = @enumFromInt(i); 34890 } 34891 } 34892 34893 const RuntimeOrder = InternPool.Key.StructType.RuntimeOrder; 34894 34895 const AlignSortContext = struct { 34896 aligns: []const Alignment, 34897 34898 fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool { 34899 if (a == .omitted) return false; 34900 if (b == .omitted) return true; 34901 const a_align = ctx.aligns[@intFromEnum(a)]; 34902 const b_align = ctx.aligns[@intFromEnum(b)]; 34903 return a_align.compare(.gt, b_align); 34904 } 34905 }; 34906 if (struct_type.isTuple(ip) or !mod.backendSupportsFeature(.field_reordering)) { 34907 // TODO: don't handle tuples differently. This logic exists only because it 34908 // uncovers latent bugs if removed. Fix the latent bugs and remove this logic! 34909 // Likewise, implement field reordering support in all the backends! 34910 // This logic does not reorder fields; it only moves the omitted ones to the end 34911 // so that logic elsewhere does not need to special-case tuples. 34912 var i: usize = 0; 34913 var off: usize = 0; 34914 while (i + off < runtime_order.len) { 34915 if (runtime_order[i + off] == .omitted) { 34916 off += 1; 34917 continue; 34918 } 34919 runtime_order[i] = runtime_order[i + off]; 34920 i += 1; 34921 } 34922 @memset(runtime_order[i..], .omitted); 34923 } else { 34924 mem.sortUnstable(RuntimeOrder, runtime_order, AlignSortContext{ 34925 .aligns = aligns, 34926 }, AlignSortContext.lessThan); 34927 } 34928 } 34929 34930 // Calculate size, alignment, and field offsets. 34931 const offsets = struct_type.offsets.get(ip); 34932 var it = struct_type.iterateRuntimeOrder(ip); 34933 var offset: u64 = 0; 34934 while (it.next()) |i| { 34935 offsets[i] = @intCast(aligns[i].forward(offset)); 34936 offset = offsets[i] + sizes[i]; 34937 } 34938 struct_type.size(ip).* = @intCast(big_align.forward(offset)); 34939 const flags = struct_type.flagsPtr(ip); 34940 flags.alignment = big_align; 34941 flags.layout_resolved = true; 34942 _ = try sema.typeRequiresComptime(ty); 34943 } 34944 34945 fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) CompileError!void { 34946 const gpa = mod.gpa; 34947 const ip = &mod.intern_pool; 34948 34949 const decl_index = struct_type.decl.unwrap().?; 34950 const decl = mod.declPtr(decl_index); 34951 34952 const zir = mod.namespacePtr(struct_type.namespace.unwrap().?).file_scope.zir; 34953 34954 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 34955 defer analysis_arena.deinit(); 34956 34957 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa); 34958 defer comptime_mutable_decls.deinit(); 34959 34960 var sema: Sema = .{ 34961 .mod = mod, 34962 .gpa = gpa, 34963 .arena = analysis_arena.allocator(), 34964 .code = zir, 34965 .owner_decl = decl, 34966 .owner_decl_index = decl_index, 34967 .func_index = .none, 34968 .func_is_naked = false, 34969 .fn_ret_ty = Type.void, 34970 .fn_ret_ty_ies = null, 34971 .owner_func_index = .none, 34972 .comptime_mutable_decls = &comptime_mutable_decls, 34973 }; 34974 defer sema.deinit(); 34975 34976 var block: Block = .{ 34977 .parent = null, 34978 .sema = &sema, 34979 .src_decl = decl_index, 34980 .namespace = struct_type.namespace.unwrap() orelse decl.src_namespace, 34981 .wip_capture_scope = try mod.createCaptureScope(decl.src_scope), 34982 .instructions = .{}, 34983 .inlining = null, 34984 .is_comptime = true, 34985 }; 34986 defer assert(block.instructions.items.len == 0); 34987 34988 const fields_bit_sum = blk: { 34989 var accumulator: u64 = 0; 34990 for (0..struct_type.field_types.len) |i| { 34991 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 34992 accumulator += try field_ty.bitSizeAdvanced(mod, &sema); 34993 } 34994 break :blk accumulator; 34995 }; 34996 34997 const extended = zir.instructions.items(.data)[@intFromEnum(struct_type.zir_index)].extended; 34998 assert(extended.opcode == .struct_decl); 34999 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 35000 35001 if (small.has_backing_int) { 35002 var extra_index: usize = extended.operand; 35003 extra_index += @intFromBool(small.has_src_node); 35004 extra_index += @intFromBool(small.has_fields_len); 35005 extra_index += @intFromBool(small.has_decls_len); 35006 35007 const backing_int_body_len = zir.extra[extra_index]; 35008 extra_index += 1; 35009 35010 const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 }; 35011 const backing_int_ty = blk: { 35012 if (backing_int_body_len == 0) { 35013 const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 35014 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref); 35015 } else { 35016 const body = zir.bodySlice(extra_index, backing_int_body_len); 35017 const ty_ref = try sema.resolveBody(&block, body, struct_type.zir_index); 35018 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref); 35019 } 35020 }; 35021 35022 try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); 35023 struct_type.backingIntType(ip).* = backing_int_ty.toIntern(); 35024 } else { 35025 if (fields_bit_sum > std.math.maxInt(u16)) { 35026 return sema.fail(&block, LazySrcLoc.nodeOffset(0), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum}); 35027 } 35028 const backing_int_ty = try mod.intType(.unsigned, @intCast(fields_bit_sum)); 35029 struct_type.backingIntType(ip).* = backing_int_ty.toIntern(); 35030 } 35031 35032 for (comptime_mutable_decls.items) |ct_decl_index| { 35033 const ct_decl = mod.declPtr(ct_decl_index); 35034 _ = try ct_decl.internValue(mod); 35035 } 35036 } 35037 35038 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { 35039 const mod = sema.mod; 35040 35041 if (!backing_int_ty.isInt(mod)) { 35042 return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)}); 35043 } 35044 if (backing_int_ty.bitSize(mod) != fields_bit_sum) { 35045 return sema.fail( 35046 block, 35047 src, 35048 "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}", 35049 .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(mod), fields_bit_sum }, 35050 ); 35051 } 35052 } 35053 35054 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 35055 const mod = sema.mod; 35056 if (!ty.isIndexable(mod)) { 35057 const msg = msg: { 35058 const msg = try sema.errMsg(block, src, "type '{}' does not support indexing", .{ty.fmt(sema.mod)}); 35059 errdefer msg.destroy(sema.gpa); 35060 try sema.errNote(block, src, msg, "operand must be an array, slice, tuple, or vector", .{}); 35061 break :msg msg; 35062 }; 35063 return sema.failWithOwnedErrorMsg(block, msg); 35064 } 35065 } 35066 35067 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 35068 const mod = sema.mod; 35069 if (ty.zigTypeTag(mod) == .Pointer) { 35070 switch (ty.ptrSize(mod)) { 35071 .Slice, .Many, .C => return, 35072 .One => { 35073 const elem_ty = ty.childType(mod); 35074 if (elem_ty.zigTypeTag(mod) == .Array) return; 35075 // TODO https://github.com/ziglang/zig/issues/15479 35076 // if (elem_ty.isTuple()) return; 35077 }, 35078 } 35079 } 35080 const msg = msg: { 35081 const msg = try sema.errMsg(block, src, "type '{}' is not an indexable pointer", .{ty.fmt(sema.mod)}); 35082 errdefer msg.destroy(sema.gpa); 35083 try sema.errNote(block, src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); 35084 break :msg msg; 35085 }; 35086 return sema.failWithOwnedErrorMsg(block, msg); 35087 } 35088 35089 /// Resolve a unions's alignment only without triggering resolution of its layout. 35090 /// Asserts that the alignment is not yet resolved. 35091 pub fn resolveUnionAlignment( 35092 sema: *Sema, 35093 ty: Type, 35094 union_type: InternPool.Key.UnionType, 35095 ) CompileError!Alignment { 35096 const mod = sema.mod; 35097 const ip = &mod.intern_pool; 35098 const target = mod.getTarget(); 35099 35100 assert(!union_type.haveLayout(ip)); 35101 35102 if (union_type.flagsPtr(ip).status == .field_types_wip) { 35103 // We'll guess "pointer-aligned", if the union has an 35104 // underaligned pointer field then some allocations 35105 // might require explicit alignment. 35106 union_type.flagsPtr(ip).assumed_pointer_aligned = true; 35107 const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); 35108 union_type.flagsPtr(ip).alignment = result; 35109 return result; 35110 } 35111 35112 try sema.resolveTypeFieldsUnion(ty, union_type); 35113 35114 const union_obj = ip.loadUnionType(union_type); 35115 var max_align: Alignment = .@"1"; 35116 for (0..union_obj.field_names.len) |field_index| { 35117 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 35118 if (!(try sema.typeHasRuntimeBits(field_ty))) continue; 35119 35120 const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index)); 35121 const field_align = if (explicit_align != .none) 35122 explicit_align 35123 else 35124 try sema.typeAbiAlignment(field_ty); 35125 35126 max_align = max_align.max(field_align); 35127 } 35128 35129 union_type.flagsPtr(ip).alignment = max_align; 35130 return max_align; 35131 } 35132 35133 /// This logic must be kept in sync with `Module.getUnionLayout`. 35134 fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { 35135 const mod = sema.mod; 35136 const ip = &mod.intern_pool; 35137 35138 const union_type = ip.indexToKey(ty.ip_index).union_type; 35139 try sema.resolveTypeFieldsUnion(ty, union_type); 35140 35141 const union_obj = ip.loadUnionType(union_type); 35142 switch (union_obj.flagsPtr(ip).status) { 35143 .none, .have_field_types => {}, 35144 .field_types_wip, .layout_wip => { 35145 const msg = try Module.ErrorMsg.create( 35146 sema.gpa, 35147 mod.declPtr(union_obj.decl).srcLoc(mod), 35148 "union '{}' depends on itself", 35149 .{ty.fmt(mod)}, 35150 ); 35151 return sema.failWithOwnedErrorMsg(null, msg); 35152 }, 35153 .have_layout, .fully_resolved_wip, .fully_resolved => return, 35154 } 35155 35156 const prev_status = union_obj.flagsPtr(ip).status; 35157 errdefer if (union_obj.flagsPtr(ip).status == .layout_wip) { 35158 union_obj.flagsPtr(ip).status = prev_status; 35159 }; 35160 35161 union_obj.flagsPtr(ip).status = .layout_wip; 35162 35163 var max_size: u64 = 0; 35164 var max_align: Alignment = .@"1"; 35165 for (0..union_obj.field_names.len) |field_index| { 35166 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 35167 if (!(try sema.typeHasRuntimeBits(field_ty))) continue; 35168 35169 max_size = @max(max_size, sema.typeAbiSize(field_ty) catch |err| switch (err) { 35170 error.AnalysisFail => { 35171 const msg = sema.err orelse return err; 35172 try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{}); 35173 return err; 35174 }, 35175 else => return err, 35176 }); 35177 35178 const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index)); 35179 const field_align = if (explicit_align != .none) 35180 explicit_align 35181 else 35182 try sema.typeAbiAlignment(field_ty); 35183 35184 max_align = max_align.max(field_align); 35185 } 35186 35187 const flags = union_obj.flagsPtr(ip); 35188 const has_runtime_tag = flags.runtime_tag.hasTag() and try sema.typeHasRuntimeBits(Type.fromInterned(union_obj.enum_tag_ty)); 35189 const size, const alignment, const padding = if (has_runtime_tag) layout: { 35190 const enum_tag_type = Type.fromInterned(union_obj.enum_tag_ty); 35191 const tag_align = try sema.typeAbiAlignment(enum_tag_type); 35192 const tag_size = try sema.typeAbiSize(enum_tag_type); 35193 35194 // Put the tag before or after the payload depending on which one's 35195 // alignment is greater. 35196 var size: u64 = 0; 35197 var padding: u32 = 0; 35198 if (tag_align.compare(.gte, max_align)) { 35199 // {Tag, Payload} 35200 size += tag_size; 35201 size = max_align.forward(size); 35202 size += max_size; 35203 const prev_size = size; 35204 size = tag_align.forward(size); 35205 padding = @intCast(size - prev_size); 35206 } else { 35207 // {Payload, Tag} 35208 size += max_size; 35209 size = tag_align.forward(size); 35210 size += tag_size; 35211 const prev_size = size; 35212 size = max_align.forward(size); 35213 padding = @intCast(size - prev_size); 35214 } 35215 35216 break :layout .{ size, max_align.max(tag_align), padding }; 35217 } else .{ max_align.forward(max_size), max_align, 0 }; 35218 35219 union_type.size(ip).* = @intCast(size); 35220 union_type.padding(ip).* = padding; 35221 flags.alignment = alignment; 35222 flags.status = .have_layout; 35223 35224 if (union_obj.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { 35225 const msg = try Module.ErrorMsg.create( 35226 sema.gpa, 35227 mod.declPtr(union_obj.decl).srcLoc(mod), 35228 "union layout depends on it having runtime bits", 35229 .{}, 35230 ); 35231 return sema.failWithOwnedErrorMsg(null, msg); 35232 } 35233 35234 if (union_obj.flagsPtr(ip).assumed_pointer_aligned and 35235 alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(mod.getTarget().ptrBitWidth(), 8)))) 35236 { 35237 const msg = try Module.ErrorMsg.create( 35238 sema.gpa, 35239 mod.declPtr(union_obj.decl).srcLoc(mod), 35240 "union layout depends on being pointer aligned", 35241 .{}, 35242 ); 35243 return sema.failWithOwnedErrorMsg(null, msg); 35244 } 35245 } 35246 35247 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to 35248 /// be resolved. 35249 pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { 35250 const mod = sema.mod; 35251 const ip = &mod.intern_pool; 35252 switch (ty.zigTypeTag(mod)) { 35253 .Pointer => { 35254 return sema.resolveTypeFully(ty.childType(mod)); 35255 }, 35256 .Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 35257 .struct_type => return sema.resolveStructFully(ty), 35258 .anon_struct_type => |tuple| { 35259 for (tuple.types.get(ip)) |field_ty| { 35260 try sema.resolveTypeFully(Type.fromInterned(field_ty)); 35261 } 35262 }, 35263 .simple_type => |simple_type| try sema.resolveSimpleType(simple_type), 35264 else => {}, 35265 }, 35266 .Union => return sema.resolveUnionFully(ty), 35267 .Array => return sema.resolveTypeFully(ty.childType(mod)), 35268 .Optional => { 35269 return sema.resolveTypeFully(ty.optionalChild(mod)); 35270 }, 35271 .ErrorUnion => return sema.resolveTypeFully(ty.errorUnionPayload(mod)), 35272 .Fn => { 35273 const info = mod.typeToFunc(ty).?; 35274 if (info.is_generic) { 35275 // Resolving of generic function types is deferred to when 35276 // the function is instantiated. 35277 return; 35278 } 35279 for (0..info.param_types.len) |i| { 35280 const param_ty = info.param_types.get(ip)[i]; 35281 try sema.resolveTypeFully(Type.fromInterned(param_ty)); 35282 } 35283 try sema.resolveTypeFully(Type.fromInterned(info.return_type)); 35284 }, 35285 else => {}, 35286 } 35287 } 35288 35289 fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { 35290 try sema.resolveStructLayout(ty); 35291 35292 const mod = sema.mod; 35293 const ip = &mod.intern_pool; 35294 const struct_type = mod.typeToStruct(ty).?; 35295 35296 if (struct_type.setFullyResolved(ip)) return; 35297 errdefer struct_type.clearFullyResolved(ip); 35298 35299 // After we have resolve struct layout we have to go over the fields again to 35300 // make sure pointer fields get their child types resolved as well. 35301 // See also similar code for unions. 35302 35303 for (0..struct_type.field_types.len) |i| { 35304 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 35305 try sema.resolveTypeFully(field_ty); 35306 } 35307 } 35308 35309 fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { 35310 try sema.resolveUnionLayout(ty); 35311 35312 const mod = sema.mod; 35313 const ip = &mod.intern_pool; 35314 const union_obj = mod.typeToUnion(ty).?; 35315 switch (union_obj.flagsPtr(ip).status) { 35316 .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, 35317 .fully_resolved_wip, .fully_resolved => return, 35318 } 35319 35320 { 35321 // After we have resolve union layout we have to go over the fields again to 35322 // make sure pointer fields get their child types resolved as well. 35323 // See also similar code for structs. 35324 const prev_status = union_obj.flagsPtr(ip).status; 35325 errdefer union_obj.flagsPtr(ip).status = prev_status; 35326 35327 union_obj.flagsPtr(ip).status = .fully_resolved_wip; 35328 for (0..union_obj.field_types.len) |field_index| { 35329 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 35330 try sema.resolveTypeFully(field_ty); 35331 } 35332 union_obj.flagsPtr(ip).status = .fully_resolved; 35333 } 35334 35335 // And let's not forget comptime-only status. 35336 _ = try sema.typeRequiresComptime(ty); 35337 } 35338 35339 pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!void { 35340 const mod = sema.mod; 35341 const ip = &mod.intern_pool; 35342 const ty_ip = ty.toIntern(); 35343 35344 switch (ty_ip) { 35345 .var_args_param_type => unreachable, 35346 35347 .none => unreachable, 35348 35349 .u0_type, 35350 .i0_type, 35351 .u1_type, 35352 .u8_type, 35353 .i8_type, 35354 .u16_type, 35355 .i16_type, 35356 .u29_type, 35357 .u32_type, 35358 .i32_type, 35359 .u64_type, 35360 .i64_type, 35361 .u80_type, 35362 .u128_type, 35363 .i128_type, 35364 .usize_type, 35365 .isize_type, 35366 .c_char_type, 35367 .c_short_type, 35368 .c_ushort_type, 35369 .c_int_type, 35370 .c_uint_type, 35371 .c_long_type, 35372 .c_ulong_type, 35373 .c_longlong_type, 35374 .c_ulonglong_type, 35375 .c_longdouble_type, 35376 .f16_type, 35377 .f32_type, 35378 .f64_type, 35379 .f80_type, 35380 .f128_type, 35381 .anyopaque_type, 35382 .bool_type, 35383 .void_type, 35384 .type_type, 35385 .anyerror_type, 35386 .adhoc_inferred_error_set_type, 35387 .comptime_int_type, 35388 .comptime_float_type, 35389 .noreturn_type, 35390 .anyframe_type, 35391 .null_type, 35392 .undefined_type, 35393 .enum_literal_type, 35394 .manyptr_u8_type, 35395 .manyptr_const_u8_type, 35396 .manyptr_const_u8_sentinel_0_type, 35397 .single_const_pointer_to_comptime_int_type, 35398 .slice_const_u8_type, 35399 .slice_const_u8_sentinel_0_type, 35400 .optional_noreturn_type, 35401 .anyerror_void_error_union_type, 35402 .generic_poison_type, 35403 .empty_struct_type, 35404 => {}, 35405 35406 .undef => unreachable, 35407 .zero => unreachable, 35408 .zero_usize => unreachable, 35409 .zero_u8 => unreachable, 35410 .one => unreachable, 35411 .one_usize => unreachable, 35412 .one_u8 => unreachable, 35413 .four_u8 => unreachable, 35414 .negative_one => unreachable, 35415 .calling_convention_c => unreachable, 35416 .calling_convention_inline => unreachable, 35417 .void_value => unreachable, 35418 .unreachable_value => unreachable, 35419 .null_value => unreachable, 35420 .bool_true => unreachable, 35421 .bool_false => unreachable, 35422 .empty_struct => unreachable, 35423 .generic_poison => unreachable, 35424 35425 else => switch (ip.items.items(.tag)[@intFromEnum(ty_ip)]) { 35426 .type_struct, 35427 .type_struct_ns, 35428 .type_struct_packed, 35429 .type_struct_packed_inits, 35430 => try sema.resolveTypeFieldsStruct(ty_ip, ip.indexToKey(ty_ip).struct_type), 35431 35432 .type_union => try sema.resolveTypeFieldsUnion(Type.fromInterned(ty_ip), ip.indexToKey(ty_ip).union_type), 35433 .simple_type => try sema.resolveSimpleType(ip.indexToKey(ty_ip).simple_type), 35434 else => {}, 35435 }, 35436 } 35437 } 35438 35439 /// Fully resolves a simple type. This is usually a nop, but for builtin types with 35440 /// special InternPool indices (such as std.builtin.Type) it will analyze and fully 35441 /// resolve the container type. 35442 fn resolveSimpleType(sema: *Sema, simple_type: InternPool.SimpleType) CompileError!void { 35443 const builtin_type_name: []const u8 = switch (simple_type) { 35444 .atomic_order => "AtomicOrder", 35445 .atomic_rmw_op => "AtomicRmwOp", 35446 .calling_convention => "CallingConvention", 35447 .address_space => "AddressSpace", 35448 .float_mode => "FloatMode", 35449 .reduce_op => "ReduceOp", 35450 .call_modifier => "CallModifer", 35451 .prefetch_options => "PrefetchOptions", 35452 .export_options => "ExportOptions", 35453 .extern_options => "ExternOptions", 35454 .type_info => "Type", 35455 else => return, 35456 }; 35457 // This will fully resolve the type. 35458 _ = try sema.getBuiltinType(builtin_type_name); 35459 } 35460 35461 pub fn resolveTypeFieldsStruct( 35462 sema: *Sema, 35463 ty: InternPool.Index, 35464 struct_type: InternPool.Key.StructType, 35465 ) CompileError!void { 35466 const mod = sema.mod; 35467 const ip = &mod.intern_pool; 35468 // If there is no owner decl it means the struct has no fields. 35469 const owner_decl = struct_type.decl.unwrap() orelse return; 35470 35471 switch (mod.declPtr(owner_decl).analysis) { 35472 .file_failure, 35473 .dependency_failure, 35474 .sema_failure, 35475 .sema_failure_retryable, 35476 => { 35477 sema.owner_decl.analysis = .dependency_failure; 35478 sema.owner_decl.generation = mod.generation; 35479 return error.AnalysisFail; 35480 }, 35481 else => {}, 35482 } 35483 35484 if (struct_type.haveFieldTypes(ip)) return; 35485 35486 if (struct_type.setTypesWip(ip)) { 35487 const msg = try Module.ErrorMsg.create( 35488 sema.gpa, 35489 mod.declPtr(owner_decl).srcLoc(mod), 35490 "struct '{}' depends on itself", 35491 .{Type.fromInterned(ty).fmt(mod)}, 35492 ); 35493 return sema.failWithOwnedErrorMsg(null, msg); 35494 } 35495 defer struct_type.clearTypesWip(ip); 35496 35497 try semaStructFields(mod, sema.arena, struct_type); 35498 } 35499 35500 pub fn resolveStructFieldInits(sema: *Sema, ty: Type) CompileError!void { 35501 const mod = sema.mod; 35502 const ip = &mod.intern_pool; 35503 const struct_type = mod.typeToStruct(ty) orelse return; 35504 const owner_decl = struct_type.decl.unwrap() orelse return; 35505 35506 // Inits can start as resolved 35507 if (struct_type.haveFieldInits(ip)) return; 35508 35509 try sema.resolveStructLayout(ty); 35510 35511 if (struct_type.setInitsWip(ip)) { 35512 const msg = try Module.ErrorMsg.create( 35513 sema.gpa, 35514 mod.declPtr(owner_decl).srcLoc(mod), 35515 "struct '{}' depends on itself", 35516 .{ty.fmt(mod)}, 35517 ); 35518 return sema.failWithOwnedErrorMsg(null, msg); 35519 } 35520 defer struct_type.clearInitsWip(ip); 35521 35522 try semaStructFieldInits(mod, sema.arena, struct_type); 35523 struct_type.setHaveFieldInits(ip); 35524 } 35525 35526 pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void { 35527 const mod = sema.mod; 35528 const ip = &mod.intern_pool; 35529 const owner_decl = mod.declPtr(union_type.decl); 35530 switch (owner_decl.analysis) { 35531 .file_failure, 35532 .dependency_failure, 35533 .sema_failure, 35534 .sema_failure_retryable, 35535 => { 35536 sema.owner_decl.analysis = .dependency_failure; 35537 sema.owner_decl.generation = mod.generation; 35538 return error.AnalysisFail; 35539 }, 35540 else => {}, 35541 } 35542 switch (union_type.flagsPtr(ip).status) { 35543 .none => {}, 35544 .field_types_wip => { 35545 const msg = try Module.ErrorMsg.create( 35546 sema.gpa, 35547 owner_decl.srcLoc(mod), 35548 "union '{}' depends on itself", 35549 .{ty.fmt(mod)}, 35550 ); 35551 return sema.failWithOwnedErrorMsg(null, msg); 35552 }, 35553 .have_field_types, 35554 .have_layout, 35555 .layout_wip, 35556 .fully_resolved_wip, 35557 .fully_resolved, 35558 => return, 35559 } 35560 35561 union_type.flagsPtr(ip).status = .field_types_wip; 35562 errdefer union_type.flagsPtr(ip).status = .none; 35563 try semaUnionFields(mod, sema.arena, union_type); 35564 union_type.flagsPtr(ip).status = .have_field_types; 35565 } 35566 35567 /// Returns a normal error set corresponding to the fully populated inferred 35568 /// error set. 35569 fn resolveInferredErrorSet( 35570 sema: *Sema, 35571 block: *Block, 35572 src: LazySrcLoc, 35573 ies_index: InternPool.Index, 35574 ) CompileError!InternPool.Index { 35575 const mod = sema.mod; 35576 const ip = &mod.intern_pool; 35577 const func_index = ip.iesFuncIndex(ies_index); 35578 const func = mod.funcInfo(func_index); 35579 const resolved_ty = func.resolvedErrorSet(ip).*; 35580 if (resolved_ty != .none) return resolved_ty; 35581 if (func.analysis(ip).state == .in_progress) 35582 return sema.fail(block, src, "unable to resolve inferred error set", .{}); 35583 35584 // In order to ensure that all dependencies are properly added to the set, 35585 // we need to ensure the function body is analyzed of the inferred error 35586 // set. However, in the case of comptime/inline function calls with 35587 // inferred error sets, each call gets an adhoc InferredErrorSet object, which 35588 // has no corresponding function body. 35589 const ies_func_owner_decl = mod.declPtr(func.owner_decl); 35590 const ies_func_info = mod.typeToFunc(ies_func_owner_decl.ty).?; 35591 // if ies declared by a inline function with generic return type, the return_type should be generic_poison, 35592 // because inline function does not create a new declaration, and the ies has been filled with analyzeCall, 35593 // so here we can simply skip this case. 35594 if (ies_func_info.return_type == .generic_poison_type) { 35595 assert(ies_func_info.cc == .Inline); 35596 } else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) { 35597 if (ies_func_info.is_generic) { 35598 const msg = msg: { 35599 const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{}); 35600 errdefer msg.destroy(sema.gpa); 35601 35602 try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(mod), msg, "generic function declared here", .{}); 35603 break :msg msg; 35604 }; 35605 return sema.failWithOwnedErrorMsg(block, msg); 35606 } 35607 // In this case we are dealing with the actual InferredErrorSet object that 35608 // corresponds to the function, not one created to track an inline/comptime call. 35609 try sema.ensureFuncBodyAnalyzed(func_index); 35610 } 35611 35612 // This will now have been resolved by the logic at the end of `Module.analyzeFnBody` 35613 // which calls `resolveInferredErrorSetPtr`. 35614 const final_resolved_ty = func.resolvedErrorSet(ip).*; 35615 assert(final_resolved_ty != .none); 35616 return final_resolved_ty; 35617 } 35618 35619 pub fn resolveInferredErrorSetPtr( 35620 sema: *Sema, 35621 block: *Block, 35622 src: LazySrcLoc, 35623 ies: *InferredErrorSet, 35624 ) CompileError!void { 35625 const mod = sema.mod; 35626 const ip = &mod.intern_pool; 35627 35628 if (ies.resolved != .none) return; 35629 35630 const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern()); 35631 35632 for (ies.inferred_error_sets.keys()) |other_ies_index| { 35633 if (ies_index == other_ies_index) continue; 35634 switch (try sema.resolveInferredErrorSet(block, src, other_ies_index)) { 35635 .anyerror_type => { 35636 ies.resolved = .anyerror_type; 35637 return; 35638 }, 35639 else => |error_set_ty_index| { 35640 const names = ip.indexToKey(error_set_ty_index).error_set_type.names; 35641 for (names.get(ip)) |name| { 35642 try ies.errors.put(sema.arena, name, {}); 35643 } 35644 }, 35645 } 35646 } 35647 35648 const resolved_error_set_ty = try mod.errorSetFromUnsortedNames(ies.errors.keys()); 35649 ies.resolved = resolved_error_set_ty.toIntern(); 35650 } 35651 35652 fn resolveAdHocInferredErrorSet( 35653 sema: *Sema, 35654 block: *Block, 35655 src: LazySrcLoc, 35656 value: InternPool.Index, 35657 ) CompileError!InternPool.Index { 35658 const mod = sema.mod; 35659 const gpa = sema.gpa; 35660 const ip = &mod.intern_pool; 35661 const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value)); 35662 if (new_ty == .none) return value; 35663 return ip.getCoerced(gpa, value, new_ty); 35664 } 35665 35666 fn resolveAdHocInferredErrorSetTy( 35667 sema: *Sema, 35668 block: *Block, 35669 src: LazySrcLoc, 35670 ty: InternPool.Index, 35671 ) CompileError!InternPool.Index { 35672 const ies = sema.fn_ret_ty_ies orelse return .none; 35673 const mod = sema.mod; 35674 const gpa = sema.gpa; 35675 const ip = &mod.intern_pool; 35676 const error_union_info = switch (ip.indexToKey(ty)) { 35677 .error_union_type => |x| x, 35678 else => return .none, 35679 }; 35680 if (error_union_info.error_set_type != .adhoc_inferred_error_set_type) 35681 return .none; 35682 35683 try sema.resolveInferredErrorSetPtr(block, src, ies); 35684 const new_ty = try ip.get(gpa, .{ .error_union_type = .{ 35685 .error_set_type = ies.resolved, 35686 .payload_type = error_union_info.payload_type, 35687 } }); 35688 return new_ty; 35689 } 35690 35691 fn resolveInferredErrorSetTy( 35692 sema: *Sema, 35693 block: *Block, 35694 src: LazySrcLoc, 35695 ty: InternPool.Index, 35696 ) CompileError!InternPool.Index { 35697 const mod = sema.mod; 35698 const ip = &mod.intern_pool; 35699 if (ty == .anyerror_type) return ty; 35700 switch (ip.indexToKey(ty)) { 35701 .error_set_type => return ty, 35702 .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty), 35703 else => unreachable, 35704 } 35705 } 35706 35707 fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct { 35708 /// fields_len 35709 usize, 35710 Zir.Inst.StructDecl.Small, 35711 /// extra_index 35712 usize, 35713 } { 35714 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; 35715 assert(extended.opcode == .struct_decl); 35716 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 35717 var extra_index: usize = extended.operand; 35718 35719 extra_index += @intFromBool(small.has_src_node); 35720 35721 const fields_len = if (small.has_fields_len) blk: { 35722 const fields_len = zir.extra[extra_index]; 35723 extra_index += 1; 35724 break :blk fields_len; 35725 } else 0; 35726 35727 const decls_len = if (small.has_decls_len) decls_len: { 35728 const decls_len = zir.extra[extra_index]; 35729 extra_index += 1; 35730 break :decls_len decls_len; 35731 } else 0; 35732 35733 // The backing integer cannot be handled until `resolveStructLayout()`. 35734 if (small.has_backing_int) { 35735 const backing_int_body_len = zir.extra[extra_index]; 35736 extra_index += 1; // backing_int_body_len 35737 if (backing_int_body_len == 0) { 35738 extra_index += 1; // backing_int_ref 35739 } else { 35740 extra_index += backing_int_body_len; // backing_int_body_inst 35741 } 35742 } 35743 35744 // Skip over decls. 35745 var decls_it = zir.declIteratorInner(extra_index, decls_len); 35746 while (decls_it.next()) |_| {} 35747 extra_index = decls_it.extra_index; 35748 35749 return .{ fields_len, small, extra_index }; 35750 } 35751 35752 fn semaStructFields( 35753 mod: *Module, 35754 arena: Allocator, 35755 struct_type: InternPool.Key.StructType, 35756 ) CompileError!void { 35757 const gpa = mod.gpa; 35758 const ip = &mod.intern_pool; 35759 const decl_index = struct_type.decl.unwrap() orelse return; 35760 const decl = mod.declPtr(decl_index); 35761 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace; 35762 const zir = mod.namespacePtr(namespace_index).file_scope.zir; 35763 const zir_index = struct_type.zir_index; 35764 35765 const src = LazySrcLoc.nodeOffset(0); 35766 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index); 35767 35768 if (fields_len == 0) switch (struct_type.layout) { 35769 .Packed => { 35770 try semaBackingIntType(mod, struct_type); 35771 return; 35772 }, 35773 .Auto, .Extern => { 35774 struct_type.flagsPtr(ip).layout_resolved = true; 35775 return; 35776 }, 35777 }; 35778 35779 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa); 35780 defer comptime_mutable_decls.deinit(); 35781 35782 var sema: Sema = .{ 35783 .mod = mod, 35784 .gpa = gpa, 35785 .arena = arena, 35786 .code = zir, 35787 .owner_decl = decl, 35788 .owner_decl_index = decl_index, 35789 .func_index = .none, 35790 .func_is_naked = false, 35791 .fn_ret_ty = Type.void, 35792 .fn_ret_ty_ies = null, 35793 .owner_func_index = .none, 35794 .comptime_mutable_decls = &comptime_mutable_decls, 35795 }; 35796 defer sema.deinit(); 35797 35798 var block_scope: Block = .{ 35799 .parent = null, 35800 .sema = &sema, 35801 .src_decl = decl_index, 35802 .namespace = namespace_index, 35803 .wip_capture_scope = try mod.createCaptureScope(decl.src_scope), 35804 .instructions = .{}, 35805 .inlining = null, 35806 .is_comptime = true, 35807 }; 35808 defer assert(block_scope.instructions.items.len == 0); 35809 35810 const Field = struct { 35811 type_body_len: u32 = 0, 35812 align_body_len: u32 = 0, 35813 init_body_len: u32 = 0, 35814 type_ref: Zir.Inst.Ref = .none, 35815 }; 35816 const fields = try sema.arena.alloc(Field, fields_len); 35817 35818 var any_inits = false; 35819 var any_aligned = false; 35820 35821 { 35822 const bits_per_field = 4; 35823 const fields_per_u32 = 32 / bits_per_field; 35824 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 35825 const flags_index = extra_index; 35826 var bit_bag_index: usize = flags_index; 35827 extra_index += bit_bags_count; 35828 var cur_bit_bag: u32 = undefined; 35829 var field_i: u32 = 0; 35830 while (field_i < fields_len) : (field_i += 1) { 35831 if (field_i % fields_per_u32 == 0) { 35832 cur_bit_bag = zir.extra[bit_bag_index]; 35833 bit_bag_index += 1; 35834 } 35835 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 35836 cur_bit_bag >>= 1; 35837 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; 35838 cur_bit_bag >>= 1; 35839 const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0; 35840 cur_bit_bag >>= 1; 35841 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; 35842 cur_bit_bag >>= 1; 35843 35844 if (is_comptime) struct_type.setFieldComptime(ip, field_i); 35845 35846 var opt_field_name_zir: ?[:0]const u8 = null; 35847 if (!small.is_tuple) { 35848 opt_field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); 35849 extra_index += 1; 35850 } 35851 extra_index += 1; // doc_comment 35852 35853 fields[field_i] = .{}; 35854 35855 if (has_type_body) { 35856 fields[field_i].type_body_len = zir.extra[extra_index]; 35857 } else { 35858 fields[field_i].type_ref = @enumFromInt(zir.extra[extra_index]); 35859 } 35860 extra_index += 1; 35861 35862 // This string needs to outlive the ZIR code. 35863 if (opt_field_name_zir) |field_name_zir| { 35864 const field_name = try ip.getOrPutString(gpa, field_name_zir); 35865 if (struct_type.addFieldName(ip, field_name)) |other_index| { 35866 const msg = msg: { 35867 const field_src = mod.fieldSrcLoc(decl_index, .{ .index = field_i }).lazy; 35868 const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{}'", .{field_name.fmt(ip)}); 35869 errdefer msg.destroy(gpa); 35870 35871 const prev_field_src = mod.fieldSrcLoc(decl_index, .{ .index = other_index }); 35872 try mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{}); 35873 try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); 35874 break :msg msg; 35875 }; 35876 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35877 } 35878 } 35879 35880 if (has_align) { 35881 fields[field_i].align_body_len = zir.extra[extra_index]; 35882 extra_index += 1; 35883 any_aligned = true; 35884 } 35885 if (has_init) { 35886 fields[field_i].init_body_len = zir.extra[extra_index]; 35887 extra_index += 1; 35888 any_inits = true; 35889 } 35890 } 35891 } 35892 35893 // Next we do only types and alignments, saving the inits for a second pass, 35894 // so that init values may depend on type layout. 35895 35896 for (fields, 0..) |zir_field, field_i| { 35897 const field_ty: Type = ty: { 35898 if (zir_field.type_ref != .none) { 35899 break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) { 35900 error.NeededSourceLocation => { 35901 const ty_src = mod.fieldSrcLoc(decl_index, .{ 35902 .index = field_i, 35903 .range = .type, 35904 }).lazy; 35905 _ = try sema.resolveType(&block_scope, ty_src, zir_field.type_ref); 35906 unreachable; 35907 }, 35908 else => |e| return e, 35909 }; 35910 } 35911 assert(zir_field.type_body_len != 0); 35912 const body = zir.bodySlice(extra_index, zir_field.type_body_len); 35913 extra_index += body.len; 35914 const ty_ref = try sema.resolveBody(&block_scope, body, zir_index); 35915 break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) { 35916 error.NeededSourceLocation => { 35917 const ty_src = mod.fieldSrcLoc(decl_index, .{ 35918 .index = field_i, 35919 .range = .type, 35920 }).lazy; 35921 _ = try sema.analyzeAsType(&block_scope, ty_src, ty_ref); 35922 unreachable; 35923 }, 35924 else => |e| return e, 35925 }; 35926 }; 35927 if (field_ty.isGenericPoison()) { 35928 return error.GenericPoison; 35929 } 35930 35931 struct_type.field_types.get(ip)[field_i] = field_ty.toIntern(); 35932 35933 if (field_ty.zigTypeTag(mod) == .Opaque) { 35934 const msg = msg: { 35935 const ty_src = mod.fieldSrcLoc(decl_index, .{ 35936 .index = field_i, 35937 .range = .type, 35938 }).lazy; 35939 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 35940 errdefer msg.destroy(sema.gpa); 35941 35942 try sema.addDeclaredHereNote(msg, field_ty); 35943 break :msg msg; 35944 }; 35945 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35946 } 35947 if (field_ty.zigTypeTag(mod) == .NoReturn) { 35948 const msg = msg: { 35949 const ty_src = mod.fieldSrcLoc(decl_index, .{ 35950 .index = field_i, 35951 .range = .type, 35952 }).lazy; 35953 const msg = try sema.errMsg(&block_scope, ty_src, "struct fields cannot be 'noreturn'", .{}); 35954 errdefer msg.destroy(sema.gpa); 35955 35956 try sema.addDeclaredHereNote(msg, field_ty); 35957 break :msg msg; 35958 }; 35959 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35960 } 35961 switch (struct_type.layout) { 35962 .Extern => if (!try sema.validateExternType(field_ty, .struct_field)) { 35963 const msg = msg: { 35964 const ty_src = mod.fieldSrcLoc(decl_index, .{ 35965 .index = field_i, 35966 .range = .type, 35967 }); 35968 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 35969 errdefer msg.destroy(sema.gpa); 35970 35971 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .struct_field); 35972 35973 try sema.addDeclaredHereNote(msg, field_ty); 35974 break :msg msg; 35975 }; 35976 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35977 }, 35978 .Packed => if (!try sema.validatePackedType(field_ty)) { 35979 const msg = msg: { 35980 const ty_src = mod.fieldSrcLoc(decl_index, .{ 35981 .index = field_i, 35982 .range = .type, 35983 }); 35984 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 35985 errdefer msg.destroy(sema.gpa); 35986 35987 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty); 35988 35989 try sema.addDeclaredHereNote(msg, field_ty); 35990 break :msg msg; 35991 }; 35992 return sema.failWithOwnedErrorMsg(&block_scope, msg); 35993 }, 35994 else => {}, 35995 } 35996 35997 if (zir_field.align_body_len > 0) { 35998 const body = zir.bodySlice(extra_index, zir_field.align_body_len); 35999 extra_index += body.len; 36000 const align_ref = try sema.resolveBody(&block_scope, body, zir_index); 36001 const field_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { 36002 error.NeededSourceLocation => { 36003 const align_src = mod.fieldSrcLoc(decl_index, .{ 36004 .index = field_i, 36005 .range = .alignment, 36006 }).lazy; 36007 _ = try sema.analyzeAsAlign(&block_scope, align_src, align_ref); 36008 unreachable; 36009 }, 36010 else => |e| return e, 36011 }; 36012 struct_type.field_aligns.get(ip)[field_i] = field_align; 36013 } 36014 36015 extra_index += zir_field.init_body_len; 36016 } 36017 36018 struct_type.clearTypesWip(ip); 36019 if (!any_inits) struct_type.setHaveFieldInits(ip); 36020 36021 for (comptime_mutable_decls.items) |ct_decl_index| { 36022 const ct_decl = mod.declPtr(ct_decl_index); 36023 _ = try ct_decl.internValue(mod); 36024 } 36025 } 36026 36027 // This logic must be kept in sync with `semaStructFields` 36028 fn semaStructFieldInits( 36029 mod: *Module, 36030 arena: Allocator, 36031 struct_type: InternPool.Key.StructType, 36032 ) CompileError!void { 36033 const gpa = mod.gpa; 36034 const ip = &mod.intern_pool; 36035 36036 assert(!struct_type.haveFieldInits(ip)); 36037 36038 const decl_index = struct_type.decl.unwrap() orelse return; 36039 const decl = mod.declPtr(decl_index); 36040 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace; 36041 const zir = mod.namespacePtr(namespace_index).file_scope.zir; 36042 const zir_index = struct_type.zir_index; 36043 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index); 36044 36045 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa); 36046 defer comptime_mutable_decls.deinit(); 36047 36048 var sema: Sema = .{ 36049 .mod = mod, 36050 .gpa = gpa, 36051 .arena = arena, 36052 .code = zir, 36053 .owner_decl = decl, 36054 .owner_decl_index = decl_index, 36055 .func_index = .none, 36056 .func_is_naked = false, 36057 .fn_ret_ty = Type.void, 36058 .fn_ret_ty_ies = null, 36059 .owner_func_index = .none, 36060 .comptime_mutable_decls = &comptime_mutable_decls, 36061 }; 36062 defer sema.deinit(); 36063 36064 var block_scope: Block = .{ 36065 .parent = null, 36066 .sema = &sema, 36067 .src_decl = decl_index, 36068 .namespace = namespace_index, 36069 .wip_capture_scope = try mod.createCaptureScope(decl.src_scope), 36070 .instructions = .{}, 36071 .inlining = null, 36072 .is_comptime = true, 36073 }; 36074 defer assert(block_scope.instructions.items.len == 0); 36075 36076 const Field = struct { 36077 type_body_len: u32 = 0, 36078 align_body_len: u32 = 0, 36079 init_body_len: u32 = 0, 36080 }; 36081 const fields = try sema.arena.alloc(Field, fields_len); 36082 36083 var any_inits = false; 36084 36085 { 36086 const bits_per_field = 4; 36087 const fields_per_u32 = 32 / bits_per_field; 36088 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 36089 const flags_index = extra_index; 36090 var bit_bag_index: usize = flags_index; 36091 extra_index += bit_bags_count; 36092 var cur_bit_bag: u32 = undefined; 36093 var field_i: u32 = 0; 36094 while (field_i < fields_len) : (field_i += 1) { 36095 if (field_i % fields_per_u32 == 0) { 36096 cur_bit_bag = zir.extra[bit_bag_index]; 36097 bit_bag_index += 1; 36098 } 36099 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 36100 cur_bit_bag >>= 1; 36101 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; 36102 cur_bit_bag >>= 2; 36103 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; 36104 cur_bit_bag >>= 1; 36105 36106 if (!small.is_tuple) { 36107 extra_index += 1; 36108 } 36109 extra_index += 1; // doc_comment 36110 36111 fields[field_i] = .{}; 36112 36113 if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index]; 36114 extra_index += 1; 36115 36116 if (has_align) { 36117 fields[field_i].align_body_len = zir.extra[extra_index]; 36118 extra_index += 1; 36119 } 36120 if (has_init) { 36121 fields[field_i].init_body_len = zir.extra[extra_index]; 36122 extra_index += 1; 36123 any_inits = true; 36124 } 36125 } 36126 } 36127 36128 if (any_inits) { 36129 for (fields, 0..) |zir_field, field_i| { 36130 extra_index += zir_field.type_body_len; 36131 extra_index += zir_field.align_body_len; 36132 const body = zir.bodySlice(extra_index, zir_field.init_body_len); 36133 extra_index += zir_field.init_body_len; 36134 36135 if (body.len == 0) continue; 36136 36137 // Pre-populate the type mapping the body expects to be there. 36138 // In init bodies, the zir index of the struct itself is used 36139 // to refer to the current field type. 36140 36141 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_i]); 36142 const type_ref = Air.internedToRef(field_ty.toIntern()); 36143 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index}); 36144 sema.inst_map.putAssumeCapacity(zir_index, type_ref); 36145 36146 const init = try sema.resolveBody(&block_scope, body, zir_index); 36147 const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) { 36148 error.NeededSourceLocation => { 36149 const init_src = mod.fieldSrcLoc(decl_index, .{ 36150 .index = field_i, 36151 .range = .value, 36152 }).lazy; 36153 _ = try sema.coerce(&block_scope, field_ty, init, init_src); 36154 unreachable; 36155 }, 36156 else => |e| return e, 36157 }; 36158 const default_val = (try sema.resolveValue(coerced)) orelse { 36159 const init_src = mod.fieldSrcLoc(decl_index, .{ 36160 .index = field_i, 36161 .range = .value, 36162 }).lazy; 36163 return sema.failWithNeededComptime(&block_scope, init_src, .{ 36164 .needed_comptime_reason = "struct field default value must be comptime-known", 36165 }); 36166 }; 36167 36168 const field_init = try default_val.intern(field_ty, mod); 36169 struct_type.field_inits.get(ip)[field_i] = field_init; 36170 } 36171 } 36172 36173 for (comptime_mutable_decls.items) |ct_decl_index| { 36174 const ct_decl = mod.declPtr(ct_decl_index); 36175 _ = try ct_decl.internValue(mod); 36176 } 36177 } 36178 36179 fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.UnionType) CompileError!void { 36180 const tracy = trace(@src()); 36181 defer tracy.end(); 36182 36183 const gpa = mod.gpa; 36184 const ip = &mod.intern_pool; 36185 const decl_index = union_type.decl; 36186 const zir = mod.namespacePtr(union_type.namespace).file_scope.zir; 36187 const extended = zir.instructions.items(.data)[@intFromEnum(union_type.zir_index)].extended; 36188 assert(extended.opcode == .union_decl); 36189 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); 36190 var extra_index: usize = extended.operand; 36191 36192 const src = LazySrcLoc.nodeOffset(0); 36193 extra_index += @intFromBool(small.has_src_node); 36194 36195 const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { 36196 const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 36197 extra_index += 1; 36198 break :blk ty_ref; 36199 } else .none; 36200 36201 const body_len = if (small.has_body_len) blk: { 36202 const body_len = zir.extra[extra_index]; 36203 extra_index += 1; 36204 break :blk body_len; 36205 } else 0; 36206 36207 const fields_len = if (small.has_fields_len) blk: { 36208 const fields_len = zir.extra[extra_index]; 36209 extra_index += 1; 36210 break :blk fields_len; 36211 } else 0; 36212 36213 const decls_len = if (small.has_decls_len) decls_len: { 36214 const decls_len = zir.extra[extra_index]; 36215 extra_index += 1; 36216 break :decls_len decls_len; 36217 } else 0; 36218 36219 // Skip over decls. 36220 var decls_it = zir.declIteratorInner(extra_index, decls_len); 36221 while (decls_it.next()) |_| {} 36222 extra_index = decls_it.extra_index; 36223 36224 const body = zir.bodySlice(extra_index, body_len); 36225 extra_index += body.len; 36226 36227 const decl = mod.declPtr(decl_index); 36228 36229 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa); 36230 defer comptime_mutable_decls.deinit(); 36231 36232 var sema: Sema = .{ 36233 .mod = mod, 36234 .gpa = gpa, 36235 .arena = arena, 36236 .code = zir, 36237 .owner_decl = decl, 36238 .owner_decl_index = decl_index, 36239 .func_index = .none, 36240 .func_is_naked = false, 36241 .fn_ret_ty = Type.void, 36242 .fn_ret_ty_ies = null, 36243 .owner_func_index = .none, 36244 .comptime_mutable_decls = &comptime_mutable_decls, 36245 }; 36246 defer sema.deinit(); 36247 36248 var block_scope: Block = .{ 36249 .parent = null, 36250 .sema = &sema, 36251 .src_decl = decl_index, 36252 .namespace = union_type.namespace, 36253 .wip_capture_scope = try mod.createCaptureScope(decl.src_scope), 36254 .instructions = .{}, 36255 .inlining = null, 36256 .is_comptime = true, 36257 }; 36258 defer assert(block_scope.instructions.items.len == 0); 36259 36260 if (body.len != 0) { 36261 try sema.analyzeBody(&block_scope, body); 36262 } 36263 36264 for (comptime_mutable_decls.items) |ct_decl_index| { 36265 const ct_decl = mod.declPtr(ct_decl_index); 36266 _ = try ct_decl.internValue(mod); 36267 } 36268 36269 var int_tag_ty: Type = undefined; 36270 var enum_field_names: []InternPool.NullTerminatedString = &.{}; 36271 var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}; 36272 var explicit_tags_seen: []bool = &.{}; 36273 if (tag_type_ref != .none) { 36274 const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; 36275 const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); 36276 if (small.auto_enum_tag) { 36277 // The provided type is an integer type and we must construct the enum tag type here. 36278 int_tag_ty = provided_ty; 36279 if (int_tag_ty.zigTypeTag(mod) != .Int and int_tag_ty.zigTypeTag(mod) != .ComptimeInt) { 36280 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(mod)}); 36281 } 36282 36283 if (fields_len > 0) { 36284 const field_count_val = try mod.intValue(Type.comptime_int, fields_len - 1); 36285 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) { 36286 const msg = msg: { 36287 const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{}); 36288 errdefer msg.destroy(sema.gpa); 36289 try sema.errNote(&block_scope, tag_ty_src, msg, "type '{}' cannot fit values in range 0...{d}", .{ 36290 int_tag_ty.fmt(mod), 36291 fields_len - 1, 36292 }); 36293 break :msg msg; 36294 }; 36295 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36296 } 36297 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 36298 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len); 36299 } 36300 } else { 36301 // The provided type is the enum tag type. 36302 union_type.tagTypePtr(ip).* = provided_ty.toIntern(); 36303 const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) { 36304 .enum_type => |x| x, 36305 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{provided_ty.fmt(mod)}), 36306 }; 36307 // The fields of the union must match the enum exactly. 36308 // A flag per field is used to check for missing and extraneous fields. 36309 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); 36310 @memset(explicit_tags_seen, false); 36311 } 36312 } else { 36313 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis 36314 // purposes, we still auto-generate an enum tag type the same way. That the union is 36315 // untagged is represented by the Type tag (union vs union_tagged). 36316 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 36317 } 36318 36319 var field_types: std.ArrayListUnmanaged(InternPool.Index) = .{}; 36320 var field_aligns: std.ArrayListUnmanaged(InternPool.Alignment) = .{}; 36321 var field_name_table: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; 36322 36323 try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len); 36324 if (small.any_aligned_fields) 36325 try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len); 36326 try field_name_table.ensureTotalCapacity(sema.arena, fields_len); 36327 36328 const bits_per_field = 4; 36329 const fields_per_u32 = 32 / bits_per_field; 36330 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 36331 var bit_bag_index: usize = extra_index; 36332 extra_index += bit_bags_count; 36333 var cur_bit_bag: u32 = undefined; 36334 var field_i: u32 = 0; 36335 var last_tag_val: ?Value = null; 36336 while (field_i < fields_len) : (field_i += 1) { 36337 if (field_i % fields_per_u32 == 0) { 36338 cur_bit_bag = zir.extra[bit_bag_index]; 36339 bit_bag_index += 1; 36340 } 36341 const has_type = @as(u1, @truncate(cur_bit_bag)) != 0; 36342 cur_bit_bag >>= 1; 36343 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 36344 cur_bit_bag >>= 1; 36345 const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0; 36346 cur_bit_bag >>= 1; 36347 const unused = @as(u1, @truncate(cur_bit_bag)) != 0; 36348 cur_bit_bag >>= 1; 36349 _ = unused; 36350 36351 const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); 36352 extra_index += 1; 36353 36354 // doc_comment 36355 extra_index += 1; 36356 36357 const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { 36358 const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 36359 extra_index += 1; 36360 break :blk field_type_ref; 36361 } else .none; 36362 36363 const align_ref: Zir.Inst.Ref = if (has_align) blk: { 36364 const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 36365 extra_index += 1; 36366 break :blk align_ref; 36367 } else .none; 36368 36369 const tag_ref: Air.Inst.Ref = if (has_tag) blk: { 36370 const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 36371 extra_index += 1; 36372 break :blk try sema.resolveInst(tag_ref); 36373 } else .none; 36374 36375 if (enum_field_vals.capacity() > 0) { 36376 const enum_tag_val = if (tag_ref != .none) blk: { 36377 const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) { 36378 error.NeededSourceLocation => { 36379 const val_src = mod.fieldSrcLoc(union_type.decl, .{ 36380 .index = field_i, 36381 .range = .value, 36382 }).lazy; 36383 _ = try sema.semaUnionFieldVal(&block_scope, val_src, int_tag_ty, tag_ref); 36384 unreachable; 36385 }, 36386 else => |e| return e, 36387 }; 36388 last_tag_val = val; 36389 36390 break :blk val; 36391 } else blk: { 36392 const val = if (last_tag_val) |val| 36393 try sema.intAdd(val, Value.one_comptime_int, int_tag_ty, undefined) 36394 else 36395 try mod.intValue(int_tag_ty, 0); 36396 last_tag_val = val; 36397 36398 break :blk val; 36399 }; 36400 const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern()); 36401 if (gop.found_existing) { 36402 const field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i }).lazy; 36403 const other_field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = gop.index }).lazy; 36404 const msg = msg: { 36405 const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)}); 36406 errdefer msg.destroy(gpa); 36407 try sema.errNote(&block_scope, other_field_src, msg, "other occurrence here", .{}); 36408 break :msg msg; 36409 }; 36410 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36411 } 36412 } 36413 36414 // This string needs to outlive the ZIR code. 36415 const field_name = try ip.getOrPutString(gpa, field_name_zir); 36416 if (enum_field_names.len != 0) { 36417 enum_field_names[field_i] = field_name; 36418 } 36419 36420 const field_ty: Type = if (!has_type) 36421 Type.void 36422 else if (field_type_ref == .none) 36423 Type.noreturn 36424 else 36425 sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) { 36426 error.NeededSourceLocation => { 36427 const ty_src = mod.fieldSrcLoc(union_type.decl, .{ 36428 .index = field_i, 36429 .range = .type, 36430 }).lazy; 36431 _ = try sema.resolveType(&block_scope, ty_src, field_type_ref); 36432 unreachable; 36433 }, 36434 else => |e| return e, 36435 }; 36436 36437 if (field_ty.isGenericPoison()) { 36438 return error.GenericPoison; 36439 } 36440 36441 const gop = field_name_table.getOrPutAssumeCapacity(field_name); 36442 if (gop.found_existing) { 36443 const msg = msg: { 36444 const field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i }).lazy; 36445 const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{}'", .{ 36446 field_name.fmt(ip), 36447 }); 36448 errdefer msg.destroy(gpa); 36449 36450 const prev_field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = gop.index }).lazy; 36451 try mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{}); 36452 try sema.errNote(&block_scope, src, msg, "union declared here", .{}); 36453 break :msg msg; 36454 }; 36455 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36456 } 36457 36458 if (explicit_tags_seen.len > 0) { 36459 const tag_info = ip.indexToKey(union_type.tagTypePtr(ip).*).enum_type; 36460 const enum_index = tag_info.nameIndex(ip, field_name) orelse { 36461 const msg = msg: { 36462 const ty_src = mod.fieldSrcLoc(union_type.decl, .{ 36463 .index = field_i, 36464 .range = .type, 36465 }).lazy; 36466 const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{}' in enum '{}'", .{ 36467 field_name.fmt(ip), Type.fromInterned(union_type.tagTypePtr(ip).*).fmt(mod), 36468 }); 36469 errdefer msg.destroy(sema.gpa); 36470 try sema.addDeclaredHereNote(msg, Type.fromInterned(union_type.tagTypePtr(ip).*)); 36471 break :msg msg; 36472 }; 36473 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36474 }; 36475 // No check for duplicate because the check already happened in order 36476 // to create the enum type in the first place. 36477 assert(!explicit_tags_seen[enum_index]); 36478 explicit_tags_seen[enum_index] = true; 36479 36480 // Enforce the enum fields and the union fields being in the same order. 36481 if (enum_index != field_i) { 36482 const msg = msg: { 36483 const ty_src = mod.fieldSrcLoc(union_type.decl, .{ 36484 .index = field_i, 36485 .range = .type, 36486 }).lazy; 36487 const enum_field_src = mod.fieldSrcLoc(tag_info.decl, .{ .index = enum_index }).lazy; 36488 const msg = try sema.errMsg(&block_scope, ty_src, "union field '{}' ordered differently than corresponding enum field", .{ 36489 field_name.fmt(ip), 36490 }); 36491 errdefer msg.destroy(sema.gpa); 36492 try sema.errNote(&block_scope, enum_field_src, msg, "enum field here", .{}); 36493 break :msg msg; 36494 }; 36495 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36496 } 36497 } 36498 36499 if (field_ty.zigTypeTag(mod) == .Opaque) { 36500 const msg = msg: { 36501 const ty_src = mod.fieldSrcLoc(union_type.decl, .{ 36502 .index = field_i, 36503 .range = .type, 36504 }).lazy; 36505 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 36506 errdefer msg.destroy(sema.gpa); 36507 36508 try sema.addDeclaredHereNote(msg, field_ty); 36509 break :msg msg; 36510 }; 36511 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36512 } 36513 const layout = union_type.getLayout(ip); 36514 if (layout == .Extern and 36515 !try sema.validateExternType(field_ty, .union_field)) 36516 { 36517 const msg = msg: { 36518 const ty_src = mod.fieldSrcLoc(union_type.decl, .{ 36519 .index = field_i, 36520 .range = .type, 36521 }); 36522 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 36523 errdefer msg.destroy(sema.gpa); 36524 36525 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .union_field); 36526 36527 try sema.addDeclaredHereNote(msg, field_ty); 36528 break :msg msg; 36529 }; 36530 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36531 } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { 36532 const msg = msg: { 36533 const ty_src = mod.fieldSrcLoc(union_type.decl, .{ 36534 .index = field_i, 36535 .range = .type, 36536 }); 36537 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 36538 errdefer msg.destroy(sema.gpa); 36539 36540 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty); 36541 36542 try sema.addDeclaredHereNote(msg, field_ty); 36543 break :msg msg; 36544 }; 36545 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36546 } 36547 36548 field_types.appendAssumeCapacity(field_ty.toIntern()); 36549 36550 if (small.any_aligned_fields) { 36551 field_aligns.appendAssumeCapacity(if (align_ref != .none) 36552 sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { 36553 error.NeededSourceLocation => { 36554 const align_src = mod.fieldSrcLoc(union_type.decl, .{ 36555 .index = field_i, 36556 .range = .alignment, 36557 }).lazy; 36558 _ = try sema.resolveAlign(&block_scope, align_src, align_ref); 36559 unreachable; 36560 }, 36561 else => |e| return e, 36562 } 36563 else 36564 .none); 36565 } else { 36566 assert(align_ref == .none); 36567 } 36568 } 36569 36570 union_type.setFieldTypes(ip, field_types.items); 36571 union_type.setFieldAligns(ip, field_aligns.items); 36572 36573 if (explicit_tags_seen.len > 0) { 36574 const tag_info = ip.indexToKey(union_type.tagTypePtr(ip).*).enum_type; 36575 if (tag_info.names.len > fields_len) { 36576 const msg = msg: { 36577 const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); 36578 errdefer msg.destroy(sema.gpa); 36579 36580 for (tag_info.names.get(ip), 0..) |field_name, field_index| { 36581 if (explicit_tags_seen[field_index]) continue; 36582 try sema.addFieldErrNote(Type.fromInterned(union_type.tagTypePtr(ip).*), field_index, msg, "field '{}' missing, declared here", .{ 36583 field_name.fmt(ip), 36584 }); 36585 } 36586 try sema.addDeclaredHereNote(msg, Type.fromInterned(union_type.tagTypePtr(ip).*)); 36587 break :msg msg; 36588 }; 36589 return sema.failWithOwnedErrorMsg(&block_scope, msg); 36590 } 36591 } else if (enum_field_vals.count() > 0) { 36592 const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), mod.declPtr(union_type.decl)); 36593 union_type.tagTypePtr(ip).* = enum_ty; 36594 } else { 36595 const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_type.decl.toOptional()); 36596 union_type.tagTypePtr(ip).* = enum_ty; 36597 } 36598 } 36599 36600 fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Type, tag_ref: Air.Inst.Ref) CompileError!Value { 36601 const coerced = try sema.coerce(block, int_tag_ty, tag_ref, src); 36602 return sema.resolveConstDefinedValue(block, src, coerced, .{ 36603 .needed_comptime_reason = "enum tag value must be comptime-known", 36604 }); 36605 } 36606 36607 fn generateUnionTagTypeNumbered( 36608 sema: *Sema, 36609 block: *Block, 36610 enum_field_names: []const InternPool.NullTerminatedString, 36611 enum_field_vals: []const InternPool.Index, 36612 decl: *Module.Decl, 36613 ) !InternPool.Index { 36614 const mod = sema.mod; 36615 const gpa = sema.gpa; 36616 const ip = &mod.intern_pool; 36617 36618 const src_decl = mod.declPtr(block.src_decl); 36619 const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); 36620 errdefer mod.destroyDecl(new_decl_index); 36621 const fqn = try decl.getFullyQualifiedName(mod); 36622 const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)}); 36623 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{ 36624 .ty = Type.noreturn, 36625 .val = Value.@"unreachable", 36626 }, name); 36627 errdefer mod.abortAnonDecl(new_decl_index); 36628 36629 const new_decl = mod.declPtr(new_decl_index); 36630 new_decl.name_fully_qualified = true; 36631 new_decl.owns_tv = true; 36632 new_decl.name_fully_qualified = true; 36633 36634 const enum_ty = try ip.getEnum(gpa, .{ 36635 .decl = new_decl_index, 36636 .namespace = .none, 36637 .tag_ty = if (enum_field_vals.len == 0) 36638 (try mod.intType(.unsigned, 0)).toIntern() 36639 else 36640 ip.typeOf(enum_field_vals[0]), 36641 .names = enum_field_names, 36642 .values = enum_field_vals, 36643 .tag_mode = .explicit, 36644 }); 36645 36646 new_decl.ty = Type.type; 36647 new_decl.val = Value.fromInterned(enum_ty); 36648 36649 try mod.finalizeAnonDecl(new_decl_index); 36650 return enum_ty; 36651 } 36652 36653 fn generateUnionTagTypeSimple( 36654 sema: *Sema, 36655 block: *Block, 36656 enum_field_names: []const InternPool.NullTerminatedString, 36657 maybe_decl_index: InternPool.OptionalDeclIndex, 36658 ) !InternPool.Index { 36659 const mod = sema.mod; 36660 const ip = &mod.intern_pool; 36661 const gpa = sema.gpa; 36662 36663 const new_decl_index = new_decl_index: { 36664 const decl_index = maybe_decl_index.unwrap() orelse { 36665 break :new_decl_index try mod.createAnonymousDecl(block, .{ 36666 .ty = Type.noreturn, 36667 .val = Value.@"unreachable", 36668 }); 36669 }; 36670 const fqn = try mod.declPtr(decl_index).getFullyQualifiedName(mod); 36671 const src_decl = mod.declPtr(block.src_decl); 36672 const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); 36673 errdefer mod.destroyDecl(new_decl_index); 36674 const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)}); 36675 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{ 36676 .ty = Type.noreturn, 36677 .val = Value.@"unreachable", 36678 }, name); 36679 mod.declPtr(new_decl_index).name_fully_qualified = true; 36680 break :new_decl_index new_decl_index; 36681 }; 36682 errdefer mod.abortAnonDecl(new_decl_index); 36683 36684 const enum_ty = try ip.getEnum(gpa, .{ 36685 .decl = new_decl_index, 36686 .namespace = .none, 36687 .tag_ty = if (enum_field_names.len == 0) 36688 (try mod.intType(.unsigned, 0)).toIntern() 36689 else 36690 (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(), 36691 .names = enum_field_names, 36692 .values = &.{}, 36693 .tag_mode = .auto, 36694 }); 36695 36696 const new_decl = mod.declPtr(new_decl_index); 36697 new_decl.owns_tv = true; 36698 new_decl.ty = Type.type; 36699 new_decl.val = Value.fromInterned(enum_ty); 36700 36701 try mod.finalizeAnonDecl(new_decl_index); 36702 return enum_ty; 36703 } 36704 36705 fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { 36706 const mod = sema.mod; 36707 const gpa = sema.gpa; 36708 const src = LazySrcLoc.nodeOffset(0); 36709 36710 var block: Block = .{ 36711 .parent = null, 36712 .sema = sema, 36713 .src_decl = sema.owner_decl_index, 36714 .namespace = sema.owner_decl.src_namespace, 36715 .wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope), 36716 .instructions = .{}, 36717 .inlining = null, 36718 .is_comptime = true, 36719 }; 36720 defer block.instructions.deinit(gpa); 36721 36722 const decl_index = try getBuiltinDecl(sema, &block, name); 36723 return sema.analyzeDeclVal(&block, src, decl_index); 36724 } 36725 36726 fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!InternPool.DeclIndex { 36727 const gpa = sema.gpa; 36728 36729 const src = LazySrcLoc.nodeOffset(0); 36730 36731 const mod = sema.mod; 36732 const ip = &mod.intern_pool; 36733 const std_mod = mod.std_mod; 36734 const std_file = (mod.importPkg(std_mod) catch unreachable).file; 36735 const opt_builtin_inst = (try sema.namespaceLookupRef( 36736 block, 36737 src, 36738 mod.declPtr(std_file.root_decl.unwrap().?).src_namespace, 36739 try ip.getOrPutString(gpa, "builtin"), 36740 )) orelse @panic("lib/std.zig is corrupt and missing 'builtin'"); 36741 const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst, src); 36742 const builtin_ty = sema.analyzeAsType(block, src, builtin_inst) catch |err| switch (err) { 36743 error.AnalysisFail => std.debug.panic("std.builtin is corrupt", .{}), 36744 else => |e| return e, 36745 }; 36746 const decl_index = (try sema.namespaceLookup( 36747 block, 36748 src, 36749 builtin_ty.getNamespaceIndex(mod).unwrap().?, 36750 try ip.getOrPutString(gpa, name), 36751 )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name}); 36752 return decl_index; 36753 } 36754 36755 fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type { 36756 const mod = sema.mod; 36757 const ty_inst = try sema.getBuiltin(name); 36758 36759 var block: Block = .{ 36760 .parent = null, 36761 .sema = sema, 36762 .src_decl = sema.owner_decl_index, 36763 .namespace = sema.owner_decl.src_namespace, 36764 .wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope), 36765 .instructions = .{}, 36766 .inlining = null, 36767 .is_comptime = true, 36768 }; 36769 defer block.instructions.deinit(sema.gpa); 36770 const src = LazySrcLoc.nodeOffset(0); 36771 36772 const result_ty = sema.analyzeAsType(&block, src, ty_inst) catch |err| switch (err) { 36773 error.AnalysisFail => std.debug.panic("std.builtin.{s} is corrupt", .{name}), 36774 else => |e| return e, 36775 }; 36776 try sema.resolveTypeFully(result_ty); // Should not fail 36777 return result_ty; 36778 } 36779 36780 /// There is another implementation of this in `Type.onePossibleValue`. This one 36781 /// in `Sema` is for calling during semantic analysis, and performs field resolution 36782 /// to get the answer. The one in `Type` is for calling during codegen and asserts 36783 /// that the types are already resolved. 36784 /// TODO assert the return value matches `ty.onePossibleValue` 36785 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { 36786 const mod = sema.mod; 36787 const ip = &mod.intern_pool; 36788 return switch (ty.toIntern()) { 36789 .u0_type, 36790 .i0_type, 36791 => try mod.intValue(ty, 0), 36792 .u1_type, 36793 .u8_type, 36794 .i8_type, 36795 .u16_type, 36796 .i16_type, 36797 .u29_type, 36798 .u32_type, 36799 .i32_type, 36800 .u64_type, 36801 .i64_type, 36802 .u80_type, 36803 .u128_type, 36804 .i128_type, 36805 .usize_type, 36806 .isize_type, 36807 .c_char_type, 36808 .c_short_type, 36809 .c_ushort_type, 36810 .c_int_type, 36811 .c_uint_type, 36812 .c_long_type, 36813 .c_ulong_type, 36814 .c_longlong_type, 36815 .c_ulonglong_type, 36816 .c_longdouble_type, 36817 .f16_type, 36818 .f32_type, 36819 .f64_type, 36820 .f80_type, 36821 .f128_type, 36822 .anyopaque_type, 36823 .bool_type, 36824 .type_type, 36825 .anyerror_type, 36826 .adhoc_inferred_error_set_type, 36827 .comptime_int_type, 36828 .comptime_float_type, 36829 .enum_literal_type, 36830 .atomic_order_type, 36831 .atomic_rmw_op_type, 36832 .calling_convention_type, 36833 .address_space_type, 36834 .float_mode_type, 36835 .reduce_op_type, 36836 .call_modifier_type, 36837 .prefetch_options_type, 36838 .export_options_type, 36839 .extern_options_type, 36840 .type_info_type, 36841 .manyptr_u8_type, 36842 .manyptr_const_u8_type, 36843 .manyptr_const_u8_sentinel_0_type, 36844 .single_const_pointer_to_comptime_int_type, 36845 .slice_const_u8_type, 36846 .slice_const_u8_sentinel_0_type, 36847 .anyerror_void_error_union_type, 36848 => null, 36849 .void_type => Value.void, 36850 .noreturn_type => Value.@"unreachable", 36851 .anyframe_type => unreachable, 36852 .null_type => Value.null, 36853 .undefined_type => Value.undef, 36854 .optional_noreturn_type => try mod.nullValue(ty), 36855 .generic_poison_type => error.GenericPoison, 36856 .empty_struct_type => Value.empty_struct, 36857 // values, not types 36858 .undef, 36859 .zero, 36860 .zero_usize, 36861 .zero_u8, 36862 .one, 36863 .one_usize, 36864 .one_u8, 36865 .four_u8, 36866 .negative_one, 36867 .calling_convention_c, 36868 .calling_convention_inline, 36869 .void_value, 36870 .unreachable_value, 36871 .null_value, 36872 .bool_true, 36873 .bool_false, 36874 .empty_struct, 36875 .generic_poison, 36876 // invalid 36877 .var_args_param_type, 36878 .none, 36879 => unreachable, 36880 36881 _ => switch (ip.items.items(.tag)[@intFromEnum(ty.toIntern())]) { 36882 .type_int_signed, // i0 handled above 36883 .type_int_unsigned, // u0 handled above 36884 .type_pointer, 36885 .type_slice, 36886 .type_optional, // ?noreturn handled above 36887 .type_anyframe, 36888 .type_error_union, 36889 .type_anyerror_union, 36890 .type_error_set, 36891 .type_inferred_error_set, 36892 .type_opaque, 36893 .type_function, 36894 => null, 36895 36896 .simple_type, // handled above 36897 // values, not types 36898 .undef, 36899 .simple_value, 36900 .ptr_decl, 36901 .ptr_anon_decl, 36902 .ptr_anon_decl_aligned, 36903 .ptr_mut_decl, 36904 .ptr_comptime_field, 36905 .ptr_int, 36906 .ptr_eu_payload, 36907 .ptr_opt_payload, 36908 .ptr_elem, 36909 .ptr_field, 36910 .ptr_slice, 36911 .opt_payload, 36912 .opt_null, 36913 .int_u8, 36914 .int_u16, 36915 .int_u32, 36916 .int_i32, 36917 .int_usize, 36918 .int_comptime_int_u32, 36919 .int_comptime_int_i32, 36920 .int_small, 36921 .int_positive, 36922 .int_negative, 36923 .int_lazy_align, 36924 .int_lazy_size, 36925 .error_set_error, 36926 .error_union_error, 36927 .error_union_payload, 36928 .enum_literal, 36929 .enum_tag, 36930 .float_f16, 36931 .float_f32, 36932 .float_f64, 36933 .float_f80, 36934 .float_f128, 36935 .float_c_longdouble_f80, 36936 .float_c_longdouble_f128, 36937 .float_comptime_float, 36938 .variable, 36939 .extern_func, 36940 .func_decl, 36941 .func_instance, 36942 .func_coerced, 36943 .only_possible_value, 36944 .union_value, 36945 .bytes, 36946 .aggregate, 36947 .repeated, 36948 // memoized value, not types 36949 .memoized_call, 36950 => unreachable, 36951 36952 .type_array_big, 36953 .type_array_small, 36954 .type_vector, 36955 .type_enum_auto, 36956 .type_enum_explicit, 36957 .type_enum_nonexhaustive, 36958 .type_struct, 36959 .type_struct_ns, 36960 .type_struct_anon, 36961 .type_struct_packed, 36962 .type_struct_packed_inits, 36963 .type_tuple_anon, 36964 .type_union, 36965 => switch (ip.indexToKey(ty.toIntern())) { 36966 inline .array_type, .vector_type => |seq_type, seq_tag| { 36967 const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; 36968 if (seq_type.len + @intFromBool(has_sentinel) == 0) return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 36969 .ty = ty.toIntern(), 36970 .storage = .{ .elems = &.{} }, 36971 } }))); 36972 36973 if (try sema.typeHasOnePossibleValue(Type.fromInterned(seq_type.child))) |opv| { 36974 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 36975 .ty = ty.toIntern(), 36976 .storage = .{ .repeated_elem = opv.toIntern() }, 36977 } }))); 36978 } 36979 return null; 36980 }, 36981 36982 .struct_type => |struct_type| { 36983 try sema.resolveTypeFields(ty); 36984 36985 if (struct_type.field_types.len == 0) { 36986 // In this case the struct has no fields at all and 36987 // therefore has one possible value. 36988 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 36989 .ty = ty.toIntern(), 36990 .storage = .{ .elems = &.{} }, 36991 } }))); 36992 } 36993 36994 const field_vals = try sema.arena.alloc( 36995 InternPool.Index, 36996 struct_type.field_types.len, 36997 ); 36998 for (field_vals, 0..) |*field_val, i| { 36999 if (struct_type.fieldIsComptime(ip, i)) { 37000 try sema.resolveStructFieldInits(ty); 37001 field_val.* = struct_type.field_inits.get(ip)[i]; 37002 continue; 37003 } 37004 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 37005 if (field_ty.eql(ty, mod)) { 37006 const msg = try Module.ErrorMsg.create( 37007 sema.gpa, 37008 mod.declPtr(struct_type.decl.unwrap().?).srcLoc(mod), 37009 "struct '{}' depends on itself", 37010 .{ty.fmt(mod)}, 37011 ); 37012 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); 37013 return sema.failWithOwnedErrorMsg(null, msg); 37014 } 37015 if (try sema.typeHasOnePossibleValue(field_ty)) |field_opv| { 37016 field_val.* = try field_opv.intern(field_ty, mod); 37017 } else return null; 37018 } 37019 37020 // In this case the struct has no runtime-known fields and 37021 // therefore has one possible value. 37022 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37023 .ty = ty.toIntern(), 37024 .storage = .{ .elems = field_vals }, 37025 } }))); 37026 }, 37027 37028 .anon_struct_type => |tuple| { 37029 for (tuple.values.get(ip)) |val| { 37030 if (val == .none) return null; 37031 } 37032 // In this case the struct has all comptime-known fields and 37033 // therefore has one possible value. 37034 // TODO: write something like getCoercedInts to avoid needing to dupe 37035 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37036 .ty = ty.toIntern(), 37037 .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values.get(ip)) }, 37038 } }))); 37039 }, 37040 37041 .union_type => |union_type| { 37042 try sema.resolveTypeFields(ty); 37043 const union_obj = ip.loadUnionType(union_type); 37044 const tag_val = (try sema.typeHasOnePossibleValue(Type.fromInterned(union_obj.enum_tag_ty))) orelse 37045 return null; 37046 if (union_obj.field_types.len == 0) { 37047 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); 37048 return Value.fromInterned(only); 37049 } 37050 const only_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]); 37051 if (only_field_ty.eql(ty, mod)) { 37052 const msg = try Module.ErrorMsg.create( 37053 sema.gpa, 37054 mod.declPtr(union_obj.decl).srcLoc(mod), 37055 "union '{}' depends on itself", 37056 .{ty.fmt(mod)}, 37057 ); 37058 try sema.addFieldErrNote(ty, 0, msg, "while checking this field", .{}); 37059 return sema.failWithOwnedErrorMsg(null, msg); 37060 } 37061 const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse 37062 return null; 37063 const only = try mod.intern(.{ .un = .{ 37064 .ty = ty.toIntern(), 37065 .tag = tag_val.toIntern(), 37066 .val = val_val.toIntern(), 37067 } }); 37068 return Value.fromInterned(only); 37069 }, 37070 37071 .enum_type => |enum_type| switch (enum_type.tag_mode) { 37072 .nonexhaustive => { 37073 if (enum_type.tag_ty == .comptime_int_type) return null; 37074 37075 if (try sema.typeHasOnePossibleValue(Type.fromInterned(enum_type.tag_ty))) |int_opv| { 37076 const only = try mod.intern(.{ .enum_tag = .{ 37077 .ty = ty.toIntern(), 37078 .int = int_opv.toIntern(), 37079 } }); 37080 return Value.fromInterned(only); 37081 } 37082 37083 return null; 37084 }, 37085 .auto, .explicit => { 37086 if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(mod)) return null; 37087 37088 return Value.fromInterned(switch (enum_type.names.len) { 37089 0 => try mod.intern(.{ .empty_enum_value = ty.toIntern() }), 37090 1 => try mod.intern(.{ .enum_tag = .{ 37091 .ty = ty.toIntern(), 37092 .int = if (enum_type.values.len == 0) 37093 (try mod.intValue(Type.fromInterned(enum_type.tag_ty), 0)).toIntern() 37094 else 37095 try mod.intern_pool.getCoercedInts( 37096 mod.gpa, 37097 mod.intern_pool.indexToKey(enum_type.values.get(ip)[0]).int, 37098 enum_type.tag_ty, 37099 ), 37100 } }), 37101 else => return null, 37102 }); 37103 }, 37104 }, 37105 37106 else => unreachable, 37107 }, 37108 }, 37109 }; 37110 } 37111 37112 /// Returns the type of the AIR instruction. 37113 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type { 37114 return sema.getTmpAir().typeOf(inst, &sema.mod.intern_pool); 37115 } 37116 37117 pub fn getTmpAir(sema: Sema) Air { 37118 return .{ 37119 .instructions = sema.air_instructions.slice(), 37120 .extra = sema.air_extra.items, 37121 }; 37122 } 37123 37124 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 { 37125 const fields = std.meta.fields(@TypeOf(extra)); 37126 try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len); 37127 return sema.addExtraAssumeCapacity(extra); 37128 } 37129 37130 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { 37131 const fields = std.meta.fields(@TypeOf(extra)); 37132 const result: u32 = @intCast(sema.air_extra.items.len); 37133 inline for (fields) |field| { 37134 sema.air_extra.appendAssumeCapacity(switch (field.type) { 37135 u32 => @field(extra, field.name), 37136 i32 => @bitCast(@field(extra, field.name)), 37137 Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(extra, field.name)), 37138 else => @compileError("bad field type: " ++ @typeName(field.type)), 37139 }); 37140 } 37141 return result; 37142 } 37143 37144 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void { 37145 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(refs)); 37146 } 37147 37148 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index { 37149 const air_datas = sema.air_instructions.items(.data); 37150 const air_tags = sema.air_instructions.items(.tag); 37151 switch (air_tags[@intFromEnum(inst_index)]) { 37152 .br => return air_datas[@intFromEnum(inst_index)].br.block_inst, 37153 else => return null, 37154 } 37155 } 37156 37157 fn isComptimeKnown( 37158 sema: *Sema, 37159 inst: Air.Inst.Ref, 37160 ) !bool { 37161 return (try sema.resolveValue(inst)) != null; 37162 } 37163 37164 fn analyzeComptimeAlloc( 37165 sema: *Sema, 37166 block: *Block, 37167 var_type: Type, 37168 alignment: Alignment, 37169 ) CompileError!Air.Inst.Ref { 37170 const mod = sema.mod; 37171 37172 // Needed to make an anon decl with type `var_type` (the `finish()` call below). 37173 _ = try sema.typeHasOnePossibleValue(var_type); 37174 37175 const ptr_type = try sema.ptrType(.{ 37176 .child = var_type.toIntern(), 37177 .flags = .{ 37178 .alignment = alignment, 37179 .address_space = target_util.defaultAddressSpace(mod.getTarget(), .global_constant), 37180 }, 37181 }); 37182 37183 var anon_decl = try block.startAnonDecl(); // TODO: comptime value mutation without Decl 37184 defer anon_decl.deinit(); 37185 37186 const decl_index = try anon_decl.finish( 37187 var_type, 37188 // There will be stores before the first load, but they may be to sub-elements or 37189 // sub-fields. So we need to initialize with undef to allow the mechanism to expand 37190 // into fields/elements and have those overridden with stored values. 37191 Value.fromInterned((try mod.intern(.{ .undef = var_type.toIntern() }))), 37192 alignment, 37193 ); 37194 const decl = mod.declPtr(decl_index); 37195 decl.alignment = alignment; 37196 37197 try sema.comptime_mutable_decls.append(decl_index); 37198 return Air.internedToRef((try mod.intern(.{ .ptr = .{ 37199 .ty = ptr_type.toIntern(), 37200 .addr = .{ .mut_decl = .{ 37201 .decl = decl_index, 37202 .runtime_index = block.runtime_index, 37203 } }, 37204 } }))); 37205 } 37206 37207 /// The places where a user can specify an address space attribute 37208 pub const AddressSpaceContext = enum { 37209 /// A function is specified to be placed in a certain address space. 37210 function, 37211 37212 /// A (global) variable is specified to be placed in a certain address space. 37213 /// In contrast to .constant, these values (and thus the address space they will be 37214 /// placed in) are required to be mutable. 37215 variable, 37216 37217 /// A (global) constant value is specified to be placed in a certain address space. 37218 /// In contrast to .variable, values placed in this address space are not required to be mutable. 37219 constant, 37220 37221 /// A pointer is ascripted to point into a certain address space. 37222 pointer, 37223 }; 37224 37225 pub fn analyzeAddressSpace( 37226 sema: *Sema, 37227 block: *Block, 37228 src: LazySrcLoc, 37229 zir_ref: Zir.Inst.Ref, 37230 ctx: AddressSpaceContext, 37231 ) !std.builtin.AddressSpace { 37232 const mod = sema.mod; 37233 const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, .{ 37234 .needed_comptime_reason = "address space must be comptime-known", 37235 }); 37236 const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); 37237 const target = sema.mod.getTarget(); 37238 const arch = target.cpu.arch; 37239 37240 const is_nv = arch == .nvptx or arch == .nvptx64; 37241 const is_amd = arch == .amdgcn; 37242 const is_spirv = arch == .spirv32 or arch == .spirv64; 37243 const is_gpu = is_nv or is_amd or is_spirv; 37244 37245 const supported = switch (address_space) { 37246 // TODO: on spir-v only when os is opencl. 37247 .generic => true, 37248 .gs, .fs, .ss => (arch == .x86 or arch == .x86_64) and ctx == .pointer, 37249 // TODO: check that .shared and .local are left uninitialized 37250 .param => is_nv, 37251 .global, .shared, .local => is_gpu, 37252 .constant => is_gpu and (ctx == .constant), 37253 // TODO this should also check how many flash banks the cpu has 37254 .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr, 37255 }; 37256 37257 if (!supported) { 37258 // TODO error messages could be made more elaborate here 37259 const entity = switch (ctx) { 37260 .function => "functions", 37261 .variable => "mutable values", 37262 .constant => "constant values", 37263 .pointer => "pointers", 37264 }; 37265 return sema.fail( 37266 block, 37267 src, 37268 "{s} with address space '{s}' are not supported on {s}", 37269 .{ entity, @tagName(address_space), arch.genericName() }, 37270 ); 37271 } 37272 37273 return address_space; 37274 } 37275 37276 /// Asserts the value is a pointer and dereferences it. 37277 /// Returns `null` if the pointer contents cannot be loaded at comptime. 37278 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { 37279 const mod = sema.mod; 37280 const load_ty = ptr_ty.childType(mod); 37281 const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty); 37282 switch (res) { 37283 .runtime_load => return null, 37284 .val => |v| return v, 37285 .needed_well_defined => |ty| return sema.fail( 37286 block, 37287 src, 37288 "comptime dereference requires '{}' to have a well-defined layout, but it does not.", 37289 .{ty.fmt(sema.mod)}, 37290 ), 37291 .out_of_bounds => |ty| return sema.fail( 37292 block, 37293 src, 37294 "dereference of '{}' exceeds bounds of containing decl of type '{}'", 37295 .{ ptr_ty.fmt(sema.mod), ty.fmt(sema.mod) }, 37296 ), 37297 } 37298 } 37299 37300 const DerefResult = union(enum) { 37301 runtime_load, 37302 val: Value, 37303 needed_well_defined: Type, 37304 out_of_bounds: Type, 37305 }; 37306 37307 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type) CompileError!DerefResult { 37308 const mod = sema.mod; 37309 const target = mod.getTarget(); 37310 const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) { 37311 error.RuntimeLoad => return DerefResult{ .runtime_load = {} }, 37312 else => |e| return e, 37313 }; 37314 37315 if (deref.pointee) |tv| { 37316 const coerce_in_mem_ok = 37317 (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or 37318 (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok; 37319 if (coerce_in_mem_ok) { 37320 // We have a Value that lines up in virtual memory exactly with what we want to load, 37321 // and it is in-memory coercible to load_ty. It may be returned without modifications. 37322 // Move mutable decl values to the InternPool and assert other decls are already in 37323 // the InternPool. 37324 const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern(); 37325 const coerced_val = try mod.getCoerced(Value.fromInterned(uncoerced_val), load_ty); 37326 return .{ .val = coerced_val }; 37327 } 37328 } 37329 37330 // The type is not in-memory coercible or the direct dereference failed, so it must 37331 // be bitcast according to the pointer type we are performing the load through. 37332 if (!load_ty.hasWellDefinedLayout(mod)) { 37333 return DerefResult{ .needed_well_defined = load_ty }; 37334 } 37335 37336 const load_sz = try sema.typeAbiSize(load_ty); 37337 37338 // Try the smaller bit-cast first, since that's more efficient than using the larger `parent` 37339 if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty)) 37340 return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load }; 37341 37342 // If that fails, try to bit-cast from the largest parent value with a well-defined layout 37343 if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty)) 37344 return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load }; 37345 37346 if (deref.ty_without_well_defined_layout) |bad_ty| { 37347 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem 37348 // is that some type we encountered when de-referencing does not have a well-defined layout. 37349 return DerefResult{ .needed_well_defined = bad_ty }; 37350 } else { 37351 // If all encountered types had well-defined layouts, the parent is the root decl and it just 37352 // wasn't big enough for the load. 37353 return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty }; 37354 } 37355 } 37356 37357 /// Used to convert a u64 value to a usize value, emitting a compile error if the number 37358 /// is too big to fit. 37359 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { 37360 if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; 37361 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}); 37362 } 37363 37364 /// For pointer-like optionals, it returns the pointer type. For pointers, 37365 /// the type is returned unmodified. 37366 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether 37367 /// a type has zero bits, which can cause a "foo depends on itself" compile error. 37368 /// This logic must be kept in sync with `Type.isPtrLikeOptional`. 37369 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { 37370 const mod = sema.mod; 37371 return switch (mod.intern_pool.indexToKey(ty.toIntern())) { 37372 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 37373 .One, .Many, .C => ty, 37374 .Slice => null, 37375 }, 37376 .opt_type => |opt_child| switch (mod.intern_pool.indexToKey(opt_child)) { 37377 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 37378 .Slice, .C => null, 37379 .Many, .One => { 37380 if (ptr_type.flags.is_allowzero) return null; 37381 37382 // optionals of zero sized types behave like bools, not pointers 37383 const payload_ty = Type.fromInterned(opt_child); 37384 if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) { 37385 return null; 37386 } 37387 37388 return payload_ty; 37389 }, 37390 }, 37391 else => null, 37392 }, 37393 else => null, 37394 }; 37395 } 37396 37397 /// `generic_poison` will return false. 37398 /// May return false negatives when structs and unions are having their field types resolved. 37399 pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { 37400 return ty.comptimeOnlyAdvanced(sema.mod, sema); 37401 } 37402 37403 pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { 37404 const mod = sema.mod; 37405 return ty.hasRuntimeBitsAdvanced(mod, false, .{ .sema = sema }) catch |err| switch (err) { 37406 error.NeedLazy => unreachable, 37407 else => |e| return e, 37408 }; 37409 } 37410 37411 fn typeAbiSize(sema: *Sema, ty: Type) !u64 { 37412 try sema.resolveTypeLayout(ty); 37413 return ty.abiSize(sema.mod); 37414 } 37415 37416 fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!Alignment { 37417 return (try ty.abiAlignmentAdvanced(sema.mod, .{ .sema = sema })).scalar; 37418 } 37419 37420 /// Not valid to call for packed unions. 37421 /// Keep implementation in sync with `Module.unionFieldNormalAlignment`. 37422 fn unionFieldAlignment(sema: *Sema, u: InternPool.UnionType, field_index: u32) !Alignment { 37423 const mod = sema.mod; 37424 const ip = &mod.intern_pool; 37425 const field_align = u.fieldAlign(ip, field_index); 37426 if (field_align != .none) return field_align; 37427 const field_ty = Type.fromInterned(u.field_types.get(ip)[field_index]); 37428 if (field_ty.isNoReturn(sema.mod)) return .none; 37429 return sema.typeAbiAlignment(field_ty); 37430 } 37431 37432 /// Keep implementation in sync with `Module.structFieldAlignment`. 37433 fn structFieldAlignment( 37434 sema: *Sema, 37435 explicit_alignment: InternPool.Alignment, 37436 field_ty: Type, 37437 layout: std.builtin.Type.ContainerLayout, 37438 ) !Alignment { 37439 if (explicit_alignment != .none) 37440 return explicit_alignment; 37441 const mod = sema.mod; 37442 switch (layout) { 37443 .Packed => return .none, 37444 .Auto => if (mod.getTarget().ofmt != .c) return sema.typeAbiAlignment(field_ty), 37445 .Extern => {}, 37446 } 37447 // extern 37448 const ty_abi_align = try sema.typeAbiAlignment(field_ty); 37449 if (field_ty.isAbiInt(mod) and field_ty.intInfo(mod).bits >= 128) { 37450 return ty_abi_align.maxStrict(.@"16"); 37451 } 37452 return ty_abi_align; 37453 } 37454 37455 pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { 37456 return ty.fnHasRuntimeBitsAdvanced(sema.mod, sema); 37457 } 37458 37459 fn unionFieldIndex( 37460 sema: *Sema, 37461 block: *Block, 37462 union_ty: Type, 37463 field_name: InternPool.NullTerminatedString, 37464 field_src: LazySrcLoc, 37465 ) !u32 { 37466 const mod = sema.mod; 37467 const ip = &mod.intern_pool; 37468 try sema.resolveTypeFields(union_ty); 37469 const union_obj = mod.typeToUnion(union_ty).?; 37470 const field_index = union_obj.nameIndex(ip, field_name) orelse 37471 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); 37472 return @intCast(field_index); 37473 } 37474 37475 fn structFieldIndex( 37476 sema: *Sema, 37477 block: *Block, 37478 struct_ty: Type, 37479 field_name: InternPool.NullTerminatedString, 37480 field_src: LazySrcLoc, 37481 ) !u32 { 37482 const mod = sema.mod; 37483 const ip = &mod.intern_pool; 37484 try sema.resolveTypeFields(struct_ty); 37485 if (struct_ty.isAnonStruct(mod)) { 37486 return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src); 37487 } else { 37488 const struct_type = mod.typeToStruct(struct_ty).?; 37489 return struct_type.nameIndex(ip, field_name) orelse 37490 return sema.failWithBadStructFieldAccess(block, struct_type, field_src, field_name); 37491 } 37492 } 37493 37494 fn anonStructFieldIndex( 37495 sema: *Sema, 37496 block: *Block, 37497 struct_ty: Type, 37498 field_name: InternPool.NullTerminatedString, 37499 field_src: LazySrcLoc, 37500 ) !u32 { 37501 const mod = sema.mod; 37502 const ip = &mod.intern_pool; 37503 switch (ip.indexToKey(struct_ty.toIntern())) { 37504 .anon_struct_type => |anon_struct_type| for (anon_struct_type.names.get(ip), 0..) |name, i| { 37505 if (name == field_name) return @intCast(i); 37506 }, 37507 .struct_type => |struct_type| if (struct_type.nameIndex(ip, field_name)) |i| return i, 37508 else => unreachable, 37509 } 37510 return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{ 37511 field_name.fmt(ip), struct_ty.fmt(sema.mod), 37512 }); 37513 } 37514 37515 fn queueFullTypeResolution(sema: *Sema, ty: Type) !void { 37516 try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {}); 37517 } 37518 37519 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting 37520 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar). 37521 fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value { 37522 var overflow: usize = undefined; 37523 return sema.intAddInner(lhs, rhs, ty, &overflow) catch |err| switch (err) { 37524 error.Overflow => { 37525 const is_vec = ty.isVector(sema.mod); 37526 overflow_idx.* = if (is_vec) overflow else 0; 37527 const safe_ty = if (is_vec) try sema.mod.vectorType(.{ 37528 .len = ty.vectorLen(sema.mod), 37529 .child = .comptime_int_type, 37530 }) else Type.comptime_int; 37531 return sema.intAddInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) { 37532 error.Overflow => unreachable, 37533 else => |e| return e, 37534 }; 37535 }, 37536 else => |e| return e, 37537 }; 37538 } 37539 37540 fn intAddInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value { 37541 const mod = sema.mod; 37542 if (ty.zigTypeTag(mod) == .Vector) { 37543 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 37544 const scalar_ty = ty.scalarType(mod); 37545 for (result_data, 0..) |*scalar, i| { 37546 const lhs_elem = try lhs.elemValue(mod, i); 37547 const rhs_elem = try rhs.elemValue(mod, i); 37548 const val = sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) { 37549 error.Overflow => { 37550 overflow_idx.* = i; 37551 return error.Overflow; 37552 }, 37553 else => |e| return e, 37554 }; 37555 scalar.* = try val.intern(scalar_ty, mod); 37556 } 37557 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37558 .ty = ty.toIntern(), 37559 .storage = .{ .elems = result_data }, 37560 } }))); 37561 } 37562 return sema.intAddScalar(lhs, rhs, ty); 37563 } 37564 37565 fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value { 37566 const mod = sema.mod; 37567 if (scalar_ty.toIntern() != .comptime_int_type) { 37568 const res = try sema.intAddWithOverflowScalar(lhs, rhs, scalar_ty); 37569 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow; 37570 return res.wrapped_result; 37571 } 37572 // TODO is this a performance issue? maybe we should try the operation without 37573 // resorting to BigInt first. 37574 var lhs_space: Value.BigIntSpace = undefined; 37575 var rhs_space: Value.BigIntSpace = undefined; 37576 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 37577 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 37578 const limbs = try sema.arena.alloc( 37579 std.math.big.Limb, 37580 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, 37581 ); 37582 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 37583 result_bigint.add(lhs_bigint, rhs_bigint); 37584 return mod.intValue_big(scalar_ty, result_bigint.toConst()); 37585 } 37586 37587 /// Supports both floats and ints; handles undefined. 37588 fn numberAddWrapScalar( 37589 sema: *Sema, 37590 lhs: Value, 37591 rhs: Value, 37592 ty: Type, 37593 ) !Value { 37594 const mod = sema.mod; 37595 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; 37596 37597 if (ty.zigTypeTag(mod) == .ComptimeInt) { 37598 return sema.intAdd(lhs, rhs, ty, undefined); 37599 } 37600 37601 if (ty.isAnyFloat()) { 37602 return Value.floatAdd(lhs, rhs, ty, sema.arena, mod); 37603 } 37604 37605 const overflow_result = try sema.intAddWithOverflow(lhs, rhs, ty); 37606 return overflow_result.wrapped_result; 37607 } 37608 37609 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting 37610 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar). 37611 fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value { 37612 var overflow: usize = undefined; 37613 return sema.intSubInner(lhs, rhs, ty, &overflow) catch |err| switch (err) { 37614 error.Overflow => { 37615 const is_vec = ty.isVector(sema.mod); 37616 overflow_idx.* = if (is_vec) overflow else 0; 37617 const safe_ty = if (is_vec) try sema.mod.vectorType(.{ 37618 .len = ty.vectorLen(sema.mod), 37619 .child = .comptime_int_type, 37620 }) else Type.comptime_int; 37621 return sema.intSubInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) { 37622 error.Overflow => unreachable, 37623 else => |e| return e, 37624 }; 37625 }, 37626 else => |e| return e, 37627 }; 37628 } 37629 37630 fn intSubInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value { 37631 const mod = sema.mod; 37632 if (ty.zigTypeTag(mod) == .Vector) { 37633 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 37634 const scalar_ty = ty.scalarType(mod); 37635 for (result_data, 0..) |*scalar, i| { 37636 const lhs_elem = try lhs.elemValue(sema.mod, i); 37637 const rhs_elem = try rhs.elemValue(sema.mod, i); 37638 const val = sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) { 37639 error.Overflow => { 37640 overflow_idx.* = i; 37641 return error.Overflow; 37642 }, 37643 else => |e| return e, 37644 }; 37645 scalar.* = try val.intern(scalar_ty, mod); 37646 } 37647 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37648 .ty = ty.toIntern(), 37649 .storage = .{ .elems = result_data }, 37650 } }))); 37651 } 37652 return sema.intSubScalar(lhs, rhs, ty); 37653 } 37654 37655 fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value { 37656 const mod = sema.mod; 37657 if (scalar_ty.toIntern() != .comptime_int_type) { 37658 const res = try sema.intSubWithOverflowScalar(lhs, rhs, scalar_ty); 37659 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow; 37660 return res.wrapped_result; 37661 } 37662 // TODO is this a performance issue? maybe we should try the operation without 37663 // resorting to BigInt first. 37664 var lhs_space: Value.BigIntSpace = undefined; 37665 var rhs_space: Value.BigIntSpace = undefined; 37666 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 37667 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 37668 const limbs = try sema.arena.alloc( 37669 std.math.big.Limb, 37670 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, 37671 ); 37672 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 37673 result_bigint.sub(lhs_bigint, rhs_bigint); 37674 return mod.intValue_big(scalar_ty, result_bigint.toConst()); 37675 } 37676 37677 /// Supports both floats and ints; handles undefined. 37678 fn numberSubWrapScalar( 37679 sema: *Sema, 37680 lhs: Value, 37681 rhs: Value, 37682 ty: Type, 37683 ) !Value { 37684 const mod = sema.mod; 37685 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; 37686 37687 if (ty.zigTypeTag(mod) == .ComptimeInt) { 37688 return sema.intSub(lhs, rhs, ty, undefined); 37689 } 37690 37691 if (ty.isAnyFloat()) { 37692 return Value.floatSub(lhs, rhs, ty, sema.arena, mod); 37693 } 37694 37695 const overflow_result = try sema.intSubWithOverflow(lhs, rhs, ty); 37696 return overflow_result.wrapped_result; 37697 } 37698 37699 fn intSubWithOverflow( 37700 sema: *Sema, 37701 lhs: Value, 37702 rhs: Value, 37703 ty: Type, 37704 ) !Value.OverflowArithmeticResult { 37705 const mod = sema.mod; 37706 if (ty.zigTypeTag(mod) == .Vector) { 37707 const vec_len = ty.vectorLen(mod); 37708 const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len); 37709 const result_data = try sema.arena.alloc(InternPool.Index, vec_len); 37710 const scalar_ty = ty.scalarType(mod); 37711 for (overflowed_data, result_data, 0..) |*of, *scalar, i| { 37712 const lhs_elem = try lhs.elemValue(sema.mod, i); 37713 const rhs_elem = try rhs.elemValue(sema.mod, i); 37714 const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty); 37715 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod); 37716 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod); 37717 } 37718 return Value.OverflowArithmeticResult{ 37719 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37720 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(), 37721 .storage = .{ .elems = overflowed_data }, 37722 } }))), 37723 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37724 .ty = ty.toIntern(), 37725 .storage = .{ .elems = result_data }, 37726 } }))), 37727 }; 37728 } 37729 return sema.intSubWithOverflowScalar(lhs, rhs, ty); 37730 } 37731 37732 fn intSubWithOverflowScalar( 37733 sema: *Sema, 37734 lhs: Value, 37735 rhs: Value, 37736 ty: Type, 37737 ) !Value.OverflowArithmeticResult { 37738 const mod = sema.mod; 37739 const info = ty.intInfo(mod); 37740 37741 var lhs_space: Value.BigIntSpace = undefined; 37742 var rhs_space: Value.BigIntSpace = undefined; 37743 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 37744 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 37745 const limbs = try sema.arena.alloc( 37746 std.math.big.Limb, 37747 std.math.big.int.calcTwosCompLimbCount(info.bits), 37748 ); 37749 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 37750 const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); 37751 const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()); 37752 return Value.OverflowArithmeticResult{ 37753 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)), 37754 .wrapped_result = wrapped_result, 37755 }; 37756 } 37757 37758 const IntFromFloatMode = enum { exact, truncate }; 37759 37760 fn intFromFloat( 37761 sema: *Sema, 37762 block: *Block, 37763 src: LazySrcLoc, 37764 val: Value, 37765 float_ty: Type, 37766 int_ty: Type, 37767 mode: IntFromFloatMode, 37768 ) CompileError!Value { 37769 const mod = sema.mod; 37770 if (float_ty.zigTypeTag(mod) == .Vector) { 37771 const elem_ty = float_ty.scalarType(mod); 37772 const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(mod)); 37773 const scalar_ty = int_ty.scalarType(mod); 37774 for (result_data, 0..) |*scalar, i| { 37775 const elem_val = try val.elemValue(sema.mod, i); 37776 scalar.* = try (try sema.intFromFloatScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod), mode)).intern(scalar_ty, mod); 37777 } 37778 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37779 .ty = int_ty.toIntern(), 37780 .storage = .{ .elems = result_data }, 37781 } }))); 37782 } 37783 return sema.intFromFloatScalar(block, src, val, float_ty, int_ty, mode); 37784 } 37785 37786 // float is expected to be finite and non-NaN 37787 fn float128IntPartToBigInt( 37788 arena: Allocator, 37789 float: f128, 37790 ) !std.math.big.int.Managed { 37791 const is_negative = std.math.signbit(float); 37792 const floored = @floor(@abs(float)); 37793 37794 var rational = try std.math.big.Rational.init(arena); 37795 defer rational.q.deinit(); 37796 rational.setFloat(f128, floored) catch |err| switch (err) { 37797 error.NonFiniteFloat => unreachable, 37798 error.OutOfMemory => return error.OutOfMemory, 37799 }; 37800 37801 // The float is reduced in rational.setFloat, so we assert that denominator is equal to one 37802 const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; 37803 assert(rational.q.toConst().eqlAbs(big_one)); 37804 37805 if (is_negative) { 37806 rational.negate(); 37807 } 37808 return rational.p; 37809 } 37810 37811 fn intFromFloatScalar( 37812 sema: *Sema, 37813 block: *Block, 37814 src: LazySrcLoc, 37815 val: Value, 37816 float_ty: Type, 37817 int_ty: Type, 37818 mode: IntFromFloatMode, 37819 ) CompileError!Value { 37820 const mod = sema.mod; 37821 37822 if (val.isUndef(mod)) return sema.failWithUseOfUndef(block, src); 37823 37824 if (mode == .exact and val.floatHasFraction(mod)) return sema.fail( 37825 block, 37826 src, 37827 "fractional component prevents float value '{}' from coercion to type '{}'", 37828 .{ val.fmtValue(float_ty, mod), int_ty.fmt(mod) }, 37829 ); 37830 37831 const float = val.toFloat(f128, mod); 37832 if (std.math.isNan(float)) { 37833 return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ 37834 int_ty.fmt(sema.mod), 37835 }); 37836 } 37837 if (std.math.isInf(float)) { 37838 return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{ 37839 int_ty.fmt(sema.mod), 37840 }); 37841 } 37842 37843 var big_int = try float128IntPartToBigInt(sema.arena, float); 37844 defer big_int.deinit(); 37845 37846 const cti_result = try mod.intValue_big(Type.comptime_int, big_int.toConst()); 37847 37848 if (!(try sema.intFitsInType(cti_result, int_ty, null))) { 37849 return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{ 37850 val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod), 37851 }); 37852 } 37853 return mod.getCoerced(cti_result, int_ty); 37854 } 37855 37856 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. 37857 /// Vectors are also accepted. Vector results are reduced with AND. 37858 /// 37859 /// If provided, `vector_index` reports the first element that failed the range check. 37860 fn intFitsInType( 37861 sema: *Sema, 37862 val: Value, 37863 ty: Type, 37864 vector_index: ?*usize, 37865 ) CompileError!bool { 37866 const mod = sema.mod; 37867 if (ty.toIntern() == .comptime_int_type) return true; 37868 const info = ty.intInfo(mod); 37869 switch (val.toIntern()) { 37870 .zero_usize, .zero_u8 => return true, 37871 else => switch (mod.intern_pool.indexToKey(val.toIntern())) { 37872 .undef => return true, 37873 .variable, .extern_func, .func, .ptr => { 37874 const target = mod.getTarget(); 37875 const ptr_bits = target.ptrBitWidth(); 37876 return switch (info.signedness) { 37877 .signed => info.bits > ptr_bits, 37878 .unsigned => info.bits >= ptr_bits, 37879 }; 37880 }, 37881 .int => |int| switch (int.storage) { 37882 .u64, .i64, .big_int => { 37883 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; 37884 const big_int = int.storage.toBigInt(&buffer); 37885 return big_int.fitsInTwosComp(info.signedness, info.bits); 37886 }, 37887 .lazy_align => |lazy_ty| { 37888 const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed); 37889 // If it is u16 or bigger we know the alignment fits without resolving it. 37890 if (info.bits >= max_needed_bits) return true; 37891 const x = try sema.typeAbiAlignment(Type.fromInterned(lazy_ty)); 37892 if (x == .none) return true; 37893 const actual_needed_bits = @as(usize, x.toLog2Units()) + 1 + @intFromBool(info.signedness == .signed); 37894 return info.bits >= actual_needed_bits; 37895 }, 37896 .lazy_size => |lazy_ty| { 37897 const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed); 37898 // If it is u64 or bigger we know the size fits without resolving it. 37899 if (info.bits >= max_needed_bits) return true; 37900 const x = try sema.typeAbiSize(Type.fromInterned(lazy_ty)); 37901 if (x == 0) return true; 37902 const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed); 37903 return info.bits >= actual_needed_bits; 37904 }, 37905 }, 37906 .aggregate => |aggregate| { 37907 assert(ty.zigTypeTag(mod) == .Vector); 37908 return switch (aggregate.storage) { 37909 .bytes => |bytes| for (bytes, 0..) |byte, i| { 37910 if (byte == 0) continue; 37911 const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); 37912 if (info.bits >= actual_needed_bits) continue; 37913 if (vector_index) |vi| vi.* = i; 37914 break false; 37915 } else true, 37916 .elems, .repeated_elem => for (switch (aggregate.storage) { 37917 .bytes => unreachable, 37918 .elems => |elems| elems, 37919 .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), 37920 }, 0..) |elem, i| { 37921 if (try sema.intFitsInType(Value.fromInterned(elem), ty.scalarType(mod), null)) continue; 37922 if (vector_index) |vi| vi.* = i; 37923 break false; 37924 } else true, 37925 }; 37926 }, 37927 else => unreachable, 37928 }, 37929 } 37930 } 37931 37932 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { 37933 const mod = sema.mod; 37934 if (!(try int_val.compareAllWithZeroAdvanced(.gte, sema))) return false; 37935 const end_val = try mod.intValue(tag_ty, end); 37936 if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false; 37937 return true; 37938 } 37939 37940 /// Asserts the type is an enum. 37941 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { 37942 const mod = sema.mod; 37943 const enum_type = mod.intern_pool.indexToKey(ty.toIntern()).enum_type; 37944 assert(enum_type.tag_mode != .nonexhaustive); 37945 // The `tagValueIndex` function call below relies on the type being the integer tag type. 37946 // `getCoerced` assumes the value will fit the new type. 37947 if (!(try sema.intFitsInType(int, Type.fromInterned(enum_type.tag_ty), null))) return false; 37948 const int_coerced = try mod.getCoerced(int, Type.fromInterned(enum_type.tag_ty)); 37949 37950 return enum_type.tagValueIndex(&mod.intern_pool, int_coerced.toIntern()) != null; 37951 } 37952 37953 fn intAddWithOverflow( 37954 sema: *Sema, 37955 lhs: Value, 37956 rhs: Value, 37957 ty: Type, 37958 ) !Value.OverflowArithmeticResult { 37959 const mod = sema.mod; 37960 if (ty.zigTypeTag(mod) == .Vector) { 37961 const vec_len = ty.vectorLen(mod); 37962 const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len); 37963 const result_data = try sema.arena.alloc(InternPool.Index, vec_len); 37964 const scalar_ty = ty.scalarType(mod); 37965 for (overflowed_data, result_data, 0..) |*of, *scalar, i| { 37966 const lhs_elem = try lhs.elemValue(sema.mod, i); 37967 const rhs_elem = try rhs.elemValue(sema.mod, i); 37968 const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty); 37969 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod); 37970 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod); 37971 } 37972 return Value.OverflowArithmeticResult{ 37973 .overflow_bit = Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37974 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(), 37975 .storage = .{ .elems = overflowed_data }, 37976 } }))), 37977 .wrapped_result = Value.fromInterned((try mod.intern(.{ .aggregate = .{ 37978 .ty = ty.toIntern(), 37979 .storage = .{ .elems = result_data }, 37980 } }))), 37981 }; 37982 } 37983 return sema.intAddWithOverflowScalar(lhs, rhs, ty); 37984 } 37985 37986 fn intAddWithOverflowScalar( 37987 sema: *Sema, 37988 lhs: Value, 37989 rhs: Value, 37990 ty: Type, 37991 ) !Value.OverflowArithmeticResult { 37992 const mod = sema.mod; 37993 const info = ty.intInfo(mod); 37994 37995 var lhs_space: Value.BigIntSpace = undefined; 37996 var rhs_space: Value.BigIntSpace = undefined; 37997 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 37998 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 37999 const limbs = try sema.arena.alloc( 38000 std.math.big.Limb, 38001 std.math.big.int.calcTwosCompLimbCount(info.bits), 38002 ); 38003 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 38004 const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); 38005 const result = try mod.intValue_big(ty, result_bigint.toConst()); 38006 return Value.OverflowArithmeticResult{ 38007 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)), 38008 .wrapped_result = result, 38009 }; 38010 } 38011 38012 /// Asserts the values are comparable. Both operands have type `ty`. 38013 /// For vectors, returns true if the comparison is true for ALL elements. 38014 /// 38015 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)` 38016 fn compareAll( 38017 sema: *Sema, 38018 lhs: Value, 38019 op: std.math.CompareOperator, 38020 rhs: Value, 38021 ty: Type, 38022 ) CompileError!bool { 38023 const mod = sema.mod; 38024 if (ty.zigTypeTag(mod) == .Vector) { 38025 var i: usize = 0; 38026 while (i < ty.vectorLen(mod)) : (i += 1) { 38027 const lhs_elem = try lhs.elemValue(sema.mod, i); 38028 const rhs_elem = try rhs.elemValue(sema.mod, i); 38029 if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)))) { 38030 return false; 38031 } 38032 } 38033 return true; 38034 } 38035 return sema.compareScalar(lhs, op, rhs, ty); 38036 } 38037 38038 /// Asserts the values are comparable. Both operands have type `ty`. 38039 fn compareScalar( 38040 sema: *Sema, 38041 lhs: Value, 38042 op: std.math.CompareOperator, 38043 rhs: Value, 38044 ty: Type, 38045 ) CompileError!bool { 38046 const mod = sema.mod; 38047 const coerced_lhs = try mod.getCoerced(lhs, ty); 38048 const coerced_rhs = try mod.getCoerced(rhs, ty); 38049 switch (op) { 38050 .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty), 38051 .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)), 38052 else => return Value.compareHeteroAdvanced(coerced_lhs, op, coerced_rhs, mod, sema), 38053 } 38054 } 38055 38056 fn valuesEqual( 38057 sema: *Sema, 38058 lhs: Value, 38059 rhs: Value, 38060 ty: Type, 38061 ) CompileError!bool { 38062 return lhs.eql(rhs, ty, sema.mod); 38063 } 38064 38065 /// Asserts the values are comparable vectors of type `ty`. 38066 fn compareVector( 38067 sema: *Sema, 38068 lhs: Value, 38069 op: std.math.CompareOperator, 38070 rhs: Value, 38071 ty: Type, 38072 ) !Value { 38073 const mod = sema.mod; 38074 assert(ty.zigTypeTag(mod) == .Vector); 38075 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 38076 for (result_data, 0..) |*scalar, i| { 38077 const lhs_elem = try lhs.elemValue(sema.mod, i); 38078 const rhs_elem = try rhs.elemValue(sema.mod, i); 38079 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)); 38080 scalar.* = try Value.makeBool(res_bool).intern(Type.bool, mod); 38081 } 38082 return Value.fromInterned((try mod.intern(.{ .aggregate = .{ 38083 .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .bool_type })).toIntern(), 38084 .storage = .{ .elems = result_data }, 38085 } }))); 38086 } 38087 38088 /// Returns the type of a pointer to an element. 38089 /// Asserts that the type is a pointer, and that the element type is indexable. 38090 /// For *[N]T, return *T 38091 /// For [*]T, returns *T 38092 /// For []T, returns *T 38093 /// Handles const-ness and address spaces in particular. 38094 /// This code is duplicated in `analyzePtrArithmetic`. 38095 fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { 38096 const mod = sema.mod; 38097 const ptr_info = ptr_ty.ptrInfo(mod); 38098 const elem_ty = ptr_ty.elemType2(mod); 38099 const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0; 38100 const parent_ty = ptr_ty.childType(mod); 38101 38102 const VI = InternPool.Key.PtrType.VectorIndex; 38103 38104 const vector_info: struct { 38105 host_size: u16 = 0, 38106 alignment: Alignment = .none, 38107 vector_index: VI = .none, 38108 } = if (parent_ty.isVector(mod) and ptr_info.flags.size == .One) blk: { 38109 const elem_bits = elem_ty.bitSize(mod); 38110 if (elem_bits == 0) break :blk .{}; 38111 const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits); 38112 if (!is_packed) break :blk .{}; 38113 38114 break :blk .{ 38115 .host_size = @intCast(parent_ty.arrayLen(mod)), 38116 .alignment = parent_ty.abiAlignment(mod), 38117 .vector_index = if (offset) |some| @enumFromInt(some) else .runtime, 38118 }; 38119 } else .{}; 38120 38121 const alignment: Alignment = a: { 38122 // Calculate the new pointer alignment. 38123 if (ptr_info.flags.alignment == .none) { 38124 // In case of an ABI-aligned pointer, any pointer arithmetic 38125 // maintains the same ABI-alignedness. 38126 break :a vector_info.alignment; 38127 } 38128 // If the addend is not a comptime-known value we can still count on 38129 // it being a multiple of the type size. 38130 const elem_size = try sema.typeAbiSize(elem_ty); 38131 const addend = if (offset) |off| elem_size * off else elem_size; 38132 38133 // The resulting pointer is aligned to the lcd between the offset (an 38134 // arbitrary number) and the alignment factor (always a power of two, 38135 // non zero). 38136 const new_align: Alignment = @enumFromInt(@min( 38137 @ctz(addend), 38138 ptr_info.flags.alignment.toLog2Units(), 38139 )); 38140 assert(new_align != .none); 38141 break :a new_align; 38142 }; 38143 return sema.ptrType(.{ 38144 .child = elem_ty.toIntern(), 38145 .flags = .{ 38146 .alignment = alignment, 38147 .is_const = ptr_info.flags.is_const, 38148 .is_volatile = ptr_info.flags.is_volatile, 38149 .is_allowzero = is_allowzero, 38150 .address_space = ptr_info.flags.address_space, 38151 .vector_index = vector_info.vector_index, 38152 }, 38153 .packed_offset = .{ 38154 .host_size = vector_info.host_size, 38155 .bit_offset = 0, 38156 }, 38157 }); 38158 } 38159 38160 /// Merge lhs with rhs. 38161 /// Asserts that lhs and rhs are both error sets and are resolved. 38162 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { 38163 const mod = sema.mod; 38164 const arena = sema.arena; 38165 const lhs_names = lhs.errorSetNames(mod); 38166 const rhs_names = rhs.errorSetNames(mod); 38167 var names: InferredErrorSet.NameMap = .{}; 38168 try names.ensureUnusedCapacity(arena, lhs_names.len); 38169 38170 for (lhs_names) |name| { 38171 names.putAssumeCapacityNoClobber(name, {}); 38172 } 38173 for (rhs_names) |name| { 38174 try names.put(arena, name, {}); 38175 } 38176 38177 return mod.errorSetFromUnsortedNames(names.keys()); 38178 } 38179 38180 /// Avoids crashing the compiler when asking if inferred allocations are noreturn. 38181 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool { 38182 if (ref == .unreachable_value) return true; 38183 if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) { 38184 .inferred_alloc, .inferred_alloc_comptime => return false, 38185 else => {}, 38186 }; 38187 return sema.typeOf(ref).isNoReturn(sema.mod); 38188 } 38189 38190 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type. 38191 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool { 38192 if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) { 38193 .inferred_alloc, .inferred_alloc_comptime => return false, 38194 else => {}, 38195 }; 38196 return sema.typeOf(ref).zigTypeTag(sema.mod) == tag; 38197 } 38198 38199 fn ptrType(sema: *Sema, info: InternPool.Key.PtrType) CompileError!Type { 38200 if (info.flags.alignment != .none) { 38201 _ = try sema.typeAbiAlignment(Type.fromInterned(info.child)); 38202 } 38203 return sema.mod.ptrType(info); 38204 }