blob 9f2c577a (1566975B) - 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: Decl.Index, 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 owner_func: ?*Module.Fn, 27 owner_func_index: Module.Fn.OptionalIndex, 28 /// The function this ZIR code is the body of, according to the source code. 29 /// This starts out the same as `owner_func` and then diverges in the case of 30 /// an inline or comptime function call. 31 func: ?*Module.Fn, 32 func_index: Module.Fn.OptionalIndex, 33 /// Used to restore the error return trace when returning a non-error from a function. 34 error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none, 35 /// When semantic analysis needs to know the return type of the function whose body 36 /// is being analyzed, this `Type` should be used instead of going through `func`. 37 /// This will correctly handle the case of a comptime/inline function call of a 38 /// generic function which uses a type expression for the return type. 39 /// The type will be `void` in the case that `func` is `null`. 40 fn_ret_ty: Type, 41 branch_quota: u32 = default_branch_quota, 42 branch_count: u32 = 0, 43 /// Populated when returning `error.ComptimeBreak`. Used to communicate the 44 /// break instruction up the stack to find the corresponding Block. 45 comptime_break_inst: Zir.Inst.Index = undefined, 46 /// This field is updated when a new source location becomes active, so that 47 /// instructions which do not have explicitly mapped source locations still have 48 /// access to the source location set by the previous instruction which did 49 /// contain a mapped source location. 50 src: LazySrcLoc = .{ .token_offset = 0 }, 51 decl_val_table: std.AutoHashMapUnmanaged(Decl.Index, Air.Inst.Ref) = .{}, 52 /// When doing a generic function instantiation, this array collects a 53 /// `Value` object for each parameter that is comptime-known and thus elided 54 /// from the generated function. This memory is allocated by a parent `Sema` and 55 /// owned by the values arena of the Sema owner_decl. 56 comptime_args: []TypedValue = &.{}, 57 /// Marks the function instruction that `comptime_args` applies to so that we 58 /// don't accidentally apply it to a function prototype which is used in the 59 /// type expression of a generic function parameter. 60 comptime_args_fn_inst: Zir.Inst.Index = 0, 61 /// When `comptime_args` is provided, this field is also provided. It was used as 62 /// the key in the `monomorphed_funcs` set. The `func` instruction is supposed 63 /// to use this instead of allocating a fresh one. This avoids an unnecessary 64 /// extra hash table lookup in the `monomorphed_funcs` set. 65 /// Sema will set this to null when it takes ownership. 66 preallocated_new_func: Module.Fn.OptionalIndex = .none, 67 /// The key is types that must be fully resolved prior to machine code 68 /// generation pass. Types are added to this set when resolving them 69 /// immediately could cause a dependency loop, but they do need to be resolved 70 /// before machine code generation passes process the AIR. 71 /// It would work fine if this were an array list instead of an array hash map. 72 /// I chose array hash map with the intention to save time by omitting 73 /// duplicates. 74 types_to_resolve: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, 75 /// These are lazily created runtime blocks from block_inline instructions. 76 /// They are created when an break_inline passes through a runtime condition, because 77 /// Sema must convert comptime control flow to runtime control flow, which means 78 /// breaking from a block. 79 post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{}, 80 /// Populated with the last compile error created. 81 err: ?*Module.ErrorMsg = null, 82 /// True when analyzing a generic instantiation. Used to suppress some errors. 83 is_generic_instantiation: bool = false, 84 /// Set to true when analyzing a func type instruction so that nested generic 85 /// function types will emit generic poison instead of a partial type. 86 no_partial_func_ty: bool = false, 87 88 /// The temporary arena is used for the memory of the `InferredAlloc` values 89 /// here so the values can be dropped without any cleanup. 90 unresolved_inferred_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .{}, 91 92 /// Indices of comptime-mutable decls created by this Sema. These decls' values 93 /// should be interned after analysis completes, as they may refer to memory in 94 /// the Sema arena. 95 /// TODO: this is a workaround for memory bugs triggered by the removal of 96 /// Decl.value_arena. A better solution needs to be found. Probably this will 97 /// involve transitioning comptime-mutable memory away from using Decls at all. 98 comptime_mutable_decls: *std.ArrayList(Decl.Index), 99 100 const std = @import("std"); 101 const math = std.math; 102 const mem = std.mem; 103 const Allocator = mem.Allocator; 104 const assert = std.debug.assert; 105 const log = std.log.scoped(.sema); 106 107 const Sema = @This(); 108 const Value = @import("value.zig").Value; 109 const Type = @import("type.zig").Type; 110 const TypedValue = @import("TypedValue.zig"); 111 const Air = @import("Air.zig"); 112 const Zir = @import("Zir.zig"); 113 const Module = @import("Module.zig"); 114 const trace = @import("tracy.zig").trace; 115 const Namespace = Module.Namespace; 116 const CompileError = Module.CompileError; 117 const SemaError = Module.SemaError; 118 const Decl = Module.Decl; 119 const CaptureScope = Module.CaptureScope; 120 const WipCaptureScope = Module.WipCaptureScope; 121 const LazySrcLoc = Module.LazySrcLoc; 122 const RangeSet = @import("RangeSet.zig"); 123 const target_util = @import("target.zig"); 124 const Package = @import("Package.zig"); 125 const crash_report = @import("crash_report.zig"); 126 const build_options = @import("build_options"); 127 const Compilation = @import("Compilation.zig"); 128 const InternPool = @import("InternPool.zig"); 129 const Alignment = InternPool.Alignment; 130 131 pub const default_branch_quota = 1000; 132 pub const default_reference_trace_len = 2; 133 134 /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve 135 /// instructions during analysis. 136 /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the 137 /// zir instruction index and a start offset. An index is not pressent in the map if the value 138 /// at the index is `Air.Inst.Ref.none`. 139 /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that 140 /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can 141 /// be called safely for any of the instructions passed in. 142 pub const InstMap = struct { 143 items: []Air.Inst.Ref = &[_]Air.Inst.Ref{}, 144 start: Zir.Inst.Index = 0, 145 146 pub fn deinit(map: InstMap, allocator: mem.Allocator) void { 147 allocator.free(map.items); 148 } 149 150 pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref { 151 if (!map.contains(key)) return null; 152 return map.items[key - map.start]; 153 } 154 155 pub fn putAssumeCapacity( 156 map: *InstMap, 157 key: Zir.Inst.Index, 158 ref: Air.Inst.Ref, 159 ) void { 160 map.items[key - map.start] = ref; 161 } 162 163 pub fn putAssumeCapacityNoClobber( 164 map: *InstMap, 165 key: Zir.Inst.Index, 166 ref: Air.Inst.Ref, 167 ) void { 168 assert(!map.contains(key)); 169 map.putAssumeCapacity(key, ref); 170 } 171 172 pub const GetOrPutResult = struct { 173 value_ptr: *Air.Inst.Ref, 174 found_existing: bool, 175 }; 176 177 pub fn getOrPutAssumeCapacity( 178 map: *InstMap, 179 key: Zir.Inst.Index, 180 ) GetOrPutResult { 181 const index = key - map.start; 182 return GetOrPutResult{ 183 .value_ptr = &map.items[index], 184 .found_existing = map.items[index] != .none, 185 }; 186 } 187 188 pub fn remove(map: InstMap, key: Zir.Inst.Index) bool { 189 if (!map.contains(key)) return false; 190 map.items[key - map.start] = .none; 191 return true; 192 } 193 194 pub fn contains(map: InstMap, key: Zir.Inst.Index) bool { 195 return map.items[key - map.start] != .none; 196 } 197 198 pub fn ensureSpaceForInstructions( 199 map: *InstMap, 200 allocator: mem.Allocator, 201 insts: []const Zir.Inst.Index, 202 ) !void { 203 const min_max = mem.minMax(Zir.Inst.Index, insts); 204 const start = min_max.min; 205 const end = min_max.max; 206 if (map.start <= start and end < map.items.len + map.start) 207 return; 208 209 const old_start = if (map.items.len == 0) start else map.start; 210 var better_capacity = map.items.len; 211 var better_start = old_start; 212 while (true) { 213 const extra_capacity = better_capacity / 2 + 16; 214 better_capacity += extra_capacity; 215 better_start -|= @as(Zir.Inst.Index, @intCast(extra_capacity / 2)); 216 if (better_start <= start and end < better_capacity + better_start) 217 break; 218 } 219 220 const start_diff = old_start - better_start; 221 const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity); 222 @memset(new_items[0..start_diff], .none); 223 @memcpy(new_items[start_diff..][0..map.items.len], map.items); 224 @memset(new_items[start_diff + map.items.len ..], .none); 225 226 allocator.free(map.items); 227 map.items = new_items; 228 map.start = @as(Zir.Inst.Index, @intCast(better_start)); 229 } 230 }; 231 232 /// This is the context needed to semantically analyze ZIR instructions and 233 /// produce AIR instructions. 234 /// This is a temporary structure stored on the stack; references to it are valid only 235 /// during semantic analysis of the block. 236 pub const Block = struct { 237 parent: ?*Block, 238 /// Shared among all child blocks. 239 sema: *Sema, 240 /// The namespace to use for lookups from this source block 241 /// When analyzing fields, this is different from src_decl.src_namespace. 242 namespace: Namespace.Index, 243 /// The AIR instructions generated for this block. 244 instructions: std.ArrayListUnmanaged(Air.Inst.Index), 245 // `param` instructions are collected here to be used by the `func` instruction. 246 params: std.ArrayListUnmanaged(Param) = .{}, 247 248 wip_capture_scope: *CaptureScope, 249 250 label: ?*Label = null, 251 inlining: ?*Inlining, 252 /// If runtime_index is not 0 then one of these is guaranteed to be non null. 253 runtime_cond: ?LazySrcLoc = null, 254 runtime_loop: ?LazySrcLoc = null, 255 /// This Decl is the Decl according to the Zig source code corresponding to this Block. 256 /// This can vary during inline or comptime function calls. See `Sema.owner_decl` 257 /// for the one that will be the same for all Block instances. 258 src_decl: Decl.Index, 259 /// Non zero if a non-inline loop or a runtime conditional have been encountered. 260 /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index. 261 runtime_index: Value.RuntimeIndex = .zero, 262 inline_block: Zir.Inst.Index = 0, 263 264 comptime_reason: ?*const ComptimeReason = null, 265 // TODO is_comptime and comptime_reason should probably be merged together. 266 is_comptime: bool, 267 is_typeof: bool = false, 268 269 /// Keep track of the active error return trace index around blocks so that we can correctly 270 /// pop the error trace upon block exit. 271 error_return_trace_index: Air.Inst.Ref = .none, 272 273 /// when null, it is determined by build mode, changed by @setRuntimeSafety 274 want_safety: ?bool = null, 275 276 /// What mode to generate float operations in, set by @setFloatMode 277 float_mode: std.builtin.FloatMode = .Strict, 278 279 c_import_buf: ?*std.ArrayList(u8) = null, 280 281 const ComptimeReason = union(enum) { 282 c_import: struct { 283 block: *Block, 284 src: LazySrcLoc, 285 }, 286 comptime_ret_ty: struct { 287 block: *Block, 288 func: Air.Inst.Ref, 289 func_src: LazySrcLoc, 290 return_ty: Type, 291 }, 292 293 fn explain(cr: ComptimeReason, sema: *Sema, msg: ?*Module.ErrorMsg) !void { 294 const parent = msg orelse return; 295 const mod = sema.mod; 296 const prefix = "expression is evaluated at comptime because "; 297 switch (cr) { 298 .c_import => |ci| { 299 try sema.errNote(ci.block, ci.src, parent, prefix ++ "it is inside a @cImport", .{}); 300 }, 301 .comptime_ret_ty => |rt| { 302 const src_loc = if (try sema.funcDeclSrc(rt.func)) |fn_decl| blk: { 303 var src_loc = fn_decl.srcLoc(mod); 304 src_loc.lazy = .{ .node_offset_fn_type_ret_ty = 0 }; 305 break :blk src_loc; 306 } else blk: { 307 const src_decl = mod.declPtr(rt.block.src_decl); 308 break :blk rt.func_src.toSrcLoc(src_decl, mod); 309 }; 310 if (rt.return_ty.isGenericPoison()) { 311 return mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{}); 312 } 313 try mod.errNoteNonLazy( 314 src_loc, 315 parent, 316 prefix ++ "the function returns a comptime-only type '{}'", 317 .{rt.return_ty.fmt(mod)}, 318 ); 319 try sema.explainWhyTypeIsComptime(parent, src_loc, rt.return_ty); 320 }, 321 } 322 } 323 }; 324 325 const Param = struct { 326 /// `noreturn` means `anytype`. 327 ty: Type, 328 is_comptime: bool, 329 name: []const u8, 330 }; 331 332 /// This `Block` maps a block ZIR instruction to the corresponding 333 /// AIR instruction for break instruction analysis. 334 pub const Label = struct { 335 zir_block: Zir.Inst.Index, 336 merges: Merges, 337 }; 338 339 /// This `Block` indicates that an inline function call is happening 340 /// and return instructions should be analyzed as a break instruction 341 /// to this AIR block instruction. 342 /// It is shared among all the blocks in an inline or comptime called 343 /// function. 344 pub const Inlining = struct { 345 func: ?*Module.Fn, 346 comptime_result: Air.Inst.Ref, 347 merges: Merges, 348 }; 349 350 pub const Merges = struct { 351 block_inst: Air.Inst.Index, 352 /// Separate array list from break_inst_list so that it can be passed directly 353 /// to resolvePeerTypes. 354 results: std.ArrayListUnmanaged(Air.Inst.Ref), 355 /// Keeps track of the break instructions so that the operand can be replaced 356 /// if we need to add type coercion at the end of block analysis. 357 /// Same indexes, capacity, length as `results`. 358 br_list: std.ArrayListUnmanaged(Air.Inst.Index), 359 /// Keeps the source location of the rhs operand of the break instruction, 360 /// to enable more precise compile errors. 361 /// Same indexes, capacity, length as `results`. 362 src_locs: std.ArrayListUnmanaged(?LazySrcLoc), 363 364 pub fn deinit(merges: *@This(), allocator: mem.Allocator) void { 365 merges.results.deinit(allocator); 366 merges.br_list.deinit(allocator); 367 merges.src_locs.deinit(allocator); 368 } 369 }; 370 371 /// For debugging purposes. 372 pub fn dump(block: *Block, mod: Module) void { 373 Zir.dumpBlock(mod, block); 374 } 375 376 pub fn makeSubBlock(parent: *Block) Block { 377 return .{ 378 .parent = parent, 379 .sema = parent.sema, 380 .src_decl = parent.src_decl, 381 .namespace = parent.namespace, 382 .instructions = .{}, 383 .wip_capture_scope = parent.wip_capture_scope, 384 .label = null, 385 .inlining = parent.inlining, 386 .is_comptime = parent.is_comptime, 387 .comptime_reason = parent.comptime_reason, 388 .is_typeof = parent.is_typeof, 389 .runtime_cond = parent.runtime_cond, 390 .runtime_loop = parent.runtime_loop, 391 .runtime_index = parent.runtime_index, 392 .want_safety = parent.want_safety, 393 .float_mode = parent.float_mode, 394 .c_import_buf = parent.c_import_buf, 395 .error_return_trace_index = parent.error_return_trace_index, 396 }; 397 } 398 399 pub fn wantSafety(block: *const Block) bool { 400 return block.want_safety orelse switch (block.sema.mod.optimizeMode()) { 401 .Debug => true, 402 .ReleaseSafe => true, 403 .ReleaseFast => false, 404 .ReleaseSmall => false, 405 }; 406 } 407 408 pub fn getFileScope(block: *Block, mod: *Module) *Module.File { 409 return mod.namespacePtr(block.namespace).file_scope; 410 } 411 412 fn addTy( 413 block: *Block, 414 tag: Air.Inst.Tag, 415 ty: Type, 416 ) error{OutOfMemory}!Air.Inst.Ref { 417 return block.addInst(.{ 418 .tag = tag, 419 .data = .{ .ty = ty }, 420 }); 421 } 422 423 fn addTyOp( 424 block: *Block, 425 tag: Air.Inst.Tag, 426 ty: Type, 427 operand: Air.Inst.Ref, 428 ) error{OutOfMemory}!Air.Inst.Ref { 429 return block.addInst(.{ 430 .tag = tag, 431 .data = .{ .ty_op = .{ 432 .ty = try block.sema.addType(ty), 433 .operand = operand, 434 } }, 435 }); 436 } 437 438 fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref { 439 return block.addInst(.{ 440 .tag = .bitcast, 441 .data = .{ .ty_op = .{ 442 .ty = try block.sema.addType(ty), 443 .operand = operand, 444 } }, 445 }); 446 } 447 448 fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref { 449 return block.addInst(.{ 450 .tag = tag, 451 .data = .{ .no_op = {} }, 452 }); 453 } 454 455 fn addUnOp( 456 block: *Block, 457 tag: Air.Inst.Tag, 458 operand: Air.Inst.Ref, 459 ) error{OutOfMemory}!Air.Inst.Ref { 460 return block.addInst(.{ 461 .tag = tag, 462 .data = .{ .un_op = operand }, 463 }); 464 } 465 466 fn addBr( 467 block: *Block, 468 target_block: Air.Inst.Index, 469 operand: Air.Inst.Ref, 470 ) error{OutOfMemory}!Air.Inst.Ref { 471 return block.addInst(.{ 472 .tag = .br, 473 .data = .{ .br = .{ 474 .block_inst = target_block, 475 .operand = operand, 476 } }, 477 }); 478 } 479 480 fn addBinOp( 481 block: *Block, 482 tag: Air.Inst.Tag, 483 lhs: Air.Inst.Ref, 484 rhs: Air.Inst.Ref, 485 ) error{OutOfMemory}!Air.Inst.Ref { 486 return block.addInst(.{ 487 .tag = tag, 488 .data = .{ .bin_op = .{ 489 .lhs = lhs, 490 .rhs = rhs, 491 } }, 492 }); 493 } 494 495 fn addStructFieldPtr( 496 block: *Block, 497 struct_ptr: Air.Inst.Ref, 498 field_index: u32, 499 ptr_field_ty: Type, 500 ) !Air.Inst.Ref { 501 const ty = try block.sema.addType(ptr_field_ty); 502 const tag: Air.Inst.Tag = switch (field_index) { 503 0 => .struct_field_ptr_index_0, 504 1 => .struct_field_ptr_index_1, 505 2 => .struct_field_ptr_index_2, 506 3 => .struct_field_ptr_index_3, 507 else => { 508 return block.addInst(.{ 509 .tag = .struct_field_ptr, 510 .data = .{ .ty_pl = .{ 511 .ty = ty, 512 .payload = try block.sema.addExtra(Air.StructField{ 513 .struct_operand = struct_ptr, 514 .field_index = field_index, 515 }), 516 } }, 517 }); 518 }, 519 }; 520 return block.addInst(.{ 521 .tag = tag, 522 .data = .{ .ty_op = .{ 523 .ty = ty, 524 .operand = struct_ptr, 525 } }, 526 }); 527 } 528 529 fn addStructFieldVal( 530 block: *Block, 531 struct_val: Air.Inst.Ref, 532 field_index: u32, 533 field_ty: Type, 534 ) !Air.Inst.Ref { 535 return block.addInst(.{ 536 .tag = .struct_field_val, 537 .data = .{ .ty_pl = .{ 538 .ty = try block.sema.addType(field_ty), 539 .payload = try block.sema.addExtra(Air.StructField{ 540 .struct_operand = struct_val, 541 .field_index = field_index, 542 }), 543 } }, 544 }); 545 } 546 547 fn addSliceElemPtr( 548 block: *Block, 549 slice: Air.Inst.Ref, 550 elem_index: Air.Inst.Ref, 551 elem_ptr_ty: Type, 552 ) !Air.Inst.Ref { 553 return block.addInst(.{ 554 .tag = .slice_elem_ptr, 555 .data = .{ .ty_pl = .{ 556 .ty = try block.sema.addType(elem_ptr_ty), 557 .payload = try block.sema.addExtra(Air.Bin{ 558 .lhs = slice, 559 .rhs = elem_index, 560 }), 561 } }, 562 }); 563 } 564 565 fn addPtrElemPtr( 566 block: *Block, 567 array_ptr: Air.Inst.Ref, 568 elem_index: Air.Inst.Ref, 569 elem_ptr_ty: Type, 570 ) !Air.Inst.Ref { 571 const ty_ref = try block.sema.addType(elem_ptr_ty); 572 return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref); 573 } 574 575 fn addPtrElemPtrTypeRef( 576 block: *Block, 577 array_ptr: Air.Inst.Ref, 578 elem_index: Air.Inst.Ref, 579 elem_ptr_ty: Air.Inst.Ref, 580 ) !Air.Inst.Ref { 581 return block.addInst(.{ 582 .tag = .ptr_elem_ptr, 583 .data = .{ .ty_pl = .{ 584 .ty = elem_ptr_ty, 585 .payload = try block.sema.addExtra(Air.Bin{ 586 .lhs = array_ptr, 587 .rhs = elem_index, 588 }), 589 } }, 590 }); 591 } 592 593 fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref { 594 const sema = block.sema; 595 const mod = sema.mod; 596 return block.addInst(.{ 597 .tag = if (block.float_mode == .Optimized) .cmp_vector_optimized else .cmp_vector, 598 .data = .{ .ty_pl = .{ 599 .ty = try sema.addType( 600 try mod.vectorType(.{ 601 .len = sema.typeOf(lhs).vectorLen(mod), 602 .child = .bool_type, 603 }), 604 ), 605 .payload = try sema.addExtra(Air.VectorCmp{ 606 .lhs = lhs, 607 .rhs = rhs, 608 .op = Air.VectorCmp.encodeOp(cmp_op), 609 }), 610 } }, 611 }); 612 } 613 614 fn addAggregateInit( 615 block: *Block, 616 aggregate_ty: Type, 617 elements: []const Air.Inst.Ref, 618 ) !Air.Inst.Ref { 619 const sema = block.sema; 620 const ty_ref = try sema.addType(aggregate_ty); 621 try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len); 622 const extra_index = @as(u32, @intCast(sema.air_extra.items.len)); 623 sema.appendRefsAssumeCapacity(elements); 624 625 return block.addInst(.{ 626 .tag = .aggregate_init, 627 .data = .{ .ty_pl = .{ 628 .ty = ty_ref, 629 .payload = extra_index, 630 } }, 631 }); 632 } 633 634 fn addUnionInit( 635 block: *Block, 636 union_ty: Type, 637 field_index: u32, 638 init: Air.Inst.Ref, 639 ) !Air.Inst.Ref { 640 return block.addInst(.{ 641 .tag = .union_init, 642 .data = .{ .ty_pl = .{ 643 .ty = try block.sema.addType(union_ty), 644 .payload = try block.sema.addExtra(Air.UnionInit{ 645 .field_index = field_index, 646 .init = init, 647 }), 648 } }, 649 }); 650 } 651 652 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { 653 return Air.indexToRef(try block.addInstAsIndex(inst)); 654 } 655 656 pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { 657 const sema = block.sema; 658 const gpa = sema.gpa; 659 660 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 661 try block.instructions.ensureUnusedCapacity(gpa, 1); 662 663 const result_index = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 664 sema.air_instructions.appendAssumeCapacity(inst); 665 block.instructions.appendAssumeCapacity(result_index); 666 return result_index; 667 } 668 669 /// Insert an instruction into the block at `index`. Moves all following 670 /// instructions forward in the block to make room. Operation is O(N). 671 pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { 672 return Air.indexToRef(try block.insertInstAsIndex(index, inst)); 673 } 674 675 pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { 676 const sema = block.sema; 677 const gpa = sema.gpa; 678 679 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 680 681 const result_index = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 682 sema.air_instructions.appendAssumeCapacity(inst); 683 684 try block.instructions.insert(gpa, index, result_index); 685 return result_index; 686 } 687 688 fn addUnreachable(block: *Block, safety_check: bool) !void { 689 if (safety_check and block.wantSafety()) { 690 try block.sema.safetyPanic(block, .unreach); 691 } else { 692 _ = try block.addNoOp(.unreach); 693 } 694 } 695 696 pub fn startAnonDecl(block: *Block) !WipAnonDecl { 697 return WipAnonDecl{ 698 .block = block, 699 .finished = false, 700 }; 701 } 702 703 pub const WipAnonDecl = struct { 704 block: *Block, 705 finished: bool, 706 707 pub fn deinit(wad: *WipAnonDecl) void { 708 wad.* = undefined; 709 } 710 711 /// `alignment` value of 0 means to use ABI alignment. 712 pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: Alignment) !Decl.Index { 713 const sema = wad.block.sema; 714 // Do this ahead of time because `createAnonymousDecl` depends on calling 715 // `type.hasRuntimeBits()`. 716 _ = try sema.typeHasRuntimeBits(ty); 717 const new_decl_index = try sema.mod.createAnonymousDecl(wad.block, .{ 718 .ty = ty, 719 .val = val, 720 }); 721 const new_decl = sema.mod.declPtr(new_decl_index); 722 new_decl.alignment = alignment; 723 errdefer sema.mod.abortAnonDecl(new_decl_index); 724 wad.finished = true; 725 try sema.mod.finalizeAnonDecl(new_decl_index); 726 return new_decl_index; 727 } 728 }; 729 }; 730 731 const LabeledBlock = struct { 732 block: Block, 733 label: Block.Label, 734 735 fn destroy(lb: *LabeledBlock, gpa: Allocator) void { 736 lb.block.instructions.deinit(gpa); 737 lb.label.merges.deinit(gpa); 738 gpa.destroy(lb); 739 } 740 }; 741 742 /// The value stored in the inferred allocation. This will go into 743 /// peer type resolution. This is stored in a separate list so that 744 /// the items are contiguous in memory and thus can be passed to 745 /// `Module.resolvePeerTypes`. 746 const InferredAlloc = struct { 747 prongs: std.MultiArrayList(struct { 748 /// The dummy instruction used as a peer to resolve the type. 749 /// Although this has a redundant type with placeholder, this is 750 /// needed in addition because it may be a constant value, which 751 /// affects peer type resolution. 752 stored_inst: Air.Inst.Ref, 753 /// The bitcast instruction used as a placeholder when the 754 /// new result pointer type is not yet known. 755 placeholder: Air.Inst.Index, 756 }) = .{}, 757 }; 758 759 pub fn deinit(sema: *Sema) void { 760 const gpa = sema.gpa; 761 sema.air_instructions.deinit(gpa); 762 sema.air_extra.deinit(gpa); 763 sema.inst_map.deinit(gpa); 764 sema.decl_val_table.deinit(gpa); 765 sema.types_to_resolve.deinit(gpa); 766 { 767 var it = sema.post_hoc_blocks.iterator(); 768 while (it.next()) |entry| { 769 const labeled_block = entry.value_ptr.*; 770 labeled_block.destroy(gpa); 771 } 772 sema.post_hoc_blocks.deinit(gpa); 773 } 774 sema.unresolved_inferred_allocs.deinit(gpa); 775 sema.* = undefined; 776 } 777 778 /// Returns only the result from the body that is specified. 779 /// Only appropriate to call when it is determined at comptime that this body 780 /// has no peers. 781 fn resolveBody( 782 sema: *Sema, 783 block: *Block, 784 body: []const Zir.Inst.Index, 785 /// This is the instruction that a break instruction within `body` can 786 /// use to return from the body. 787 body_inst: Zir.Inst.Index, 788 ) CompileError!Air.Inst.Ref { 789 const break_data = (try sema.analyzeBodyBreak(block, body)) orelse 790 return Air.Inst.Ref.unreachable_value; 791 // For comptime control flow, we need to detect when `analyzeBody` reports 792 // that we need to break from an outer block. In such case we 793 // use Zig's error mechanism to send control flow up the stack until 794 // we find the corresponding block to this break. 795 if (block.is_comptime and break_data.block_inst != body_inst) { 796 sema.comptime_break_inst = break_data.inst; 797 return error.ComptimeBreak; 798 } 799 return try sema.resolveInst(break_data.operand); 800 } 801 802 fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void { 803 _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) { 804 error.ComptimeBreak => { 805 const zir_datas = sema.code.instructions.items(.data); 806 const break_data = zir_datas[sema.comptime_break_inst].@"break"; 807 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 808 try sema.addRuntimeBreak(block, .{ 809 .block_inst = extra.block_inst, 810 .operand = break_data.operand, 811 .inst = sema.comptime_break_inst, 812 }); 813 }, 814 else => |e| return e, 815 }; 816 } 817 818 pub fn analyzeBody( 819 sema: *Sema, 820 block: *Block, 821 body: []const Zir.Inst.Index, 822 ) !void { 823 _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) { 824 error.ComptimeBreak => unreachable, // unexpected comptime control flow 825 else => |e| return e, 826 }; 827 } 828 829 const BreakData = struct { 830 block_inst: Zir.Inst.Index, 831 operand: Zir.Inst.Ref, 832 inst: Zir.Inst.Index, 833 }; 834 835 pub fn analyzeBodyBreak( 836 sema: *Sema, 837 block: *Block, 838 body: []const Zir.Inst.Index, 839 ) CompileError!?BreakData { 840 const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) { 841 error.ComptimeBreak => sema.comptime_break_inst, 842 else => |e| return e, 843 }; 844 if (block.instructions.items.len != 0 and 845 sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1]))) 846 return null; 847 const break_data = sema.code.instructions.items(.data)[break_inst].@"break"; 848 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 849 return BreakData{ 850 .block_inst = extra.block_inst, 851 .operand = break_data.operand, 852 .inst = break_inst, 853 }; 854 } 855 856 /// ZIR instructions which are always `noreturn` return this. This matches the 857 /// return type of `analyzeBody` so that we can tail call them. 858 /// Only appropriate to return when the instruction is known to be NoReturn 859 /// solely based on the ZIR tag. 860 const always_noreturn: CompileError!Zir.Inst.Index = @as(Zir.Inst.Index, undefined); 861 862 /// This function is the main loop of `Sema` and it can be used in two different ways: 863 /// * The traditional way where there are N breaks out of the block and peer type 864 /// resolution is done on the break operands. In this case, the `Zir.Inst.Index` 865 /// part of the return value will be `undefined`, and callsites should ignore it, 866 /// finding the block result value via the block scope. 867 /// * The "flat" way. There is only 1 break out of the block, and it is with a `break_inline` 868 /// instruction. In this case, the `Zir.Inst.Index` part of the return value will be 869 /// the break instruction. This communicates both which block the break applies to, as 870 /// well as the operand. No block scope needs to be created for this strategy. 871 fn analyzeBodyInner( 872 sema: *Sema, 873 block: *Block, 874 body: []const Zir.Inst.Index, 875 ) CompileError!Zir.Inst.Index { 876 // No tracy calls here, to avoid interfering with the tail call mechanism. 877 878 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); 879 880 // Most of the time, we don't need to construct a new capture scope for a 881 // block. However, successive iterations of comptime loops can capture 882 // different values for the same Zir.Inst.Index, so in those cases, we will 883 // have to create nested capture scopes; see the `.repeat` case below. 884 const parent_capture_scope = block.wip_capture_scope; 885 parent_capture_scope.incRef(); 886 var wip_captures: WipCaptureScope = .{ 887 .scope = parent_capture_scope, 888 .gpa = sema.gpa, 889 .finalized = true, // don't finalize the parent scope 890 }; 891 defer wip_captures.deinit(); 892 893 const mod = sema.mod; 894 const map = &sema.inst_map; 895 const tags = sema.code.instructions.items(.tag); 896 const datas = sema.code.instructions.items(.data); 897 898 var orig_captures: usize = parent_capture_scope.captures.count(); 899 900 var crash_info = crash_report.prepAnalyzeBody(sema, block, body); 901 crash_info.push(); 902 defer crash_info.pop(); 903 904 var dbg_block_begins: u32 = 0; 905 906 // We use a while (true) loop here to avoid a redundant way of breaking out of 907 // the loop. The only way to break out of the loop is with a `noreturn` 908 // instruction. 909 var i: usize = 0; 910 const result = while (true) { 911 crash_info.setBodyIndex(i); 912 const inst = body[i]; 913 std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ 914 mod.namespacePtr(mod.declPtr(block.src_decl).src_namespace).file_scope.sub_file_path, inst, 915 }); 916 const air_inst: Air.Inst.Ref = switch (tags[inst]) { 917 // zig fmt: off 918 .alloc => try sema.zirAlloc(block, inst), 919 .alloc_inferred => try sema.zirAllocInferred(block, inst, true), 920 .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, false), 921 .alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst, true), 922 .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(inst, false), 923 .alloc_mut => try sema.zirAllocMut(block, inst), 924 .alloc_comptime_mut => try sema.zirAllocComptime(block, inst), 925 .make_ptr_const => try sema.zirMakePtrConst(block, inst), 926 .anyframe_type => try sema.zirAnyframeType(block, inst), 927 .array_cat => try sema.zirArrayCat(block, inst), 928 .array_mul => try sema.zirArrayMul(block, inst), 929 .array_type => try sema.zirArrayType(block, inst), 930 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), 931 .vector_type => try sema.zirVectorType(block, inst), 932 .as => try sema.zirAs(block, inst), 933 .as_node => try sema.zirAsNode(block, inst), 934 .as_shift_operand => try sema.zirAsShiftOperand(block, inst), 935 .bit_and => try sema.zirBitwise(block, inst, .bit_and), 936 .bit_not => try sema.zirBitNot(block, inst), 937 .bit_or => try sema.zirBitwise(block, inst, .bit_or), 938 .bitcast => try sema.zirBitcast(block, inst), 939 .suspend_block => try sema.zirSuspendBlock(block, inst), 940 .bool_not => try sema.zirBoolNot(block, inst), 941 .bool_br_and => try sema.zirBoolBr(block, inst, false), 942 .bool_br_or => try sema.zirBoolBr(block, inst, true), 943 .c_import => try sema.zirCImport(block, inst), 944 .call => try sema.zirCall(block, inst, .direct), 945 .field_call => try sema.zirCall(block, inst, .field), 946 .closure_get => try sema.zirClosureGet(block, inst), 947 .cmp_lt => try sema.zirCmp(block, inst, .lt), 948 .cmp_lte => try sema.zirCmp(block, inst, .lte), 949 .cmp_eq => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .Optimized)), 950 .cmp_gte => try sema.zirCmp(block, inst, .gte), 951 .cmp_gt => try sema.zirCmp(block, inst, .gt), 952 .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .Optimized)), 953 .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst), 954 .decl_ref => try sema.zirDeclRef(block, inst), 955 .decl_val => try sema.zirDeclVal(block, inst), 956 .load => try sema.zirLoad(block, inst), 957 .elem_ptr => try sema.zirElemPtr(block, inst), 958 .elem_ptr_node => try sema.zirElemPtrNode(block, inst), 959 .elem_ptr_imm => try sema.zirElemPtrImm(block, inst), 960 .elem_val => try sema.zirElemVal(block, inst), 961 .elem_val_node => try sema.zirElemValNode(block, inst), 962 .elem_type_index => try sema.zirElemTypeIndex(block, inst), 963 .elem_type => try sema.zirElemType(block, inst), 964 .enum_literal => try sema.zirEnumLiteral(block, inst), 965 .int_from_enum => try sema.zirIntFromEnum(block, inst), 966 .enum_from_int => try sema.zirEnumFromInt(block, inst), 967 .err_union_code => try sema.zirErrUnionCode(block, inst), 968 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), 969 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst), 970 .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst), 971 .error_union_type => try sema.zirErrorUnionType(block, inst), 972 .error_value => try sema.zirErrorValue(block, inst), 973 .field_ptr => try sema.zirFieldPtr(block, inst, false), 974 .field_ptr_init => try sema.zirFieldPtr(block, inst, true), 975 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), 976 .field_val => try sema.zirFieldVal(block, inst), 977 .field_val_named => try sema.zirFieldValNamed(block, inst), 978 .func => try sema.zirFunc(block, inst, false), 979 .func_inferred => try sema.zirFunc(block, inst, true), 980 .func_fancy => try sema.zirFuncFancy(block, inst), 981 .import => try sema.zirImport(block, inst), 982 .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), 983 .int => try sema.zirInt(block, inst), 984 .int_big => try sema.zirIntBig(block, inst), 985 .float => try sema.zirFloat(block, inst), 986 .float128 => try sema.zirFloat128(block, inst), 987 .int_type => try sema.zirIntType(inst), 988 .is_non_err => try sema.zirIsNonErr(block, inst), 989 .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), 990 .ret_is_non_err => try sema.zirRetIsNonErr(block, inst), 991 .is_non_null => try sema.zirIsNonNull(block, inst), 992 .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), 993 .merge_error_sets => try sema.zirMergeErrorSets(block, inst), 994 .negate => try sema.zirNegate(block, inst), 995 .negate_wrap => try sema.zirNegateWrap(block, inst), 996 .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), 997 .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), 998 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), 999 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), 1000 .optional_type => try sema.zirOptionalType(block, inst), 1001 .ptr_type => try sema.zirPtrType(block, inst), 1002 .ref => try sema.zirRef(block, inst), 1003 .ret_err_value_code => try sema.zirRetErrValueCode(inst), 1004 .shr => try sema.zirShr(block, inst, .shr), 1005 .shr_exact => try sema.zirShr(block, inst, .shr_exact), 1006 .slice_end => try sema.zirSliceEnd(block, inst), 1007 .slice_sentinel => try sema.zirSliceSentinel(block, inst), 1008 .slice_start => try sema.zirSliceStart(block, inst), 1009 .slice_length => try sema.zirSliceLength(block, inst), 1010 .str => try sema.zirStr(block, inst), 1011 .switch_block => try sema.zirSwitchBlock(block, inst, false), 1012 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), 1013 .type_info => try sema.zirTypeInfo(block, inst), 1014 .size_of => try sema.zirSizeOf(block, inst), 1015 .bit_size_of => try sema.zirBitSizeOf(block, inst), 1016 .typeof => try sema.zirTypeof(block, inst), 1017 .typeof_builtin => try sema.zirTypeofBuiltin(block, inst), 1018 .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), 1019 .xor => try sema.zirBitwise(block, inst, .xor), 1020 .struct_init_empty => try sema.zirStructInitEmpty(block, inst), 1021 .struct_init => try sema.zirStructInit(block, inst, false), 1022 .struct_init_ref => try sema.zirStructInit(block, inst, true), 1023 .struct_init_anon => try sema.zirStructInitAnon(block, inst, false), 1024 .struct_init_anon_ref => try sema.zirStructInitAnon(block, inst, true), 1025 .array_init => try sema.zirArrayInit(block, inst, false), 1026 .array_init_ref => try sema.zirArrayInit(block, inst, true), 1027 .array_init_anon => try sema.zirArrayInitAnon(block, inst, false), 1028 .array_init_anon_ref => try sema.zirArrayInitAnon(block, inst, true), 1029 .union_init => try sema.zirUnionInit(block, inst), 1030 .field_type => try sema.zirFieldType(block, inst), 1031 .field_type_ref => try sema.zirFieldTypeRef(block, inst), 1032 .int_from_ptr => try sema.zirIntFromPtr(block, inst), 1033 .align_of => try sema.zirAlignOf(block, inst), 1034 .int_from_bool => try sema.zirIntFromBool(block, inst), 1035 .embed_file => try sema.zirEmbedFile(block, inst), 1036 .error_name => try sema.zirErrorName(block, inst), 1037 .tag_name => try sema.zirTagName(block, inst), 1038 .type_name => try sema.zirTypeName(block, inst), 1039 .frame_type => try sema.zirFrameType(block, inst), 1040 .frame_size => try sema.zirFrameSize(block, inst), 1041 .int_from_float => try sema.zirIntFromFloat(block, inst), 1042 .float_from_int => try sema.zirFloatFromInt(block, inst), 1043 .ptr_from_int => try sema.zirPtrFromInt(block, inst), 1044 .float_cast => try sema.zirFloatCast(block, inst), 1045 .int_cast => try sema.zirIntCast(block, inst), 1046 .ptr_cast => try sema.zirPtrCast(block, inst), 1047 .truncate => try sema.zirTruncate(block, inst), 1048 .has_decl => try sema.zirHasDecl(block, inst), 1049 .has_field => try sema.zirHasField(block, inst), 1050 .byte_swap => try sema.zirByteSwap(block, inst), 1051 .bit_reverse => try sema.zirBitReverse(block, inst), 1052 .bit_offset_of => try sema.zirBitOffsetOf(block, inst), 1053 .offset_of => try sema.zirOffsetOf(block, inst), 1054 .splat => try sema.zirSplat(block, inst), 1055 .reduce => try sema.zirReduce(block, inst), 1056 .shuffle => try sema.zirShuffle(block, inst), 1057 .atomic_load => try sema.zirAtomicLoad(block, inst), 1058 .atomic_rmw => try sema.zirAtomicRmw(block, inst), 1059 .mul_add => try sema.zirMulAdd(block, inst), 1060 .builtin_call => try sema.zirBuiltinCall(block, inst), 1061 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), 1062 .@"resume" => try sema.zirResume(block, inst), 1063 .@"await" => try sema.zirAwait(block, inst), 1064 .array_base_ptr => try sema.zirArrayBasePtr(block, inst), 1065 .field_base_ptr => try sema.zirFieldBasePtr(block, inst), 1066 .for_len => try sema.zirForLen(block, inst), 1067 1068 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), 1069 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), 1070 .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount), 1071 1072 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), 1073 .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), 1074 .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos), 1075 .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan), 1076 .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp), 1077 .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2), 1078 .log => try sema.zirUnaryMath(block, inst, .log, Value.log), 1079 .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2), 1080 .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10), 1081 .fabs => try sema.zirUnaryMath(block, inst, .fabs, Value.fabs), 1082 .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor), 1083 .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil), 1084 .round => try sema.zirUnaryMath(block, inst, .round, Value.round), 1085 .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc), 1086 1087 .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), 1088 .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), 1089 .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), 1090 1091 .add => try sema.zirArithmetic(block, inst, .add, true), 1092 .addwrap => try sema.zirArithmetic(block, inst, .addwrap, true), 1093 .add_sat => try sema.zirArithmetic(block, inst, .add_sat, true), 1094 .add_unsafe=> try sema.zirArithmetic(block, inst, .add_unsafe, false), 1095 .mul => try sema.zirArithmetic(block, inst, .mul, true), 1096 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap, true), 1097 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat, true), 1098 .sub => try sema.zirArithmetic(block, inst, .sub, true), 1099 .subwrap => try sema.zirArithmetic(block, inst, .subwrap, true), 1100 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat, true), 1101 1102 .div => try sema.zirDiv(block, inst), 1103 .div_exact => try sema.zirDivExact(block, inst), 1104 .div_floor => try sema.zirDivFloor(block, inst), 1105 .div_trunc => try sema.zirDivTrunc(block, inst), 1106 1107 .mod_rem => try sema.zirModRem(block, inst), 1108 .mod => try sema.zirMod(block, inst), 1109 .rem => try sema.zirRem(block, inst), 1110 1111 .max => try sema.zirMinMax(block, inst, .max), 1112 .min => try sema.zirMinMax(block, inst, .min), 1113 1114 .shl => try sema.zirShl(block, inst, .shl), 1115 .shl_exact => try sema.zirShl(block, inst, .shl_exact), 1116 .shl_sat => try sema.zirShl(block, inst, .shl_sat), 1117 1118 .ret_ptr => try sema.zirRetPtr(block), 1119 .ret_type => try sema.addType(sema.fn_ret_ty), 1120 1121 // Instructions that we know to *always* be noreturn based solely on their tag. 1122 // These functions match the return type of analyzeBody so that we can 1123 // tail call them here. 1124 .compile_error => break sema.zirCompileError(block, inst), 1125 .ret_implicit => break sema.zirRetImplicit(block, inst), 1126 .ret_node => break sema.zirRetNode(block, inst), 1127 .ret_load => break sema.zirRetLoad(block, inst), 1128 .ret_err_value => break sema.zirRetErrValue(block, inst), 1129 .@"unreachable" => break sema.zirUnreachable(block, inst), 1130 .panic => break sema.zirPanic(block, inst), 1131 .trap => break sema.zirTrap(block, inst), 1132 // zig fmt: on 1133 1134 .extended => ext: { 1135 const extended = datas[inst].extended; 1136 break :ext switch (extended.opcode) { 1137 // zig fmt: off 1138 .variable => try sema.zirVarExtended( block, extended), 1139 .struct_decl => try sema.zirStructDecl( block, extended, inst), 1140 .enum_decl => try sema.zirEnumDecl( block, extended, inst), 1141 .union_decl => try sema.zirUnionDecl( block, extended, inst), 1142 .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst), 1143 .this => try sema.zirThis( block, extended), 1144 .ret_addr => try sema.zirRetAddr( block, extended), 1145 .builtin_src => try sema.zirBuiltinSrc( block, extended), 1146 .error_return_trace => try sema.zirErrorReturnTrace( block), 1147 .frame => try sema.zirFrame( block, extended), 1148 .frame_address => try sema.zirFrameAddress( block, extended), 1149 .alloc => try sema.zirAllocExtended( block, extended), 1150 .builtin_extern => try sema.zirBuiltinExtern( block, extended), 1151 .@"asm" => try sema.zirAsm( block, extended, false), 1152 .asm_expr => try sema.zirAsm( block, extended, true), 1153 .typeof_peer => try sema.zirTypeofPeer( block, extended), 1154 .compile_log => try sema.zirCompileLog( extended), 1155 .min_multi => try sema.zirMinMaxMulti( block, extended, .min), 1156 .max_multi => try sema.zirMinMaxMulti( block, extended, .max), 1157 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1158 .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1159 .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1160 .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), 1161 .c_undef => try sema.zirCUndef( block, extended), 1162 .c_include => try sema.zirCInclude( block, extended), 1163 .c_define => try sema.zirCDefine( block, extended), 1164 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), 1165 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), 1166 .prefetch => try sema.zirPrefetch( block, extended), 1167 .err_set_cast => try sema.zirErrSetCast( block, extended), 1168 .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), 1169 .select => try sema.zirSelect( block, extended), 1170 .int_from_error => try sema.zirIntFromError( block, extended), 1171 .error_from_int => try sema.zirErrorFromInt( block, extended), 1172 .reify => try sema.zirReify( block, extended, inst), 1173 .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended), 1174 .cmpxchg => try sema.zirCmpxchg( block, extended), 1175 .c_va_arg => try sema.zirCVaArg( block, extended), 1176 .c_va_copy => try sema.zirCVaCopy( block, extended), 1177 .c_va_end => try sema.zirCVaEnd( block, extended), 1178 .c_va_start => try sema.zirCVaStart( block, extended), 1179 .ptr_cast_full => try sema.zirPtrCastFull( block, extended), 1180 .ptr_cast_no_dest => try sema.zirPtrCastNoDest( block, extended), 1181 .work_item_id => try sema.zirWorkItem( block, extended, extended.opcode), 1182 .work_group_size => try sema.zirWorkItem( block, extended, extended.opcode), 1183 .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode), 1184 .in_comptime => try sema.zirInComptime( block), 1185 // zig fmt: on 1186 1187 .fence => { 1188 try sema.zirFence(block, extended); 1189 i += 1; 1190 continue; 1191 }, 1192 .set_float_mode => { 1193 try sema.zirSetFloatMode(block, extended); 1194 i += 1; 1195 continue; 1196 }, 1197 .set_align_stack => { 1198 try sema.zirSetAlignStack(block, extended); 1199 i += 1; 1200 continue; 1201 }, 1202 .set_cold => { 1203 try sema.zirSetCold(block, extended); 1204 i += 1; 1205 continue; 1206 }, 1207 .breakpoint => { 1208 if (!block.is_comptime) { 1209 _ = try block.addNoOp(.breakpoint); 1210 } 1211 i += 1; 1212 continue; 1213 }, 1214 .value_placeholder => unreachable, // never appears in a body 1215 }; 1216 }, 1217 1218 // Instructions that we know can *never* be noreturn based solely on 1219 // their tag. We avoid needlessly checking if they are noreturn and 1220 // continue the loop. 1221 // We also know that they cannot be referenced later, so we avoid 1222 // putting them into the map. 1223 .dbg_stmt => { 1224 try sema.zirDbgStmt(block, inst); 1225 i += 1; 1226 continue; 1227 }, 1228 .dbg_var_ptr => { 1229 try sema.zirDbgVar(block, inst, .dbg_var_ptr); 1230 i += 1; 1231 continue; 1232 }, 1233 .dbg_var_val => { 1234 try sema.zirDbgVar(block, inst, .dbg_var_val); 1235 i += 1; 1236 continue; 1237 }, 1238 .dbg_block_begin => { 1239 dbg_block_begins += 1; 1240 try sema.zirDbgBlockBegin(block); 1241 i += 1; 1242 continue; 1243 }, 1244 .dbg_block_end => { 1245 dbg_block_begins -= 1; 1246 try sema.zirDbgBlockEnd(block); 1247 i += 1; 1248 continue; 1249 }, 1250 .ensure_err_union_payload_void => { 1251 try sema.zirEnsureErrUnionPayloadVoid(block, inst); 1252 i += 1; 1253 continue; 1254 }, 1255 .ensure_result_non_error => { 1256 try sema.zirEnsureResultNonError(block, inst); 1257 i += 1; 1258 continue; 1259 }, 1260 .ensure_result_used => { 1261 try sema.zirEnsureResultUsed(block, inst); 1262 i += 1; 1263 continue; 1264 }, 1265 .set_eval_branch_quota => { 1266 try sema.zirSetEvalBranchQuota(block, inst); 1267 i += 1; 1268 continue; 1269 }, 1270 .atomic_store => { 1271 try sema.zirAtomicStore(block, inst); 1272 i += 1; 1273 continue; 1274 }, 1275 .store => { 1276 try sema.zirStore(block, inst); 1277 i += 1; 1278 continue; 1279 }, 1280 .store_node => { 1281 try sema.zirStoreNode(block, inst); 1282 i += 1; 1283 continue; 1284 }, 1285 .store_to_block_ptr => { 1286 try sema.zirStoreToBlockPtr(block, inst); 1287 i += 1; 1288 continue; 1289 }, 1290 .store_to_inferred_ptr => { 1291 try sema.zirStoreToInferredPtr(block, inst); 1292 i += 1; 1293 continue; 1294 }, 1295 .resolve_inferred_alloc => { 1296 try sema.zirResolveInferredAlloc(block, inst); 1297 i += 1; 1298 continue; 1299 }, 1300 .validate_array_init_ty => { 1301 try sema.validateArrayInitTy(block, inst); 1302 i += 1; 1303 continue; 1304 }, 1305 .validate_struct_init_ty => { 1306 try sema.validateStructInitTy(block, inst); 1307 i += 1; 1308 continue; 1309 }, 1310 .validate_struct_init => { 1311 try sema.zirValidateStructInit(block, inst); 1312 i += 1; 1313 continue; 1314 }, 1315 .validate_array_init => { 1316 try sema.zirValidateArrayInit(block, inst); 1317 i += 1; 1318 continue; 1319 }, 1320 .validate_deref => { 1321 try sema.zirValidateDeref(block, inst); 1322 i += 1; 1323 continue; 1324 }, 1325 .@"export" => { 1326 try sema.zirExport(block, inst); 1327 i += 1; 1328 continue; 1329 }, 1330 .export_value => { 1331 try sema.zirExportValue(block, inst); 1332 i += 1; 1333 continue; 1334 }, 1335 .set_runtime_safety => { 1336 try sema.zirSetRuntimeSafety(block, inst); 1337 i += 1; 1338 continue; 1339 }, 1340 .param => { 1341 try sema.zirParam(block, inst, false); 1342 i += 1; 1343 continue; 1344 }, 1345 .param_comptime => { 1346 try sema.zirParam(block, inst, true); 1347 i += 1; 1348 continue; 1349 }, 1350 .param_anytype => { 1351 try sema.zirParamAnytype(block, inst, false); 1352 i += 1; 1353 continue; 1354 }, 1355 .param_anytype_comptime => { 1356 try sema.zirParamAnytype(block, inst, true); 1357 i += 1; 1358 continue; 1359 }, 1360 .closure_capture => { 1361 try sema.zirClosureCapture(block, inst); 1362 i += 1; 1363 continue; 1364 }, 1365 .memcpy => { 1366 try sema.zirMemcpy(block, inst); 1367 i += 1; 1368 continue; 1369 }, 1370 .memset => { 1371 try sema.zirMemset(block, inst); 1372 i += 1; 1373 continue; 1374 }, 1375 .check_comptime_control_flow => { 1376 if (!block.is_comptime) { 1377 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 1378 const src = inst_data.src(); 1379 const inline_block = Zir.refToIndex(inst_data.operand).?; 1380 1381 var check_block = block; 1382 const target_runtime_index = while (true) { 1383 if (check_block.inline_block == inline_block) { 1384 break check_block.runtime_index; 1385 } 1386 check_block = check_block.parent.?; 1387 }; 1388 1389 if (@intFromEnum(target_runtime_index) < @intFromEnum(block.runtime_index)) { 1390 const runtime_src = block.runtime_cond orelse block.runtime_loop.?; 1391 const msg = msg: { 1392 const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{}); 1393 errdefer msg.destroy(sema.gpa); 1394 1395 try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{}); 1396 break :msg msg; 1397 }; 1398 return sema.failWithOwnedErrorMsg(msg); 1399 } 1400 } 1401 i += 1; 1402 continue; 1403 }, 1404 .save_err_ret_index => { 1405 try sema.zirSaveErrRetIndex(block, inst); 1406 i += 1; 1407 continue; 1408 }, 1409 .restore_err_ret_index => { 1410 try sema.zirRestoreErrRetIndex(block, inst); 1411 i += 1; 1412 continue; 1413 }, 1414 1415 // Special case instructions to handle comptime control flow. 1416 .@"break" => { 1417 if (block.is_comptime) { 1418 break inst; // same as break_inline 1419 } else { 1420 break sema.zirBreak(block, inst); 1421 } 1422 }, 1423 .break_inline => { 1424 if (block.is_comptime) { 1425 break inst; 1426 } else { 1427 sema.comptime_break_inst = inst; 1428 return error.ComptimeBreak; 1429 } 1430 }, 1431 .repeat => { 1432 if (block.is_comptime) { 1433 // Send comptime control flow back to the beginning of this block. 1434 const src = LazySrcLoc.nodeOffset(datas[inst].node); 1435 try sema.emitBackwardBranch(block, src); 1436 if (wip_captures.scope.captures.count() != orig_captures) { 1437 // We need to construct new capture scopes for the next loop iteration so it 1438 // can capture values without clobbering the earlier iteration's captures. 1439 // At first, we reused the parent capture scope as an optimization, but for 1440 // successive scopes we have to create new ones as children of the parent 1441 // scope. 1442 try wip_captures.reset(parent_capture_scope); 1443 block.wip_capture_scope = wip_captures.scope; 1444 orig_captures = 0; 1445 } 1446 i = 0; 1447 continue; 1448 } else { 1449 break always_noreturn; 1450 } 1451 }, 1452 .repeat_inline => { 1453 // Send comptime control flow back to the beginning of this block. 1454 const src = LazySrcLoc.nodeOffset(datas[inst].node); 1455 try sema.emitBackwardBranch(block, src); 1456 if (wip_captures.scope.captures.count() != orig_captures) { 1457 // We need to construct new capture scopes for the next loop iteration so it 1458 // can capture values without clobbering the earlier iteration's captures. 1459 // At first, we reused the parent capture scope as an optimization, but for 1460 // successive scopes we have to create new ones as children of the parent 1461 // scope. 1462 try wip_captures.reset(parent_capture_scope); 1463 block.wip_capture_scope = wip_captures.scope; 1464 orig_captures = 0; 1465 } 1466 i = 0; 1467 continue; 1468 }, 1469 .loop => blk: { 1470 if (!block.is_comptime) break :blk try sema.zirLoop(block, inst); 1471 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220 1472 const inst_data = datas[inst].pl_node; 1473 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 1474 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; 1475 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1476 break always_noreturn; 1477 if (inst == break_data.block_inst) { 1478 break :blk try sema.resolveInst(break_data.operand); 1479 } else { 1480 break break_data.inst; 1481 } 1482 }, 1483 .block, .block_comptime => blk: { 1484 if (!block.is_comptime) { 1485 break :blk try sema.zirBlock(block, inst, tags[inst] == .block_comptime); 1486 } 1487 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220 1488 const inst_data = datas[inst].pl_node; 1489 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 1490 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; 1491 // If this block contains a function prototype, we need to reset the 1492 // current list of parameters and restore it later. 1493 // Note: this probably needs to be resolved in a more general manner. 1494 const prev_params = block.params; 1495 block.params = .{}; 1496 defer { 1497 block.params.deinit(sema.gpa); 1498 block.params = prev_params; 1499 } 1500 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1501 break always_noreturn; 1502 if (inst == break_data.block_inst) { 1503 break :blk try sema.resolveInst(break_data.operand); 1504 } else { 1505 break break_data.inst; 1506 } 1507 }, 1508 .block_inline => blk: { 1509 // Directly analyze the block body without introducing a new block. 1510 // However, in the case of a corresponding break_inline which reaches 1511 // through a runtime conditional branch, we must retroactively emit 1512 // a block, so we remember the block index here just in case. 1513 const block_index = block.instructions.items.len; 1514 const inst_data = datas[inst].pl_node; 1515 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 1516 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; 1517 const gpa = sema.gpa; 1518 1519 const opt_break_data = b: { 1520 // Create a temporary child block so that this inline block is properly 1521 // labeled for any .restore_err_ret_index instructions 1522 var child_block = block.makeSubBlock(); 1523 1524 // If this block contains a function prototype, we need to reset the 1525 // current list of parameters and restore it later. 1526 // Note: this probably needs to be resolved in a more general manner. 1527 child_block.inline_block = 1528 if (tags[inline_body[inline_body.len - 1]] == .repeat_inline) inline_body[0] else inst; 1529 1530 var label: Block.Label = .{ 1531 .zir_block = inst, 1532 .merges = undefined, 1533 }; 1534 child_block.label = &label; 1535 defer child_block.params.deinit(gpa); 1536 1537 // Write these instructions directly into the parent block 1538 child_block.instructions = block.instructions; 1539 defer block.instructions = child_block.instructions; 1540 1541 break :b try sema.analyzeBodyBreak(&child_block, inline_body); 1542 }; 1543 1544 // A runtime conditional branch that needs a post-hoc block to be 1545 // emitted communicates this by mapping the block index into the inst map. 1546 if (map.get(inst)) |new_block_ref| ph: { 1547 // Comptime control flow populates the map, so we don't actually know 1548 // if this is a post-hoc runtime block until we check the 1549 // post_hoc_block map. 1550 const new_block_inst = Air.refToIndex(new_block_ref) orelse break :ph; 1551 const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse 1552 break :ph; 1553 1554 // In this case we need to move all the instructions starting at 1555 // block_index from the current block into this new one. 1556 1557 if (opt_break_data) |break_data| { 1558 // This is a comptime break which we now change to a runtime break 1559 // since it crosses a runtime branch. 1560 // It may pass through our currently being analyzed block_inline or it 1561 // may point directly to it. In the latter case, this modifies the 1562 // block that we are about to look up in the post_hoc_blocks map below. 1563 try sema.addRuntimeBreak(block, break_data); 1564 } else { 1565 // Here the comptime control flow ends with noreturn; however 1566 // we have runtime control flow continuing after this block. 1567 // This branch is therefore handled by the `i += 1; continue;` 1568 // logic below. 1569 } 1570 1571 try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]); 1572 block.instructions.items.len = block_index; 1573 1574 const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges); 1575 { 1576 // Destroy the ad-hoc block entry so that it does not interfere with 1577 // the next iteration of comptime control flow, if any. 1578 labeled_block.destroy(gpa); 1579 assert(sema.post_hoc_blocks.remove(new_block_inst)); 1580 } 1581 map.putAssumeCapacity(inst, block_result); 1582 i += 1; 1583 continue; 1584 } 1585 1586 const break_data = opt_break_data orelse break always_noreturn; 1587 if (inst == break_data.block_inst) { 1588 break :blk try sema.resolveInst(break_data.operand); 1589 } else { 1590 break break_data.inst; 1591 } 1592 }, 1593 .condbr => blk: { 1594 if (!block.is_comptime) break sema.zirCondbr(block, inst); 1595 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220 1596 const inst_data = datas[inst].pl_node; 1597 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; 1598 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 1599 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; 1600 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; 1601 const cond = sema.resolveInstConst(block, cond_src, extra.data.condition, "condition in comptime branch must be comptime-known") catch |err| { 1602 if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err); 1603 return err; 1604 }; 1605 const inline_body = if (cond.val.toBool()) then_body else else_body; 1606 1607 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src); 1608 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1609 break always_noreturn; 1610 if (inst == break_data.block_inst) { 1611 break :blk try sema.resolveInst(break_data.operand); 1612 } else { 1613 break break_data.inst; 1614 } 1615 }, 1616 .condbr_inline => blk: { 1617 const inst_data = datas[inst].pl_node; 1618 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; 1619 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 1620 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; 1621 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; 1622 const cond = sema.resolveInstConst(block, cond_src, extra.data.condition, "condition in comptime branch must be comptime-known") catch |err| { 1623 if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err); 1624 return err; 1625 }; 1626 const inline_body = if (cond.val.toBool()) then_body else else_body; 1627 1628 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src); 1629 const old_runtime_index = block.runtime_index; 1630 defer block.runtime_index = old_runtime_index; 1631 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1632 break always_noreturn; 1633 if (inst == break_data.block_inst) { 1634 break :blk try sema.resolveInst(break_data.operand); 1635 } else { 1636 break break_data.inst; 1637 } 1638 }, 1639 .@"try" => blk: { 1640 if (!block.is_comptime) break :blk try sema.zirTry(block, inst); 1641 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 1642 const src = inst_data.src(); 1643 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 1644 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 1645 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; 1646 const err_union = try sema.resolveInst(extra.data.operand); 1647 const err_union_ty = sema.typeOf(err_union); 1648 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 1649 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ 1650 err_union_ty.fmt(mod), 1651 }); 1652 } 1653 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); 1654 assert(is_non_err != .none); 1655 const is_non_err_val = sema.resolveConstValue(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { 1656 if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err); 1657 return err; 1658 }; 1659 if (is_non_err_val.toBool()) { 1660 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); 1661 } 1662 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1663 break always_noreturn; 1664 if (inst == break_data.block_inst) { 1665 break :blk try sema.resolveInst(break_data.operand); 1666 } else { 1667 break break_data.inst; 1668 } 1669 }, 1670 .try_ptr => blk: { 1671 if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst); 1672 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 1673 const src = inst_data.src(); 1674 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 1675 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 1676 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; 1677 const operand = try sema.resolveInst(extra.data.operand); 1678 const err_union = try sema.analyzeLoad(block, src, operand, operand_src); 1679 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); 1680 assert(is_non_err != .none); 1681 const is_non_err_val = sema.resolveConstValue(block, operand_src, is_non_err, "try operand inside comptime block must be comptime-known") catch |err| { 1682 if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err); 1683 return err; 1684 }; 1685 if (is_non_err_val.toBool()) { 1686 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); 1687 } 1688 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse 1689 break always_noreturn; 1690 if (inst == break_data.block_inst) { 1691 break :blk try sema.resolveInst(break_data.operand); 1692 } else { 1693 break break_data.inst; 1694 } 1695 }, 1696 .@"defer" => blk: { 1697 const inst_data = sema.code.instructions.items(.data)[inst].@"defer"; 1698 const defer_body = sema.code.extra[inst_data.index..][0..inst_data.len]; 1699 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) { 1700 error.ComptimeBreak => sema.comptime_break_inst, 1701 else => |e| return e, 1702 }; 1703 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn; 1704 break :blk Air.Inst.Ref.void_value; 1705 }, 1706 .defer_err_code => blk: { 1707 const inst_data = sema.code.instructions.items(.data)[inst].defer_err_code; 1708 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; 1709 const defer_body = sema.code.extra[extra.index..][0..extra.len]; 1710 const err_code = try sema.resolveInst(inst_data.err_code); 1711 map.putAssumeCapacity(extra.remapped_err_code, err_code); 1712 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) { 1713 error.ComptimeBreak => sema.comptime_break_inst, 1714 else => |e| return e, 1715 }; 1716 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn; 1717 break :blk Air.Inst.Ref.void_value; 1718 }, 1719 }; 1720 if (sema.isNoReturn(air_inst)) { 1721 // We're going to assume that the body itself is noreturn, so let's ensure that now 1722 assert(block.instructions.items.len > 0); 1723 assert(sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1]))); 1724 break always_noreturn; 1725 } 1726 map.putAssumeCapacity(inst, air_inst); 1727 i += 1; 1728 }; 1729 1730 // balance out dbg_block_begins in case of early noreturn 1731 const noreturn_inst = block.instructions.popOrNull(); 1732 while (dbg_block_begins > 0) { 1733 dbg_block_begins -= 1; 1734 if (block.is_comptime or mod.comp.bin_file.options.strip) continue; 1735 1736 _ = try block.addInst(.{ 1737 .tag = .dbg_block_end, 1738 .data = undefined, 1739 }); 1740 } 1741 if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some); 1742 1743 if (!wip_captures.finalized) { 1744 // We've updated the capture scope due to a `repeat` instruction where 1745 // the body had a capture; finalize our child scope and reset 1746 try wip_captures.finalize(); 1747 block.wip_capture_scope = parent_capture_scope; 1748 } 1749 1750 return result; 1751 } 1752 1753 pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { 1754 if (zir_ref == .none) { 1755 return .none; 1756 } else { 1757 return resolveInst(sema, zir_ref); 1758 } 1759 } 1760 1761 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { 1762 assert(zir_ref != .none); 1763 const i = @intFromEnum(zir_ref); 1764 // First section of indexes correspond to a set number of constant values. 1765 // We intentionally map the same indexes to the same values between ZIR and AIR. 1766 if (i < InternPool.static_len) return @as(Air.Inst.Ref, @enumFromInt(i)); 1767 // The last section of indexes refers to the map of ZIR => AIR. 1768 const inst = sema.inst_map.get(i - InternPool.static_len).?; 1769 if (inst == .generic_poison) return error.GenericPoison; 1770 return inst; 1771 } 1772 1773 fn resolveConstBool( 1774 sema: *Sema, 1775 block: *Block, 1776 src: LazySrcLoc, 1777 zir_ref: Zir.Inst.Ref, 1778 reason: []const u8, 1779 ) !bool { 1780 const air_inst = try sema.resolveInst(zir_ref); 1781 const wanted_type = Type.bool; 1782 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 1783 const val = try sema.resolveConstValue(block, src, coerced_inst, reason); 1784 return val.toBool(); 1785 } 1786 1787 pub fn resolveConstString( 1788 sema: *Sema, 1789 block: *Block, 1790 src: LazySrcLoc, 1791 zir_ref: Zir.Inst.Ref, 1792 reason: []const u8, 1793 ) ![]u8 { 1794 const air_inst = try sema.resolveInst(zir_ref); 1795 const wanted_type = Type.slice_const_u8; 1796 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 1797 const val = try sema.resolveConstValue(block, src, coerced_inst, reason); 1798 return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod); 1799 } 1800 1801 pub fn resolveConstStringIntern( 1802 sema: *Sema, 1803 block: *Block, 1804 src: LazySrcLoc, 1805 zir_ref: Zir.Inst.Ref, 1806 reason: []const u8, 1807 ) !InternPool.NullTerminatedString { 1808 const air_inst = try sema.resolveInst(zir_ref); 1809 const wanted_type = Type.slice_const_u8; 1810 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 1811 const val = try sema.resolveConstValue(block, src, coerced_inst, reason); 1812 return val.toIpString(wanted_type, sema.mod); 1813 } 1814 1815 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { 1816 const air_inst = try sema.resolveInst(zir_ref); 1817 assert(air_inst != .var_args_param_type); 1818 const ty = try sema.analyzeAsType(block, src, air_inst); 1819 if (ty.isGenericPoison()) return error.GenericPoison; 1820 return ty; 1821 } 1822 1823 fn resolveCastDestType( 1824 sema: *Sema, 1825 block: *Block, 1826 src: LazySrcLoc, 1827 zir_ref: Zir.Inst.Ref, 1828 strat: enum { remove_eu_opt, remove_eu, remove_opt }, 1829 builtin_name: []const u8, 1830 ) !Type { 1831 const mod = sema.mod; 1832 const remove_eu = switch (strat) { 1833 .remove_eu_opt, .remove_eu => true, 1834 .remove_opt => false, 1835 }; 1836 const remove_opt = switch (strat) { 1837 .remove_eu_opt, .remove_opt => true, 1838 .remove_eu => false, 1839 }; 1840 1841 const raw_ty = sema.resolveType(block, src, zir_ref) catch |err| switch (err) { 1842 error.GenericPoison => { 1843 // Cast builtins use their result type as the destination type, but 1844 // it could be an anytype argument, which we can't catch in AstGen. 1845 const msg = msg: { 1846 const msg = try sema.errMsg(block, src, "{s} must have a known result type", .{builtin_name}); 1847 errdefer msg.destroy(sema.gpa); 1848 try sema.errNote(block, src, msg, "result type is unknown due to anytype parameter", .{}); 1849 try sema.errNote(block, src, msg, "use @as to provide explicit result type", .{}); 1850 break :msg msg; 1851 }; 1852 return sema.failWithOwnedErrorMsg(msg); 1853 }, 1854 else => |e| return e, 1855 }; 1856 1857 if (remove_eu and raw_ty.zigTypeTag(mod) == .ErrorUnion) { 1858 const eu_child = raw_ty.errorUnionPayload(mod); 1859 if (remove_opt and eu_child.zigTypeTag(mod) == .Optional) { 1860 return eu_child.childType(mod); 1861 } 1862 return eu_child; 1863 } 1864 if (remove_opt and raw_ty.zigTypeTag(mod) == .Optional) { 1865 return raw_ty.childType(mod); 1866 } 1867 return raw_ty; 1868 } 1869 1870 fn analyzeAsType( 1871 sema: *Sema, 1872 block: *Block, 1873 src: LazySrcLoc, 1874 air_inst: Air.Inst.Ref, 1875 ) !Type { 1876 const wanted_type = Type.type; 1877 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); 1878 const val = try sema.resolveConstValue(block, src, coerced_inst, "types must be comptime-known"); 1879 return val.toType(); 1880 } 1881 1882 pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { 1883 const mod = sema.mod; 1884 const gpa = sema.gpa; 1885 const ip = &mod.intern_pool; 1886 if (!mod.backendSupportsFeature(.error_return_trace)) return; 1887 1888 assert(!block.is_comptime); 1889 var err_trace_block = block.makeSubBlock(); 1890 defer err_trace_block.instructions.deinit(gpa); 1891 1892 const src: LazySrcLoc = .unneeded; 1893 1894 // var addrs: [err_return_trace_addr_count]usize = undefined; 1895 const err_return_trace_addr_count = 32; 1896 const addr_arr_ty = try mod.arrayType(.{ 1897 .len = err_return_trace_addr_count, 1898 .child = .usize_type, 1899 }); 1900 const addrs_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(addr_arr_ty)); 1901 1902 // var st: StackTrace = undefined; 1903 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 1904 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 1905 const st_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(stack_trace_ty)); 1906 1907 // st.instruction_addresses = &addrs; 1908 const instruction_addresses_field_name = try ip.getOrPutString(gpa, "instruction_addresses"); 1909 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true); 1910 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); 1911 1912 // st.index = 0; 1913 const index_field_name = try ip.getOrPutString(gpa, "index"); 1914 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true); 1915 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store); 1916 1917 // @errorReturnTrace() = &st; 1918 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr); 1919 1920 try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items); 1921 } 1922 1923 /// May return Value Tags: `variable`, `undef`. 1924 /// See `resolveConstValue` for an alternative. 1925 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. 1926 fn resolveValue( 1927 sema: *Sema, 1928 block: *Block, 1929 src: LazySrcLoc, 1930 air_ref: Air.Inst.Ref, 1931 reason: []const u8, 1932 ) CompileError!Value { 1933 if (try sema.resolveMaybeUndefValAllowVariables(air_ref)) |val| { 1934 if (val.isGenericPoison()) return error.GenericPoison; 1935 return val; 1936 } 1937 return sema.failWithNeededComptime(block, src, reason); 1938 } 1939 1940 /// Value Tag `variable` will cause a compile error. 1941 /// Value Tag `undef` may be returned. 1942 fn resolveConstMaybeUndefVal( 1943 sema: *Sema, 1944 block: *Block, 1945 src: LazySrcLoc, 1946 inst: Air.Inst.Ref, 1947 reason: []const u8, 1948 ) CompileError!Value { 1949 if (try sema.resolveMaybeUndefValAllowVariables(inst)) |val| { 1950 if (val.isGenericPoison()) return error.GenericPoison; 1951 if (sema.mod.intern_pool.isVariable(val.toIntern())) 1952 return sema.failWithNeededComptime(block, src, reason); 1953 return val; 1954 } 1955 return sema.failWithNeededComptime(block, src, reason); 1956 } 1957 1958 /// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors. 1959 /// See `resolveValue` for an alternative. 1960 fn resolveConstValue( 1961 sema: *Sema, 1962 block: *Block, 1963 src: LazySrcLoc, 1964 air_ref: Air.Inst.Ref, 1965 reason: []const u8, 1966 ) CompileError!Value { 1967 if (try sema.resolveMaybeUndefValAllowVariables(air_ref)) |val| { 1968 if (val.isGenericPoison()) return error.GenericPoison; 1969 if (val.isUndef(sema.mod)) return sema.failWithUseOfUndef(block, src); 1970 if (sema.mod.intern_pool.isVariable(val.toIntern())) 1971 return sema.failWithNeededComptime(block, src, reason); 1972 return val; 1973 } 1974 return sema.failWithNeededComptime(block, src, reason); 1975 } 1976 1977 /// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors. 1978 /// Lazy values are recursively resolved. 1979 fn resolveConstLazyValue( 1980 sema: *Sema, 1981 block: *Block, 1982 src: LazySrcLoc, 1983 air_ref: Air.Inst.Ref, 1984 reason: []const u8, 1985 ) CompileError!Value { 1986 return sema.resolveLazyValue(try sema.resolveConstValue(block, src, air_ref, reason)); 1987 } 1988 1989 /// Value Tag `variable` causes this function to return `null`. 1990 /// Value Tag `undef` causes this function to return a compile error. 1991 fn resolveDefinedValue( 1992 sema: *Sema, 1993 block: *Block, 1994 src: LazySrcLoc, 1995 air_ref: Air.Inst.Ref, 1996 ) CompileError!?Value { 1997 const mod = sema.mod; 1998 if (try sema.resolveMaybeUndefVal(air_ref)) |val| { 1999 if (val.isUndef(mod)) { 2000 if (block.is_typeof) return null; 2001 return sema.failWithUseOfUndef(block, src); 2002 } 2003 return val; 2004 } 2005 return null; 2006 } 2007 2008 /// Value Tag `variable` causes this function to return `null`. 2009 /// Value Tag `undef` causes this function to return the Value. 2010 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. 2011 fn resolveMaybeUndefVal( 2012 sema: *Sema, 2013 inst: Air.Inst.Ref, 2014 ) CompileError!?Value { 2015 const val = (try sema.resolveMaybeUndefValAllowVariables(inst)) orelse return null; 2016 if (val.isGenericPoison()) return error.GenericPoison; 2017 if (val.ip_index != .none and sema.mod.intern_pool.isVariable(val.toIntern())) return null; 2018 return val; 2019 } 2020 2021 /// Value Tag `variable` causes this function to return `null`. 2022 /// Value Tag `undef` causes this function to return the Value. 2023 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. 2024 /// Lazy values are recursively resolved. 2025 fn resolveMaybeUndefLazyVal( 2026 sema: *Sema, 2027 inst: Air.Inst.Ref, 2028 ) CompileError!?Value { 2029 return try sema.resolveLazyValue((try sema.resolveMaybeUndefVal(inst)) orelse return null); 2030 } 2031 2032 /// Value Tag `variable` results in `null`. 2033 /// Value Tag `undef` results in the Value. 2034 /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. 2035 /// Value Tag `decl_ref` and `decl_ref_mut` or any nested such value results in `null`. 2036 /// Lazy values are recursively resolved. 2037 fn resolveMaybeUndefValIntable( 2038 sema: *Sema, 2039 inst: Air.Inst.Ref, 2040 ) CompileError!?Value { 2041 const val = (try sema.resolveMaybeUndefValAllowVariables(inst)) orelse return null; 2042 if (val.isGenericPoison()) return error.GenericPoison; 2043 if (val.ip_index == .none) return val; 2044 if (sema.mod.intern_pool.isVariable(val.toIntern())) return null; 2045 if (sema.mod.intern_pool.getBackingAddrTag(val.toIntern())) |addr| switch (addr) { 2046 .decl, .mut_decl, .comptime_field => return null, 2047 .int => {}, 2048 .eu_payload, .opt_payload, .elem, .field => unreachable, 2049 }; 2050 return try sema.resolveLazyValue(val); 2051 } 2052 2053 /// Returns all Value tags including `variable` and `undef`. 2054 fn resolveMaybeUndefValAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { 2055 var make_runtime = false; 2056 if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(inst, &make_runtime)) |val| { 2057 if (make_runtime) return null; 2058 return val; 2059 } 2060 return null; 2061 } 2062 2063 /// Returns all Value tags including `variable`, `undef` and `runtime_value`. 2064 fn resolveMaybeUndefValAllowVariablesMaybeRuntime( 2065 sema: *Sema, 2066 inst: Air.Inst.Ref, 2067 make_runtime: *bool, 2068 ) CompileError!?Value { 2069 assert(inst != .none); 2070 // First section of indexes correspond to a set number of constant values. 2071 if (@intFromEnum(inst) < InternPool.static_len) { 2072 return @as(InternPool.Index, @enumFromInt(@intFromEnum(inst))).toValue(); 2073 } 2074 2075 const air_tags = sema.air_instructions.items(.tag); 2076 if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { 2077 if (Air.refToInterned(inst)) |ip_index| { 2078 const val = ip_index.toValue(); 2079 if (val.getVariable(sema.mod) != null) return val; 2080 } 2081 return opv; 2082 } 2083 const ip_index = Air.refToInterned(inst) orelse { 2084 switch (air_tags[Air.refToIndex(inst).?]) { 2085 .inferred_alloc => unreachable, 2086 .inferred_alloc_comptime => unreachable, 2087 else => return null, 2088 } 2089 }; 2090 const val = ip_index.toValue(); 2091 if (val.isRuntimeValue(sema.mod)) make_runtime.* = true; 2092 if (val.isPtrToThreadLocal(sema.mod)) make_runtime.* = true; 2093 return val; 2094 } 2095 2096 fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: []const u8) CompileError { 2097 const msg = msg: { 2098 const msg = try sema.errMsg(block, src, "unable to resolve comptime value", .{}); 2099 errdefer msg.destroy(sema.gpa); 2100 2101 try sema.errNote(block, src, msg, "{s}", .{reason}); 2102 break :msg msg; 2103 }; 2104 return sema.failWithOwnedErrorMsg(msg); 2105 } 2106 2107 fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2108 return sema.fail(block, src, "use of undefined value here causes undefined behavior", .{}); 2109 } 2110 2111 fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2112 return sema.fail(block, src, "division by zero here causes undefined behavior", .{}); 2113 } 2114 2115 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { 2116 return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ 2117 lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod), 2118 }); 2119 } 2120 2121 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError { 2122 return sema.fail(block, src, "expected optional type, found '{}'", .{optional_ty.fmt(sema.mod)}); 2123 } 2124 2125 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { 2126 const mod = sema.mod; 2127 const msg = msg: { 2128 const msg = try sema.errMsg(block, src, "type '{}' does not support array initialization syntax", .{ 2129 ty.fmt(mod), 2130 }); 2131 errdefer msg.destroy(sema.gpa); 2132 if (ty.isSlice(mod)) { 2133 try sema.errNote(block, src, msg, "inferred array length is specified with an underscore: '[_]{}'", .{ty.elemType2(mod).fmt(mod)}); 2134 } 2135 break :msg msg; 2136 }; 2137 return sema.failWithOwnedErrorMsg(msg); 2138 } 2139 2140 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { 2141 return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{ 2142 ty.fmt(sema.mod), 2143 }); 2144 } 2145 2146 fn failWithErrorSetCodeMissing( 2147 sema: *Sema, 2148 block: *Block, 2149 src: LazySrcLoc, 2150 dest_err_set_ty: Type, 2151 src_err_set_ty: Type, 2152 ) CompileError { 2153 return sema.fail(block, src, "expected type '{}', found type '{}'", .{ 2154 dest_err_set_ty.fmt(sema.mod), src_err_set_ty.fmt(sema.mod), 2155 }); 2156 } 2157 2158 fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: usize) CompileError { 2159 const mod = sema.mod; 2160 if (int_ty.zigTypeTag(mod) == .Vector) { 2161 const msg = msg: { 2162 const msg = try sema.errMsg(block, src, "overflow of vector type '{}' with value '{}'", .{ 2163 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod), 2164 }); 2165 errdefer msg.destroy(sema.gpa); 2166 try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index}); 2167 break :msg msg; 2168 }; 2169 return sema.failWithOwnedErrorMsg(msg); 2170 } 2171 return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{ 2172 int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod), 2173 }); 2174 } 2175 2176 fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError { 2177 const mod = sema.mod; 2178 const msg = msg: { 2179 const msg = try sema.errMsg(block, init_src, "value stored in comptime field does not match the default value of the field", .{}); 2180 errdefer msg.destroy(sema.gpa); 2181 2182 const struct_ty = mod.typeToStruct(container_ty) orelse break :msg msg; 2183 const default_value_src = mod.fieldSrcLoc(struct_ty.owner_decl, .{ 2184 .index = field_index, 2185 .range = .value, 2186 }); 2187 try mod.errNoteNonLazy(default_value_src, msg, "default value set here", .{}); 2188 break :msg msg; 2189 }; 2190 return sema.failWithOwnedErrorMsg(msg); 2191 } 2192 2193 fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { 2194 const msg = msg: { 2195 const msg = try sema.errMsg(block, src, "async has not been implemented in the self-hosted compiler yet", .{}); 2196 errdefer msg.destroy(sema.gpa); 2197 break :msg msg; 2198 }; 2199 return sema.failWithOwnedErrorMsg(msg); 2200 } 2201 2202 fn failWithInvalidFieldAccess( 2203 sema: *Sema, 2204 block: *Block, 2205 src: LazySrcLoc, 2206 object_ty: Type, 2207 field_name: InternPool.NullTerminatedString, 2208 ) CompileError { 2209 const mod = sema.mod; 2210 const inner_ty = if (object_ty.isSinglePointer(mod)) object_ty.childType(mod) else object_ty; 2211 2212 if (inner_ty.zigTypeTag(mod) == .Optional) opt: { 2213 const child_ty = inner_ty.optionalChild(mod); 2214 if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :opt; 2215 const msg = msg: { 2216 const msg = try sema.errMsg(block, src, "optional type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); 2217 errdefer msg.destroy(sema.gpa); 2218 try sema.errNote(block, src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 2219 break :msg msg; 2220 }; 2221 return sema.failWithOwnedErrorMsg(msg); 2222 } else if (inner_ty.zigTypeTag(mod) == .ErrorUnion) err: { 2223 const child_ty = inner_ty.errorUnionPayload(mod); 2224 if (!typeSupportsFieldAccess(mod, child_ty, field_name)) break :err; 2225 const msg = msg: { 2226 const msg = try sema.errMsg(block, src, "error union type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); 2227 errdefer msg.destroy(sema.gpa); 2228 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); 2229 break :msg msg; 2230 }; 2231 return sema.failWithOwnedErrorMsg(msg); 2232 } 2233 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); 2234 } 2235 2236 fn typeSupportsFieldAccess(mod: *const Module, ty: Type, field_name: InternPool.NullTerminatedString) bool { 2237 const ip = &mod.intern_pool; 2238 switch (ty.zigTypeTag(mod)) { 2239 .Array => return ip.stringEqlSlice(field_name, "len"), 2240 .Pointer => { 2241 const ptr_info = ty.ptrInfo(mod); 2242 if (ptr_info.flags.size == .Slice) { 2243 return ip.stringEqlSlice(field_name, "ptr") or ip.stringEqlSlice(field_name, "len"); 2244 } else if (ptr_info.child.toType().zigTypeTag(mod) == .Array) { 2245 return ip.stringEqlSlice(field_name, "len"); 2246 } else return false; 2247 }, 2248 .Type, .Struct, .Union => return true, 2249 else => return false, 2250 } 2251 } 2252 2253 /// We don't return a pointer to the new error note because the pointer 2254 /// becomes invalid when you add another one. 2255 fn errNote( 2256 sema: *Sema, 2257 block: *Block, 2258 src: LazySrcLoc, 2259 parent: *Module.ErrorMsg, 2260 comptime format: []const u8, 2261 args: anytype, 2262 ) error{OutOfMemory}!void { 2263 const mod = sema.mod; 2264 const src_decl = mod.declPtr(block.src_decl); 2265 return mod.errNoteNonLazy(src.toSrcLoc(src_decl, mod), parent, format, args); 2266 } 2267 2268 fn addFieldErrNote( 2269 sema: *Sema, 2270 container_ty: Type, 2271 field_index: usize, 2272 parent: *Module.ErrorMsg, 2273 comptime format: []const u8, 2274 args: anytype, 2275 ) !void { 2276 @setCold(true); 2277 const mod = sema.mod; 2278 const decl_index = container_ty.getOwnerDecl(mod); 2279 const decl = mod.declPtr(decl_index); 2280 2281 const field_src = blk: { 2282 const tree = decl.getFileScope(mod).getTree(sema.gpa) catch |err| { 2283 log.err("unable to load AST to report compile error: {s}", .{@errorName(err)}); 2284 break :blk decl.srcLoc(mod); 2285 }; 2286 2287 const container_node = decl.relativeToNodeIndex(0); 2288 const node_tags = tree.nodes.items(.tag); 2289 var buf: [2]std.zig.Ast.Node.Index = undefined; 2290 const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc(mod); 2291 2292 var it_index: usize = 0; 2293 for (container_decl.ast.members) |member_node| { 2294 switch (node_tags[member_node]) { 2295 .container_field_init, 2296 .container_field_align, 2297 .container_field, 2298 => { 2299 if (it_index == field_index) { 2300 break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node), mod); 2301 } 2302 it_index += 1; 2303 }, 2304 else => continue, 2305 } 2306 } 2307 unreachable; 2308 }; 2309 try mod.errNoteNonLazy(field_src, parent, format, args); 2310 } 2311 2312 fn errMsg( 2313 sema: *Sema, 2314 block: *Block, 2315 src: LazySrcLoc, 2316 comptime format: []const u8, 2317 args: anytype, 2318 ) error{OutOfMemory}!*Module.ErrorMsg { 2319 const mod = sema.mod; 2320 const src_decl = mod.declPtr(block.src_decl); 2321 return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl, mod), format, args); 2322 } 2323 2324 pub fn fail( 2325 sema: *Sema, 2326 block: *Block, 2327 src: LazySrcLoc, 2328 comptime format: []const u8, 2329 args: anytype, 2330 ) CompileError { 2331 const err_msg = try sema.errMsg(block, src, format, args); 2332 return sema.failWithOwnedErrorMsg(err_msg); 2333 } 2334 2335 fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { 2336 @setCold(true); 2337 const gpa = sema.gpa; 2338 const mod = sema.mod; 2339 2340 if (crash_report.is_enabled and mod.comp.debug_compile_errors) { 2341 if (err_msg.src_loc.lazy == .unneeded) return error.NeededSourceLocation; 2342 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 2343 wip_errors.init(gpa) catch unreachable; 2344 Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*) catch unreachable; 2345 std.debug.print("compile error during Sema:\n", .{}); 2346 var error_bundle = wip_errors.toOwnedBundle("") catch unreachable; 2347 error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); 2348 crash_report.compilerPanic("unexpected compile error occurred", null, null); 2349 } 2350 2351 ref: { 2352 errdefer err_msg.destroy(gpa); 2353 if (err_msg.src_loc.lazy == .unneeded) { 2354 return error.NeededSourceLocation; 2355 } 2356 try mod.failed_decls.ensureUnusedCapacity(gpa, 1); 2357 try mod.failed_files.ensureUnusedCapacity(gpa, 1); 2358 2359 const max_references = blk: { 2360 if (mod.comp.reference_trace) |num| break :blk num; 2361 // Do not add multiple traces without explicit request. 2362 if (mod.failed_decls.count() != 0) break :ref; 2363 break :blk default_reference_trace_len; 2364 }; 2365 2366 var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; 2367 var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa); 2368 defer reference_stack.deinit(); 2369 2370 // Avoid infinite loops. 2371 var seen = std.AutoHashMap(Decl.Index, void).init(gpa); 2372 defer seen.deinit(); 2373 2374 var cur_reference_trace: u32 = 0; 2375 while (sema.mod.reference_table.get(referenced_by)) |ref| : (cur_reference_trace += 1) { 2376 const gop = try seen.getOrPut(ref.referencer); 2377 if (gop.found_existing) break; 2378 if (cur_reference_trace < max_references) { 2379 const decl = sema.mod.declPtr(ref.referencer); 2380 try reference_stack.append(.{ 2381 .decl = decl.name.toOptional(), 2382 .src_loc = ref.src.toSrcLoc(decl, mod), 2383 }); 2384 } 2385 referenced_by = ref.referencer; 2386 } 2387 if (sema.mod.comp.reference_trace == null and cur_reference_trace > 0) { 2388 try reference_stack.append(.{ 2389 .decl = .none, 2390 .src_loc = undefined, 2391 .hidden = 0, 2392 }); 2393 } else if (cur_reference_trace > max_references) { 2394 try reference_stack.append(.{ 2395 .decl = undefined, 2396 .src_loc = undefined, 2397 .hidden = cur_reference_trace - max_references, 2398 }); 2399 } 2400 err_msg.reference_trace = try reference_stack.toOwnedSlice(); 2401 } 2402 if (sema.owner_func) |func| { 2403 func.state = .sema_failure; 2404 } else { 2405 sema.owner_decl.analysis = .sema_failure; 2406 sema.owner_decl.generation = mod.generation; 2407 } 2408 if (sema.func) |func| { 2409 func.state = .sema_failure; 2410 } 2411 const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl_index); 2412 if (gop.found_existing) { 2413 // If there are multiple errors for the same Decl, prefer the first one added. 2414 sema.err = null; 2415 err_msg.destroy(gpa); 2416 } else { 2417 sema.err = err_msg; 2418 gop.value_ptr.* = err_msg; 2419 } 2420 return error.AnalysisFail; 2421 } 2422 2423 /// Given an ErrorMsg, modify its message and source location to the given values, turning the 2424 /// original message into a note. Notes on the original message are preserved as further notes. 2425 /// Reference trace is preserved. 2426 fn reparentOwnedErrorMsg( 2427 sema: *Sema, 2428 block: *Block, 2429 src: LazySrcLoc, 2430 msg: *Module.ErrorMsg, 2431 comptime format: []const u8, 2432 args: anytype, 2433 ) !void { 2434 const mod = sema.mod; 2435 const src_decl = mod.declPtr(block.src_decl); 2436 const resolved_src = src.toSrcLoc(src_decl, mod); 2437 const msg_str = try std.fmt.allocPrint(mod.gpa, format, args); 2438 2439 const orig_notes = msg.notes.len; 2440 msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1); 2441 std.mem.copyBackwards(Module.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]); 2442 msg.notes[0] = .{ 2443 .src_loc = msg.src_loc, 2444 .msg = msg.msg, 2445 }; 2446 2447 msg.src_loc = resolved_src; 2448 msg.msg = msg_str; 2449 } 2450 2451 const align_ty = Type.u29; 2452 2453 fn analyzeAsAlign( 2454 sema: *Sema, 2455 block: *Block, 2456 src: LazySrcLoc, 2457 air_ref: Air.Inst.Ref, 2458 ) !Alignment { 2459 const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, "alignment must be comptime-known"); 2460 const alignment = @as(u32, @intCast(alignment_big)); // We coerce to u29 in the prev line. 2461 try sema.validateAlign(block, src, alignment); 2462 return Alignment.fromNonzeroByteUnits(alignment); 2463 } 2464 2465 fn validateAlign( 2466 sema: *Sema, 2467 block: *Block, 2468 src: LazySrcLoc, 2469 alignment: u32, 2470 ) !void { 2471 if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); 2472 if (!std.math.isPowerOfTwo(alignment)) { 2473 return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{ 2474 alignment, 2475 }); 2476 } 2477 } 2478 2479 pub fn resolveAlign( 2480 sema: *Sema, 2481 block: *Block, 2482 src: LazySrcLoc, 2483 zir_ref: Zir.Inst.Ref, 2484 ) !Alignment { 2485 const air_ref = try sema.resolveInst(zir_ref); 2486 return sema.analyzeAsAlign(block, src, air_ref); 2487 } 2488 2489 fn resolveInt( 2490 sema: *Sema, 2491 block: *Block, 2492 src: LazySrcLoc, 2493 zir_ref: Zir.Inst.Ref, 2494 dest_ty: Type, 2495 reason: []const u8, 2496 ) !u64 { 2497 const air_ref = try sema.resolveInst(zir_ref); 2498 return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason); 2499 } 2500 2501 fn analyzeAsInt( 2502 sema: *Sema, 2503 block: *Block, 2504 src: LazySrcLoc, 2505 air_ref: Air.Inst.Ref, 2506 dest_ty: Type, 2507 reason: []const u8, 2508 ) !u64 { 2509 const mod = sema.mod; 2510 const coerced = try sema.coerce(block, dest_ty, air_ref, src); 2511 const val = try sema.resolveConstValue(block, src, coerced, reason); 2512 return (try val.getUnsignedIntAdvanced(mod, sema)).?; 2513 } 2514 2515 // Returns a compile error if the value has tag `variable`. See `resolveInstValue` for 2516 // a function that does not. 2517 pub fn resolveInstConst( 2518 sema: *Sema, 2519 block: *Block, 2520 src: LazySrcLoc, 2521 zir_ref: Zir.Inst.Ref, 2522 reason: []const u8, 2523 ) CompileError!TypedValue { 2524 const air_ref = try sema.resolveInst(zir_ref); 2525 const val = try sema.resolveConstValue(block, src, air_ref, reason); 2526 return TypedValue{ 2527 .ty = sema.typeOf(air_ref), 2528 .val = val, 2529 }; 2530 } 2531 2532 // Value Tag may be `undef` or `variable`. 2533 // See `resolveInstConst` for an alternative. 2534 pub fn resolveInstValue( 2535 sema: *Sema, 2536 block: *Block, 2537 src: LazySrcLoc, 2538 zir_ref: Zir.Inst.Ref, 2539 reason: []const u8, 2540 ) CompileError!TypedValue { 2541 const air_ref = try sema.resolveInst(zir_ref); 2542 const val = try sema.resolveValue(block, src, air_ref, reason); 2543 return TypedValue{ 2544 .ty = sema.typeOf(air_ref), 2545 .val = val, 2546 }; 2547 } 2548 2549 fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 2550 const tracy = trace(@src()); 2551 defer tracy.end(); 2552 2553 const mod = sema.mod; 2554 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 2555 const src = inst_data.src(); 2556 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 2557 const pointee_ty = try sema.resolveType(block, src, extra.lhs); 2558 const ptr = try sema.resolveInst(extra.rhs); 2559 const target = mod.getTarget(); 2560 const addr_space = target_util.defaultAddressSpace(target, .local); 2561 2562 if (Air.refToIndex(ptr)) |ptr_inst| { 2563 switch (sema.air_instructions.items(.tag)[ptr_inst]) { 2564 .inferred_alloc => { 2565 const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc; 2566 const ia2 = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; 2567 // Add the stored instruction to the set we will use to resolve peer types 2568 // for the inferred allocation. 2569 // This instruction will not make it to codegen; it is only to participate 2570 // in the `stored_inst_list` of the `inferred_alloc`. 2571 var trash_block = block.makeSubBlock(); 2572 defer trash_block.instructions.deinit(sema.gpa); 2573 const operand = try trash_block.addBitCast(pointee_ty, .void_value); 2574 2575 const ptr_ty = try mod.ptrType(.{ 2576 .child = pointee_ty.toIntern(), 2577 .flags = .{ 2578 .alignment = ia1.alignment, 2579 .address_space = addr_space, 2580 }, 2581 }); 2582 const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr); 2583 2584 try ia2.prongs.append(sema.arena, .{ 2585 .stored_inst = operand, 2586 .placeholder = Air.refToIndex(bitcasted_ptr).?, 2587 }); 2588 2589 return bitcasted_ptr; 2590 }, 2591 .inferred_alloc_comptime => { 2592 const alignment = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.alignment; 2593 // There will be only one coerce_result_ptr because we are running at comptime. 2594 // The alloc will turn into a Decl. 2595 var anon_decl = try block.startAnonDecl(); 2596 defer anon_decl.deinit(); 2597 const decl_index = try anon_decl.finish( 2598 pointee_ty, 2599 (try mod.intern(.{ .undef = pointee_ty.toIntern() })).toValue(), 2600 alignment, 2601 ); 2602 sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.decl_index = decl_index; 2603 if (alignment != .none) { 2604 try sema.resolveTypeLayout(pointee_ty); 2605 } 2606 const ptr_ty = try mod.ptrType(.{ 2607 .child = pointee_ty.toIntern(), 2608 .flags = .{ 2609 .alignment = alignment, 2610 .address_space = addr_space, 2611 }, 2612 }); 2613 try sema.maybeQueueFuncBodyAnalysis(decl_index); 2614 try sema.comptime_mutable_decls.append(decl_index); 2615 return sema.addConstant((try mod.intern(.{ .ptr = .{ 2616 .ty = ptr_ty.toIntern(), 2617 .addr = .{ .mut_decl = .{ 2618 .decl = decl_index, 2619 .runtime_index = block.runtime_index, 2620 } }, 2621 } })).toValue()); 2622 }, 2623 else => {}, 2624 } 2625 } 2626 2627 // Make a dummy store through the pointer to test the coercion. 2628 // We will then use the generated instructions to decide what 2629 // kind of transformations to make on the result pointer. 2630 var trash_block = block.makeSubBlock(); 2631 trash_block.is_comptime = false; 2632 defer trash_block.instructions.deinit(sema.gpa); 2633 2634 const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); 2635 const dummy_operand = try trash_block.addBitCast(pointee_ty, .void_value); 2636 return sema.coerceResultPtr(block, src, ptr, dummy_ptr, dummy_operand, &trash_block); 2637 } 2638 2639 fn coerceResultPtr( 2640 sema: *Sema, 2641 block: *Block, 2642 src: LazySrcLoc, 2643 ptr: Air.Inst.Ref, 2644 dummy_ptr: Air.Inst.Ref, 2645 dummy_operand: Air.Inst.Ref, 2646 trash_block: *Block, 2647 ) CompileError!Air.Inst.Ref { 2648 const mod = sema.mod; 2649 const target = sema.mod.getTarget(); 2650 const addr_space = target_util.defaultAddressSpace(target, .local); 2651 const pointee_ty = sema.typeOf(dummy_operand); 2652 const prev_trash_len = trash_block.instructions.items.len; 2653 2654 try sema.storePtr2(trash_block, src, dummy_ptr, src, dummy_operand, src, .bitcast); 2655 2656 { 2657 const air_tags = sema.air_instructions.items(.tag); 2658 2659 //std.debug.print("dummy storePtr instructions:\n", .{}); 2660 //for (trash_block.instructions.items) |item| { 2661 // std.debug.print(" {s}\n", .{@tagName(air_tags[item])}); 2662 //} 2663 2664 // The last one is always `store`. 2665 const trash_inst = trash_block.instructions.items[trash_block.instructions.items.len - 1]; 2666 if (air_tags[trash_inst] != .store and air_tags[trash_inst] != .store_safe) { 2667 // no store instruction is generated for zero sized types 2668 assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null); 2669 } else { 2670 trash_block.instructions.items.len -= 1; 2671 assert(trash_inst == sema.air_instructions.len - 1); 2672 sema.air_instructions.len -= 1; 2673 } 2674 } 2675 2676 const ptr_ty = try mod.ptrType(.{ 2677 .child = pointee_ty.toIntern(), 2678 .flags = .{ .address_space = addr_space }, 2679 }); 2680 2681 var new_ptr = ptr; 2682 2683 while (true) { 2684 const air_tags = sema.air_instructions.items(.tag); 2685 const air_datas = sema.air_instructions.items(.data); 2686 2687 if (trash_block.instructions.items.len == prev_trash_len) { 2688 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { 2689 return sema.addConstant(ptr_val); 2690 } 2691 if (pointee_ty.eql(Type.null, sema.mod)) { 2692 const null_inst = try sema.addConstant(Value.null); 2693 _ = try block.addBinOp(.store, new_ptr, null_inst); 2694 return Air.Inst.Ref.void_value; 2695 } 2696 return sema.bitCast(block, ptr_ty, new_ptr, src, null); 2697 } 2698 2699 const trash_inst = trash_block.instructions.pop(); 2700 2701 switch (air_tags[trash_inst]) { 2702 // Array coerced to Vector where element size is not equal but coercible. 2703 .aggregate_init => { 2704 const ty_pl = air_datas[trash_inst].ty_pl; 2705 const ptr_operand_ty = try mod.ptrType(.{ 2706 .child = (try sema.analyzeAsType(block, src, ty_pl.ty)).toIntern(), 2707 .flags = .{ .address_space = addr_space }, 2708 }); 2709 2710 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { 2711 return sema.addConstant(ptr_val); 2712 } else { 2713 return sema.bitCast(block, ptr_operand_ty, new_ptr, src, null); 2714 } 2715 }, 2716 .bitcast => { 2717 const ty_op = air_datas[trash_inst].ty_op; 2718 const operand_ty = sema.typeOf(ty_op.operand); 2719 const ptr_operand_ty = try mod.ptrType(.{ 2720 .child = operand_ty.toIntern(), 2721 .flags = .{ .address_space = addr_space }, 2722 }); 2723 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { 2724 new_ptr = try sema.addConstant(try mod.getCoerced(ptr_val, ptr_operand_ty)); 2725 } else { 2726 new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src, null); 2727 } 2728 }, 2729 .wrap_optional => { 2730 new_ptr = try sema.analyzeOptionalPayloadPtr(block, src, new_ptr, false, true); 2731 }, 2732 .wrap_errunion_err => { 2733 return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{}); 2734 }, 2735 .wrap_errunion_payload => { 2736 new_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, new_ptr, false, true); 2737 }, 2738 .array_to_slice => { 2739 return sema.fail(block, src, "TODO coerce_result_ptr array_to_slice", .{}); 2740 }, 2741 .get_union_tag => { 2742 return sema.fail(block, src, "TODO coerce_result_ptr get_union_tag", .{}); 2743 }, 2744 else => { 2745 if (std.debug.runtime_safety) { 2746 std.debug.panic("unexpected AIR tag for coerce_result_ptr: {}", .{ 2747 air_tags[trash_inst], 2748 }); 2749 } else { 2750 unreachable; 2751 } 2752 }, 2753 } 2754 } 2755 } 2756 2757 pub fn analyzeStructDecl( 2758 sema: *Sema, 2759 new_decl: *Decl, 2760 inst: Zir.Inst.Index, 2761 struct_index: Module.Struct.Index, 2762 ) SemaError!void { 2763 const mod = sema.mod; 2764 const struct_obj = mod.structPtr(struct_index); 2765 const extended = sema.code.instructions.items(.data)[inst].extended; 2766 assert(extended.opcode == .struct_decl); 2767 const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small)); 2768 2769 struct_obj.known_non_opv = small.known_non_opv; 2770 if (small.known_comptime_only) { 2771 struct_obj.requires_comptime = .yes; 2772 } 2773 2774 var extra_index: usize = extended.operand; 2775 extra_index += @intFromBool(small.has_src_node); 2776 extra_index += @intFromBool(small.has_fields_len); 2777 const decls_len = if (small.has_decls_len) blk: { 2778 const decls_len = sema.code.extra[extra_index]; 2779 extra_index += 1; 2780 break :blk decls_len; 2781 } else 0; 2782 2783 if (small.has_backing_int) { 2784 const backing_int_body_len = sema.code.extra[extra_index]; 2785 extra_index += 1; // backing_int_body_len 2786 if (backing_int_body_len == 0) { 2787 extra_index += 1; // backing_int_ref 2788 } else { 2789 extra_index += backing_int_body_len; // backing_int_body_inst 2790 } 2791 } 2792 2793 _ = try mod.scanNamespace(struct_obj.namespace, extra_index, decls_len, new_decl); 2794 } 2795 2796 fn zirStructDecl( 2797 sema: *Sema, 2798 block: *Block, 2799 extended: Zir.Inst.Extended.InstData, 2800 inst: Zir.Inst.Index, 2801 ) CompileError!Air.Inst.Ref { 2802 const mod = sema.mod; 2803 const gpa = sema.gpa; 2804 const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small)); 2805 const src: LazySrcLoc = if (small.has_src_node) blk: { 2806 const node_offset = @as(i32, @bitCast(sema.code.extra[extended.operand])); 2807 break :blk LazySrcLoc.nodeOffset(node_offset); 2808 } else sema.src; 2809 2810 // Because these three things each reference each other, `undefined` 2811 // placeholders are used before being set after the struct type gains an 2812 // InternPool index. 2813 2814 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 2815 .ty = Type.noreturn, 2816 .val = Value.@"unreachable", 2817 }, small.name_strategy, "struct", inst); 2818 const new_decl = mod.declPtr(new_decl_index); 2819 new_decl.owns_tv = true; 2820 errdefer mod.abortAnonDecl(new_decl_index); 2821 2822 const new_namespace_index = try mod.createNamespace(.{ 2823 .parent = block.namespace.toOptional(), 2824 .ty = undefined, 2825 .file_scope = block.getFileScope(mod), 2826 }); 2827 const new_namespace = mod.namespacePtr(new_namespace_index); 2828 errdefer mod.destroyNamespace(new_namespace_index); 2829 2830 const struct_index = try mod.createStruct(.{ 2831 .owner_decl = new_decl_index, 2832 .fields = .{}, 2833 .zir_index = inst, 2834 .layout = small.layout, 2835 .status = .none, 2836 .known_non_opv = undefined, 2837 .is_tuple = small.is_tuple, 2838 .namespace = new_namespace_index, 2839 }); 2840 errdefer mod.destroyStruct(struct_index); 2841 2842 const struct_ty = try mod.intern_pool.get(gpa, .{ .struct_type = .{ 2843 .index = struct_index.toOptional(), 2844 .namespace = new_namespace_index.toOptional(), 2845 } }); 2846 // TODO: figure out InternPool removals for incremental compilation 2847 //errdefer mod.intern_pool.remove(struct_ty); 2848 2849 new_decl.ty = Type.type; 2850 new_decl.val = struct_ty.toValue(); 2851 new_namespace.ty = struct_ty.toType(); 2852 2853 try sema.analyzeStructDecl(new_decl, inst, struct_index); 2854 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 2855 try mod.finalizeAnonDecl(new_decl_index); 2856 return decl_val; 2857 } 2858 2859 fn createAnonymousDeclTypeNamed( 2860 sema: *Sema, 2861 block: *Block, 2862 src: LazySrcLoc, 2863 typed_value: TypedValue, 2864 name_strategy: Zir.Inst.NameStrategy, 2865 anon_prefix: []const u8, 2866 inst: ?Zir.Inst.Index, 2867 ) !Decl.Index { 2868 const mod = sema.mod; 2869 const gpa = sema.gpa; 2870 const namespace = block.namespace; 2871 const src_scope = block.wip_capture_scope; 2872 const src_decl = mod.declPtr(block.src_decl); 2873 const src_node = src_decl.relativeToNodeIndex(src.node_offset.x); 2874 const new_decl_index = try mod.allocateNewDecl(namespace, src_node, src_scope); 2875 errdefer mod.destroyDecl(new_decl_index); 2876 2877 switch (name_strategy) { 2878 .anon => { 2879 // It would be neat to have "struct:line:column" but this name has 2880 // to survive incremental updates, where it may have been shifted down 2881 // or up to a different line, but unchanged, and thus not unnecessarily 2882 // semantically analyzed. 2883 // This name is also used as the key in the parent namespace so it cannot be 2884 // renamed. 2885 2886 const name = mod.intern_pool.getOrPutStringFmt(gpa, "{}__{s}_{d}", .{ 2887 src_decl.name.fmt(&mod.intern_pool), anon_prefix, @intFromEnum(new_decl_index), 2888 }) catch unreachable; 2889 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); 2890 return new_decl_index; 2891 }, 2892 .parent => { 2893 const name = mod.declPtr(block.src_decl).name; 2894 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); 2895 return new_decl_index; 2896 }, 2897 .func => { 2898 const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst); 2899 const zir_tags = sema.code.instructions.items(.tag); 2900 2901 var buf = std.ArrayList(u8).init(gpa); 2902 defer buf.deinit(); 2903 2904 const writer = buf.writer(); 2905 try writer.print("{}(", .{mod.declPtr(block.src_decl).name.fmt(&mod.intern_pool)}); 2906 2907 var arg_i: usize = 0; 2908 for (fn_info.param_body) |zir_inst| switch (zir_tags[zir_inst]) { 2909 .param, .param_comptime, .param_anytype, .param_anytype_comptime => { 2910 const arg = sema.inst_map.get(zir_inst).?; 2911 // If this is being called in a generic function then analyzeCall will 2912 // have already resolved the args and this will work. 2913 // If not then this is a struct type being returned from a non-generic 2914 // function and the name doesn't matter since it will later 2915 // result in a compile error. 2916 const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, "") catch 2917 return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); 2918 2919 if (arg_i != 0) try writer.writeByte(','); 2920 try writer.print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)}); 2921 2922 arg_i += 1; 2923 continue; 2924 }, 2925 else => continue, 2926 }; 2927 2928 try writer.writeByte(')'); 2929 const name = try mod.intern_pool.getOrPutString(gpa, buf.items); 2930 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); 2931 return new_decl_index; 2932 }, 2933 .dbg_var => { 2934 const ref = Zir.indexToRef(inst.?); 2935 const zir_tags = sema.code.instructions.items(.tag); 2936 const zir_data = sema.code.instructions.items(.data); 2937 var i = inst.?; 2938 while (i < zir_tags.len) : (i += 1) switch (zir_tags[i]) { 2939 .dbg_var_ptr, .dbg_var_val => { 2940 if (zir_data[i].str_op.operand != ref) continue; 2941 2942 const name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}.{s}", .{ 2943 src_decl.name.fmt(&mod.intern_pool), zir_data[i].str_op.getStr(sema.code), 2944 }); 2945 2946 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); 2947 return new_decl_index; 2948 }, 2949 else => {}, 2950 }; 2951 return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); 2952 }, 2953 } 2954 } 2955 2956 fn zirEnumDecl( 2957 sema: *Sema, 2958 block: *Block, 2959 extended: Zir.Inst.Extended.InstData, 2960 inst: Zir.Inst.Index, 2961 ) CompileError!Air.Inst.Ref { 2962 const tracy = trace(@src()); 2963 defer tracy.end(); 2964 2965 const mod = sema.mod; 2966 const gpa = sema.gpa; 2967 const small = @as(Zir.Inst.EnumDecl.Small, @bitCast(extended.small)); 2968 var extra_index: usize = extended.operand; 2969 2970 const src: LazySrcLoc = if (small.has_src_node) blk: { 2971 const node_offset = @as(i32, @bitCast(sema.code.extra[extra_index])); 2972 extra_index += 1; 2973 break :blk LazySrcLoc.nodeOffset(node_offset); 2974 } else sema.src; 2975 const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; 2976 2977 const tag_type_ref = if (small.has_tag_type) blk: { 2978 const tag_type_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 2979 extra_index += 1; 2980 break :blk tag_type_ref; 2981 } else .none; 2982 2983 const body_len = if (small.has_body_len) blk: { 2984 const body_len = sema.code.extra[extra_index]; 2985 extra_index += 1; 2986 break :blk body_len; 2987 } else 0; 2988 2989 const fields_len = if (small.has_fields_len) blk: { 2990 const fields_len = sema.code.extra[extra_index]; 2991 extra_index += 1; 2992 break :blk fields_len; 2993 } else 0; 2994 2995 const decls_len = if (small.has_decls_len) blk: { 2996 const decls_len = sema.code.extra[extra_index]; 2997 extra_index += 1; 2998 break :blk decls_len; 2999 } else 0; 3000 3001 // Because these three things each reference each other, `undefined` 3002 // placeholders are used before being set after the enum type gains an 3003 // InternPool index. 3004 3005 var done = false; 3006 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 3007 .ty = Type.noreturn, 3008 .val = Value.@"unreachable", 3009 }, small.name_strategy, "enum", inst); 3010 const new_decl = mod.declPtr(new_decl_index); 3011 new_decl.owns_tv = true; 3012 errdefer if (!done) mod.abortAnonDecl(new_decl_index); 3013 3014 const new_namespace_index = try mod.createNamespace(.{ 3015 .parent = block.namespace.toOptional(), 3016 .ty = undefined, 3017 .file_scope = block.getFileScope(mod), 3018 }); 3019 const new_namespace = mod.namespacePtr(new_namespace_index); 3020 errdefer if (!done) mod.destroyNamespace(new_namespace_index); 3021 3022 extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); 3023 3024 const body = sema.code.extra[extra_index..][0..body_len]; 3025 extra_index += body.len; 3026 3027 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; 3028 const body_end = extra_index; 3029 extra_index += bit_bags_count; 3030 3031 const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { 3032 if (bag != 0) break true; 3033 } else false; 3034 3035 const incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{ 3036 .decl = new_decl_index, 3037 .namespace = new_namespace_index.toOptional(), 3038 .fields_len = fields_len, 3039 .has_values = any_values, 3040 .tag_mode = if (small.nonexhaustive) 3041 .nonexhaustive 3042 else if (tag_type_ref == .none) 3043 .auto 3044 else 3045 .explicit, 3046 }); 3047 // TODO: figure out InternPool removals for incremental compilation 3048 //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index); 3049 3050 new_decl.ty = Type.type; 3051 new_decl.val = incomplete_enum.index.toValue(); 3052 new_namespace.ty = incomplete_enum.index.toType(); 3053 3054 const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); 3055 try mod.finalizeAnonDecl(new_decl_index); 3056 done = true; 3057 3058 const int_tag_ty = ty: { 3059 // We create a block for the field type instructions because they 3060 // may need to reference Decls from inside the enum namespace. 3061 // Within the field type, default value, and alignment expressions, the "owner decl" 3062 // should be the enum itself. 3063 3064 const prev_owner_decl = sema.owner_decl; 3065 const prev_owner_decl_index = sema.owner_decl_index; 3066 sema.owner_decl = new_decl; 3067 sema.owner_decl_index = new_decl_index; 3068 defer { 3069 sema.owner_decl = prev_owner_decl; 3070 sema.owner_decl_index = prev_owner_decl_index; 3071 } 3072 3073 const prev_owner_func = sema.owner_func; 3074 const prev_owner_func_index = sema.owner_func_index; 3075 sema.owner_func = null; 3076 sema.owner_func_index = .none; 3077 defer sema.owner_func = prev_owner_func; 3078 defer sema.owner_func_index = prev_owner_func_index; 3079 3080 const prev_func = sema.func; 3081 const prev_func_index = sema.func_index; 3082 sema.func = null; 3083 sema.func_index = .none; 3084 defer sema.func = prev_func; 3085 defer sema.func_index = prev_func_index; 3086 3087 var wip_captures = try WipCaptureScope.init(gpa, new_decl.src_scope); 3088 defer wip_captures.deinit(); 3089 3090 var enum_block: Block = .{ 3091 .parent = null, 3092 .sema = sema, 3093 .src_decl = new_decl_index, 3094 .namespace = new_namespace_index, 3095 .wip_capture_scope = wip_captures.scope, 3096 .instructions = .{}, 3097 .inlining = null, 3098 .is_comptime = true, 3099 }; 3100 defer enum_block.instructions.deinit(sema.gpa); 3101 3102 if (body.len != 0) { 3103 try sema.analyzeBody(&enum_block, body); 3104 } 3105 3106 try wip_captures.finalize(); 3107 3108 if (tag_type_ref != .none) { 3109 const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); 3110 if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) { 3111 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); 3112 } 3113 incomplete_enum.setTagType(&mod.intern_pool, ty.toIntern()); 3114 break :ty ty; 3115 } else if (fields_len == 0) { 3116 break :ty try mod.intType(.unsigned, 0); 3117 } else { 3118 const bits = std.math.log2_int_ceil(usize, fields_len); 3119 break :ty try mod.intType(.unsigned, bits); 3120 } 3121 }; 3122 3123 if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) { 3124 if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(mod)) { 3125 return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); 3126 } 3127 } 3128 3129 var bit_bag_index: usize = body_end; 3130 var cur_bit_bag: u32 = undefined; 3131 var field_i: u32 = 0; 3132 var last_tag_val: ?Value = null; 3133 while (field_i < fields_len) : (field_i += 1) { 3134 if (field_i % 32 == 0) { 3135 cur_bit_bag = sema.code.extra[bit_bag_index]; 3136 bit_bag_index += 1; 3137 } 3138 const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0; 3139 cur_bit_bag >>= 1; 3140 3141 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]); 3142 extra_index += 1; 3143 3144 // doc comment 3145 extra_index += 1; 3146 3147 const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir); 3148 if (try incomplete_enum.addFieldName(&mod.intern_pool, gpa, field_name)) |other_index| { 3149 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy; 3150 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; 3151 const msg = msg: { 3152 const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name_zir}); 3153 errdefer msg.destroy(gpa); 3154 try sema.errNote(block, other_field_src, msg, "other field here", .{}); 3155 break :msg msg; 3156 }; 3157 return sema.failWithOwnedErrorMsg(msg); 3158 } 3159 3160 const tag_overflow = if (has_tag_value) overflow: { 3161 const tag_val_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 3162 extra_index += 1; 3163 const tag_inst = try sema.resolveInst(tag_val_ref); 3164 last_tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) { 3165 error.NeededSourceLocation => { 3166 const value_src = mod.fieldSrcLoc(new_decl_index, .{ 3167 .index = field_i, 3168 .range = .value, 3169 }).lazy; 3170 _ = try sema.resolveConstValue(block, value_src, tag_inst, "enum tag value must be comptime-known"); 3171 unreachable; 3172 }, 3173 else => |e| return e, 3174 }; 3175 if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true; 3176 last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); 3177 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| { 3178 const value_src = mod.fieldSrcLoc(new_decl_index, .{ 3179 .index = field_i, 3180 .range = .value, 3181 }).lazy; 3182 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; 3183 const msg = msg: { 3184 const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); 3185 errdefer msg.destroy(gpa); 3186 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{}); 3187 break :msg msg; 3188 }; 3189 return sema.failWithOwnedErrorMsg(msg); 3190 } 3191 break :overflow false; 3192 } else if (any_values) overflow: { 3193 var overflow: ?usize = null; 3194 last_tag_val = if (last_tag_val) |val| 3195 try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty, &overflow) 3196 else 3197 try mod.intValue(int_tag_ty, 0); 3198 if (overflow != null) break :overflow true; 3199 if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, last_tag_val.?.toIntern())) |other_index| { 3200 const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy; 3201 const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; 3202 const msg = msg: { 3203 const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); 3204 errdefer msg.destroy(gpa); 3205 try sema.errNote(block, other_field_src, msg, "other occurrence here", .{}); 3206 break :msg msg; 3207 }; 3208 return sema.failWithOwnedErrorMsg(msg); 3209 } 3210 break :overflow false; 3211 } else overflow: { 3212 last_tag_val = try mod.intValue(Type.comptime_int, field_i); 3213 if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true; 3214 last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); 3215 break :overflow false; 3216 }; 3217 3218 if (tag_overflow) { 3219 const value_src = mod.fieldSrcLoc(new_decl_index, .{ 3220 .index = field_i, 3221 .range = if (has_tag_value) .value else .name, 3222 }).lazy; 3223 const msg = try sema.errMsg(block, value_src, "enumeration value '{}' too large for type '{}'", .{ 3224 last_tag_val.?.fmtValue(int_tag_ty, mod), int_tag_ty.fmt(mod), 3225 }); 3226 return sema.failWithOwnedErrorMsg(msg); 3227 } 3228 } 3229 return decl_val; 3230 } 3231 3232 fn zirUnionDecl( 3233 sema: *Sema, 3234 block: *Block, 3235 extended: Zir.Inst.Extended.InstData, 3236 inst: Zir.Inst.Index, 3237 ) CompileError!Air.Inst.Ref { 3238 const tracy = trace(@src()); 3239 defer tracy.end(); 3240 3241 const mod = sema.mod; 3242 const gpa = sema.gpa; 3243 const small = @as(Zir.Inst.UnionDecl.Small, @bitCast(extended.small)); 3244 var extra_index: usize = extended.operand; 3245 3246 const src: LazySrcLoc = if (small.has_src_node) blk: { 3247 const node_offset = @as(i32, @bitCast(sema.code.extra[extra_index])); 3248 extra_index += 1; 3249 break :blk LazySrcLoc.nodeOffset(node_offset); 3250 } else sema.src; 3251 3252 extra_index += @intFromBool(small.has_tag_type); 3253 extra_index += @intFromBool(small.has_body_len); 3254 extra_index += @intFromBool(small.has_fields_len); 3255 3256 const decls_len = if (small.has_decls_len) blk: { 3257 const decls_len = sema.code.extra[extra_index]; 3258 extra_index += 1; 3259 break :blk decls_len; 3260 } else 0; 3261 3262 // Because these three things each reference each other, `undefined` 3263 // placeholders are used before being set after the union type gains an 3264 // InternPool index. 3265 3266 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 3267 .ty = Type.noreturn, 3268 .val = Value.@"unreachable", 3269 }, small.name_strategy, "union", inst); 3270 const new_decl = mod.declPtr(new_decl_index); 3271 new_decl.owns_tv = true; 3272 errdefer mod.abortAnonDecl(new_decl_index); 3273 3274 const new_namespace_index = try mod.createNamespace(.{ 3275 .parent = block.namespace.toOptional(), 3276 .ty = undefined, 3277 .file_scope = block.getFileScope(mod), 3278 }); 3279 const new_namespace = mod.namespacePtr(new_namespace_index); 3280 errdefer mod.destroyNamespace(new_namespace_index); 3281 3282 const union_index = try mod.createUnion(.{ 3283 .owner_decl = new_decl_index, 3284 .tag_ty = Type.null, 3285 .fields = .{}, 3286 .zir_index = inst, 3287 .layout = small.layout, 3288 .status = .none, 3289 .namespace = new_namespace_index, 3290 }); 3291 errdefer mod.destroyUnion(union_index); 3292 3293 const union_ty = try mod.intern_pool.get(gpa, .{ .union_type = .{ 3294 .index = union_index, 3295 .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) 3296 .tagged 3297 else if (small.layout != .Auto) 3298 .none 3299 else switch (block.sema.mod.optimizeMode()) { 3300 .Debug, .ReleaseSafe => .safety, 3301 .ReleaseFast, .ReleaseSmall => .none, 3302 }, 3303 } }); 3304 // TODO: figure out InternPool removals for incremental compilation 3305 //errdefer mod.intern_pool.remove(union_ty); 3306 3307 new_decl.ty = Type.type; 3308 new_decl.val = union_ty.toValue(); 3309 new_namespace.ty = union_ty.toType(); 3310 3311 _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); 3312 3313 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 3314 try mod.finalizeAnonDecl(new_decl_index); 3315 return decl_val; 3316 } 3317 3318 fn zirOpaqueDecl( 3319 sema: *Sema, 3320 block: *Block, 3321 extended: Zir.Inst.Extended.InstData, 3322 inst: Zir.Inst.Index, 3323 ) CompileError!Air.Inst.Ref { 3324 const tracy = trace(@src()); 3325 defer tracy.end(); 3326 3327 const mod = sema.mod; 3328 const small = @as(Zir.Inst.OpaqueDecl.Small, @bitCast(extended.small)); 3329 var extra_index: usize = extended.operand; 3330 3331 const src: LazySrcLoc = if (small.has_src_node) blk: { 3332 const node_offset = @as(i32, @bitCast(sema.code.extra[extra_index])); 3333 extra_index += 1; 3334 break :blk LazySrcLoc.nodeOffset(node_offset); 3335 } else sema.src; 3336 3337 const decls_len = if (small.has_decls_len) blk: { 3338 const decls_len = sema.code.extra[extra_index]; 3339 extra_index += 1; 3340 break :blk decls_len; 3341 } else 0; 3342 3343 // Because these three things each reference each other, `undefined` 3344 // placeholders are used in two places before being set after the opaque 3345 // type gains an InternPool index. 3346 3347 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 3348 .ty = Type.noreturn, 3349 .val = Value.@"unreachable", 3350 }, small.name_strategy, "opaque", inst); 3351 const new_decl = mod.declPtr(new_decl_index); 3352 new_decl.owns_tv = true; 3353 errdefer mod.abortAnonDecl(new_decl_index); 3354 3355 const new_namespace_index = try mod.createNamespace(.{ 3356 .parent = block.namespace.toOptional(), 3357 .ty = undefined, 3358 .file_scope = block.getFileScope(mod), 3359 }); 3360 const new_namespace = mod.namespacePtr(new_namespace_index); 3361 errdefer mod.destroyNamespace(new_namespace_index); 3362 3363 const opaque_ty = try mod.intern(.{ .opaque_type = .{ 3364 .decl = new_decl_index, 3365 .namespace = new_namespace_index, 3366 } }); 3367 // TODO: figure out InternPool removals for incremental compilation 3368 //errdefer mod.intern_pool.remove(opaque_ty); 3369 3370 new_decl.ty = Type.type; 3371 new_decl.val = opaque_ty.toValue(); 3372 new_namespace.ty = opaque_ty.toType(); 3373 3374 extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); 3375 3376 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 3377 try mod.finalizeAnonDecl(new_decl_index); 3378 return decl_val; 3379 } 3380 3381 fn zirErrorSetDecl( 3382 sema: *Sema, 3383 block: *Block, 3384 inst: Zir.Inst.Index, 3385 name_strategy: Zir.Inst.NameStrategy, 3386 ) CompileError!Air.Inst.Ref { 3387 const tracy = trace(@src()); 3388 defer tracy.end(); 3389 3390 const mod = sema.mod; 3391 const gpa = sema.gpa; 3392 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 3393 const src = inst_data.src(); 3394 const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); 3395 3396 var names: Module.Fn.InferredErrorSet.NameMap = .{}; 3397 try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len); 3398 3399 var extra_index = @as(u32, @intCast(extra.end)); 3400 const extra_index_end = extra_index + (extra.data.fields_len * 2); 3401 while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string 3402 const str_index = sema.code.extra[extra_index]; 3403 const name = sema.code.nullTerminatedString(str_index); 3404 const name_ip = try mod.intern_pool.getOrPutString(gpa, name); 3405 _ = try mod.getErrorValue(name_ip); 3406 const result = names.getOrPutAssumeCapacity(name_ip); 3407 assert(!result.found_existing); // verified in AstGen 3408 } 3409 3410 const error_set_ty = try mod.errorSetFromUnsortedNames(names.keys()); 3411 3412 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 3413 .ty = Type.type, 3414 .val = error_set_ty.toValue(), 3415 }, name_strategy, "error", inst); 3416 const new_decl = mod.declPtr(new_decl_index); 3417 new_decl.owns_tv = true; 3418 errdefer mod.abortAnonDecl(new_decl_index); 3419 3420 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 3421 try mod.finalizeAnonDecl(new_decl_index); 3422 return decl_val; 3423 } 3424 3425 fn zirRetPtr(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 3426 const tracy = trace(@src()); 3427 defer tracy.end(); 3428 3429 if (block.is_comptime or try sema.typeRequiresComptime(sema.fn_ret_ty)) { 3430 const fn_ret_ty = try sema.resolveTypeFields(sema.fn_ret_ty); 3431 return sema.analyzeComptimeAlloc(block, fn_ret_ty, .none); 3432 } 3433 3434 const target = sema.mod.getTarget(); 3435 const ptr_type = try sema.mod.ptrType(.{ 3436 .child = sema.fn_ret_ty.toIntern(), 3437 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 3438 }); 3439 3440 if (block.inlining != null) { 3441 // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr. 3442 // TODO when functions gain result location support, the inlining struct in 3443 // Block should contain the return pointer, and we would pass that through here. 3444 return block.addTy(.alloc, ptr_type); 3445 } 3446 3447 return block.addTy(.ret_ptr, ptr_type); 3448 } 3449 3450 fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3451 const tracy = trace(@src()); 3452 defer tracy.end(); 3453 3454 const inst_data = sema.code.instructions.items(.data)[inst].un_tok; 3455 const operand = try sema.resolveInst(inst_data.operand); 3456 return sema.analyzeRef(block, inst_data.src(), operand); 3457 } 3458 3459 fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3460 const tracy = trace(@src()); 3461 defer tracy.end(); 3462 3463 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3464 const operand = try sema.resolveInst(inst_data.operand); 3465 const src = inst_data.src(); 3466 3467 return sema.ensureResultUsed(block, sema.typeOf(operand), src); 3468 } 3469 3470 fn ensureResultUsed( 3471 sema: *Sema, 3472 block: *Block, 3473 ty: Type, 3474 src: LazySrcLoc, 3475 ) CompileError!void { 3476 const mod = sema.mod; 3477 switch (ty.zigTypeTag(mod)) { 3478 .Void, .NoReturn => return, 3479 .ErrorSet, .ErrorUnion => { 3480 const msg = msg: { 3481 const msg = try sema.errMsg(block, src, "error is ignored", .{}); 3482 errdefer msg.destroy(sema.gpa); 3483 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); 3484 break :msg msg; 3485 }; 3486 return sema.failWithOwnedErrorMsg(msg); 3487 }, 3488 else => { 3489 const msg = msg: { 3490 const msg = try sema.errMsg(block, src, "value of type '{}' ignored", .{ty.fmt(sema.mod)}); 3491 errdefer msg.destroy(sema.gpa); 3492 try sema.errNote(block, src, msg, "all non-void values must be used", .{}); 3493 try sema.errNote(block, src, msg, "this error can be suppressed by assigning the value to '_'", .{}); 3494 break :msg msg; 3495 }; 3496 return sema.failWithOwnedErrorMsg(msg); 3497 }, 3498 } 3499 } 3500 3501 fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3502 const tracy = trace(@src()); 3503 defer tracy.end(); 3504 3505 const mod = sema.mod; 3506 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3507 const operand = try sema.resolveInst(inst_data.operand); 3508 const src = inst_data.src(); 3509 const operand_ty = sema.typeOf(operand); 3510 switch (operand_ty.zigTypeTag(mod)) { 3511 .ErrorSet, .ErrorUnion => { 3512 const msg = msg: { 3513 const msg = try sema.errMsg(block, src, "error is discarded", .{}); 3514 errdefer msg.destroy(sema.gpa); 3515 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); 3516 break :msg msg; 3517 }; 3518 return sema.failWithOwnedErrorMsg(msg); 3519 }, 3520 else => return, 3521 } 3522 } 3523 3524 fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3525 const tracy = trace(@src()); 3526 defer tracy.end(); 3527 3528 const mod = sema.mod; 3529 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3530 const src = inst_data.src(); 3531 const operand = try sema.resolveInst(inst_data.operand); 3532 const operand_ty = sema.typeOf(operand); 3533 const err_union_ty = if (operand_ty.zigTypeTag(mod) == .Pointer) 3534 operand_ty.childType(mod) 3535 else 3536 operand_ty; 3537 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) return; 3538 const payload_ty = err_union_ty.errorUnionPayload(mod).zigTypeTag(mod); 3539 if (payload_ty != .Void and payload_ty != .NoReturn) { 3540 const msg = msg: { 3541 const msg = try sema.errMsg(block, src, "error union payload is ignored", .{}); 3542 errdefer msg.destroy(sema.gpa); 3543 try sema.errNote(block, src, msg, "payload value can be explicitly ignored with '|_|'", .{}); 3544 break :msg msg; 3545 }; 3546 return sema.failWithOwnedErrorMsg(msg); 3547 } 3548 } 3549 3550 fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3551 const tracy = trace(@src()); 3552 defer tracy.end(); 3553 3554 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3555 const src = inst_data.src(); 3556 const object = try sema.resolveInst(inst_data.operand); 3557 3558 return indexablePtrLen(sema, block, src, object); 3559 } 3560 3561 fn indexablePtrLen( 3562 sema: *Sema, 3563 block: *Block, 3564 src: LazySrcLoc, 3565 object: Air.Inst.Ref, 3566 ) CompileError!Air.Inst.Ref { 3567 const mod = sema.mod; 3568 const object_ty = sema.typeOf(object); 3569 const is_pointer_to = object_ty.isSinglePointer(mod); 3570 const indexable_ty = if (is_pointer_to) object_ty.childType(mod) else object_ty; 3571 try checkIndexable(sema, block, src, indexable_ty); 3572 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len"); 3573 return sema.fieldVal(block, src, object, field_name, src); 3574 } 3575 3576 fn indexablePtrLenOrNone( 3577 sema: *Sema, 3578 block: *Block, 3579 src: LazySrcLoc, 3580 operand: Air.Inst.Ref, 3581 ) CompileError!Air.Inst.Ref { 3582 const mod = sema.mod; 3583 const operand_ty = sema.typeOf(operand); 3584 try checkMemOperand(sema, block, src, operand_ty); 3585 if (operand_ty.ptrSize(mod) == .Many) return .none; 3586 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len"); 3587 return sema.fieldVal(block, src, operand, field_name, src); 3588 } 3589 3590 fn zirAllocExtended( 3591 sema: *Sema, 3592 block: *Block, 3593 extended: Zir.Inst.Extended.InstData, 3594 ) CompileError!Air.Inst.Ref { 3595 const gpa = sema.gpa; 3596 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); 3597 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = extra.data.src_node }; 3598 const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = extra.data.src_node }; 3599 const small = @as(Zir.Inst.AllocExtended.Small, @bitCast(extended.small)); 3600 3601 var extra_index: usize = extra.end; 3602 3603 const var_ty: Type = if (small.has_type) blk: { 3604 const type_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 3605 extra_index += 1; 3606 break :blk try sema.resolveType(block, ty_src, type_ref); 3607 } else undefined; 3608 3609 const alignment = if (small.has_align) blk: { 3610 const align_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 3611 extra_index += 1; 3612 const alignment = try sema.resolveAlign(block, align_src, align_ref); 3613 break :blk alignment; 3614 } else .none; 3615 3616 if (block.is_comptime or small.is_comptime) { 3617 if (small.has_type) { 3618 return sema.analyzeComptimeAlloc(block, var_ty, alignment); 3619 } else { 3620 try sema.air_instructions.append(gpa, .{ 3621 .tag = .inferred_alloc_comptime, 3622 .data = .{ .inferred_alloc_comptime = .{ 3623 .decl_index = undefined, 3624 .alignment = alignment, 3625 .is_const = small.is_const, 3626 } }, 3627 }); 3628 return Air.indexToRef(@as(u32, @intCast(sema.air_instructions.len - 1))); 3629 } 3630 } 3631 3632 if (small.has_type) { 3633 if (!small.is_const) { 3634 try sema.validateVarType(block, ty_src, var_ty, false); 3635 } 3636 const target = sema.mod.getTarget(); 3637 try sema.resolveTypeLayout(var_ty); 3638 const ptr_type = try sema.mod.ptrType(.{ 3639 .child = var_ty.toIntern(), 3640 .flags = .{ 3641 .alignment = alignment, 3642 .address_space = target_util.defaultAddressSpace(target, .local), 3643 }, 3644 }); 3645 return block.addTy(.alloc, ptr_type); 3646 } 3647 3648 const result_index = try block.addInstAsIndex(.{ 3649 .tag = .inferred_alloc, 3650 .data = .{ .inferred_alloc = .{ 3651 .alignment = alignment, 3652 .is_const = small.is_const, 3653 } }, 3654 }); 3655 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); 3656 return Air.indexToRef(result_index); 3657 } 3658 3659 fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3660 const tracy = trace(@src()); 3661 defer tracy.end(); 3662 3663 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3664 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; 3665 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 3666 return sema.analyzeComptimeAlloc(block, var_ty, .none); 3667 } 3668 3669 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3670 const mod = sema.mod; 3671 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3672 const alloc = try sema.resolveInst(inst_data.operand); 3673 const alloc_ty = sema.typeOf(alloc); 3674 3675 var ptr_info = alloc_ty.ptrInfo(mod); 3676 const elem_ty = ptr_info.child.toType(); 3677 3678 // Detect if all stores to an `.alloc` were comptime-known. 3679 ct: { 3680 var search_index: usize = block.instructions.items.len; 3681 const air_tags = sema.air_instructions.items(.tag); 3682 const air_datas = sema.air_instructions.items(.data); 3683 3684 const store_inst = while (true) { 3685 if (search_index == 0) break :ct; 3686 search_index -= 1; 3687 3688 const candidate = block.instructions.items[search_index]; 3689 switch (air_tags[candidate]) { 3690 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, 3691 .store, .store_safe => break candidate, 3692 else => break :ct, 3693 } 3694 }; 3695 3696 while (true) { 3697 if (search_index == 0) break :ct; 3698 search_index -= 1; 3699 3700 const candidate = block.instructions.items[search_index]; 3701 switch (air_tags[candidate]) { 3702 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, 3703 .alloc => { 3704 if (Air.indexToRef(candidate) != alloc) break :ct; 3705 break; 3706 }, 3707 else => break :ct, 3708 } 3709 } 3710 3711 const store_op = air_datas[store_inst].bin_op; 3712 const store_val = (try sema.resolveMaybeUndefVal(store_op.rhs)) orelse break :ct; 3713 if (store_op.lhs != alloc) break :ct; 3714 3715 // Remove all the unnecessary runtime instructions. 3716 block.instructions.shrinkRetainingCapacity(search_index); 3717 3718 var anon_decl = try block.startAnonDecl(); 3719 defer anon_decl.deinit(); 3720 return sema.analyzeDeclRef(try anon_decl.finish(elem_ty, store_val, ptr_info.flags.alignment)); 3721 } 3722 3723 return sema.makePtrConst(block, alloc); 3724 } 3725 3726 fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { 3727 const mod = sema.mod; 3728 const alloc_ty = sema.typeOf(alloc); 3729 3730 var ptr_info = alloc_ty.ptrInfo(mod); 3731 ptr_info.flags.is_const = true; 3732 const const_ptr_ty = try mod.ptrType(ptr_info); 3733 3734 // Detect if a comptime value simply needs to have its type changed. 3735 if (try sema.resolveMaybeUndefVal(alloc)) |val| { 3736 return sema.addConstant(try mod.getCoerced(val, const_ptr_ty)); 3737 } 3738 3739 return block.addBitCast(const_ptr_ty, alloc); 3740 } 3741 3742 fn zirAllocInferredComptime( 3743 sema: *Sema, 3744 inst: Zir.Inst.Index, 3745 is_const: bool, 3746 ) CompileError!Air.Inst.Ref { 3747 const gpa = sema.gpa; 3748 const src_node = sema.code.instructions.items(.data)[inst].node; 3749 const src = LazySrcLoc.nodeOffset(src_node); 3750 sema.src = src; 3751 3752 try sema.air_instructions.append(gpa, .{ 3753 .tag = .inferred_alloc_comptime, 3754 .data = .{ .inferred_alloc_comptime = .{ 3755 .decl_index = undefined, 3756 .alignment = .none, 3757 .is_const = is_const, 3758 } }, 3759 }); 3760 return Air.indexToRef(@as(u32, @intCast(sema.air_instructions.len - 1))); 3761 } 3762 3763 fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3764 const tracy = trace(@src()); 3765 defer tracy.end(); 3766 3767 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3768 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; 3769 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 3770 if (block.is_comptime) { 3771 return sema.analyzeComptimeAlloc(block, var_ty, .none); 3772 } 3773 const target = sema.mod.getTarget(); 3774 const ptr_type = try sema.mod.ptrType(.{ 3775 .child = var_ty.toIntern(), 3776 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 3777 }); 3778 try sema.queueFullTypeResolution(var_ty); 3779 return block.addTy(.alloc, ptr_type); 3780 } 3781 3782 fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 3783 const tracy = trace(@src()); 3784 defer tracy.end(); 3785 3786 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3787 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; 3788 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); 3789 if (block.is_comptime) { 3790 return sema.analyzeComptimeAlloc(block, var_ty, .none); 3791 } 3792 try sema.validateVarType(block, ty_src, var_ty, false); 3793 const target = sema.mod.getTarget(); 3794 const ptr_type = try sema.mod.ptrType(.{ 3795 .child = var_ty.toIntern(), 3796 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 3797 }); 3798 try sema.queueFullTypeResolution(var_ty); 3799 return block.addTy(.alloc, ptr_type); 3800 } 3801 3802 fn zirAllocInferred( 3803 sema: *Sema, 3804 block: *Block, 3805 inst: Zir.Inst.Index, 3806 is_const: bool, 3807 ) CompileError!Air.Inst.Ref { 3808 const tracy = trace(@src()); 3809 defer tracy.end(); 3810 3811 const gpa = sema.gpa; 3812 const src_node = sema.code.instructions.items(.data)[inst].node; 3813 const src = LazySrcLoc.nodeOffset(src_node); 3814 sema.src = src; 3815 3816 if (block.is_comptime) { 3817 try sema.air_instructions.append(gpa, .{ 3818 .tag = .inferred_alloc_comptime, 3819 .data = .{ .inferred_alloc_comptime = .{ 3820 .decl_index = undefined, 3821 .alignment = .none, 3822 .is_const = is_const, 3823 } }, 3824 }); 3825 return Air.indexToRef(@as(u32, @intCast(sema.air_instructions.len - 1))); 3826 } 3827 3828 const result_index = try block.addInstAsIndex(.{ 3829 .tag = .inferred_alloc, 3830 .data = .{ .inferred_alloc = .{ 3831 .alignment = .none, 3832 .is_const = is_const, 3833 } }, 3834 }); 3835 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); 3836 return Air.indexToRef(result_index); 3837 } 3838 3839 fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 3840 const tracy = trace(@src()); 3841 defer tracy.end(); 3842 3843 const mod = sema.mod; 3844 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 3845 const src = inst_data.src(); 3846 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; 3847 const ptr = try sema.resolveInst(inst_data.operand); 3848 const ptr_inst = Air.refToIndex(ptr).?; 3849 const target = mod.getTarget(); 3850 3851 switch (sema.air_instructions.items(.tag)[ptr_inst]) { 3852 .inferred_alloc_comptime => { 3853 const iac = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime; 3854 const decl_index = iac.decl_index; 3855 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 3856 3857 const decl = mod.declPtr(decl_index); 3858 if (iac.is_const) try decl.intern(mod); 3859 const final_elem_ty = decl.ty; 3860 const final_ptr_ty = try mod.ptrType(.{ 3861 .child = final_elem_ty.toIntern(), 3862 .flags = .{ 3863 .is_const = false, 3864 .alignment = iac.alignment, 3865 .address_space = target_util.defaultAddressSpace(target, .local), 3866 }, 3867 }); 3868 3869 if (std.debug.runtime_safety) { 3870 // The inferred_alloc_comptime should never be referenced again 3871 sema.air_instructions.set(ptr_inst, .{ .tag = undefined, .data = undefined }); 3872 } 3873 3874 try sema.maybeQueueFuncBodyAnalysis(decl_index); 3875 3876 const interned = try mod.intern(.{ .ptr = .{ 3877 .ty = final_ptr_ty.toIntern(), 3878 .addr = if (!iac.is_const) .{ .mut_decl = .{ 3879 .decl = decl_index, 3880 .runtime_index = block.runtime_index, 3881 } } else .{ .decl = decl_index }, 3882 } }); 3883 3884 // Remap the ZIR operand to the resolved pointer value 3885 sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(interned)); 3886 }, 3887 .inferred_alloc => { 3888 const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc; 3889 const ia2 = sema.unresolved_inferred_allocs.fetchRemove(ptr_inst).?.value; 3890 const peer_inst_list = ia2.prongs.items(.stored_inst); 3891 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none); 3892 3893 const final_ptr_ty = try mod.ptrType(.{ 3894 .child = final_elem_ty.toIntern(), 3895 .flags = .{ 3896 .alignment = ia1.alignment, 3897 .address_space = target_util.defaultAddressSpace(target, .local), 3898 }, 3899 }); 3900 3901 if (!ia1.is_const) { 3902 try sema.validateVarType(block, ty_src, final_elem_ty, false); 3903 } else ct: { 3904 // Detect if the value is comptime-known. In such case, the 3905 // last 3 AIR instructions of the block will look like this: 3906 // 3907 // %a = inferred_alloc 3908 // %b = bitcast(%a) 3909 // %c = store(%b, %d) 3910 // 3911 // If `%d` is comptime-known, then we want to store the value 3912 // inside an anonymous Decl and then erase these three AIR 3913 // instructions from the block, replacing the inst_map entry 3914 // corresponding to the ZIR alloc instruction with a constant 3915 // decl_ref pointing at our new Decl. 3916 // dbg_stmt instructions may be interspersed into this pattern 3917 // which must be ignored. 3918 if (block.instructions.items.len < 3) break :ct; 3919 var search_index: usize = block.instructions.items.len; 3920 const air_tags = sema.air_instructions.items(.tag); 3921 const air_datas = sema.air_instructions.items(.data); 3922 3923 const store_inst = while (true) { 3924 if (search_index == 0) break :ct; 3925 search_index -= 1; 3926 3927 const candidate = block.instructions.items[search_index]; 3928 switch (air_tags[candidate]) { 3929 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, 3930 .store, .store_safe => break candidate, 3931 else => break :ct, 3932 } 3933 }; 3934 3935 const bitcast_inst = while (true) { 3936 if (search_index == 0) break :ct; 3937 search_index -= 1; 3938 3939 const candidate = block.instructions.items[search_index]; 3940 switch (air_tags[candidate]) { 3941 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, 3942 .bitcast => break candidate, 3943 else => break :ct, 3944 } 3945 }; 3946 3947 while (true) { 3948 if (search_index == 0) break :ct; 3949 search_index -= 1; 3950 3951 const candidate = block.instructions.items[search_index]; 3952 if (candidate == ptr_inst) break; 3953 switch (air_tags[candidate]) { 3954 .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, 3955 else => break :ct, 3956 } 3957 } 3958 3959 const store_op = air_datas[store_inst].bin_op; 3960 const store_val = (try sema.resolveMaybeUndefVal(store_op.rhs)) orelse break :ct; 3961 if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct; 3962 if (air_datas[bitcast_inst].ty_op.operand != ptr) break :ct; 3963 3964 const new_decl_index = d: { 3965 var anon_decl = try block.startAnonDecl(); 3966 defer anon_decl.deinit(); 3967 const new_decl_index = try anon_decl.finish(final_elem_ty, store_val, ia1.alignment); 3968 break :d new_decl_index; 3969 }; 3970 try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); 3971 3972 // Remove the instruction from the block so that codegen does not see it. 3973 block.instructions.shrinkRetainingCapacity(search_index); 3974 try sema.maybeQueueFuncBodyAnalysis(new_decl_index); 3975 3976 if (std.debug.runtime_safety) { 3977 // The inferred_alloc should never be referenced again 3978 sema.air_instructions.set(ptr_inst, .{ .tag = undefined, .data = undefined }); 3979 } 3980 3981 const interned = try mod.intern(.{ .ptr = .{ 3982 .ty = final_ptr_ty.toIntern(), 3983 .addr = .{ .decl = new_decl_index }, 3984 } }); 3985 3986 // Remap the ZIR oeprand to the resolved pointer value 3987 sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(interned)); 3988 3989 // Unless the block is comptime, `alloc_inferred` always produces 3990 // a runtime constant. The final inferred type needs to be 3991 // fully resolved so it can be lowered in codegen. 3992 try sema.resolveTypeFully(final_elem_ty); 3993 3994 return; 3995 } 3996 3997 try sema.queueFullTypeResolution(final_elem_ty); 3998 3999 // Change it to a normal alloc. 4000 sema.air_instructions.set(ptr_inst, .{ 4001 .tag = .alloc, 4002 .data = .{ .ty = final_ptr_ty }, 4003 }); 4004 4005 // Now we need to go back over all the coerce_result_ptr instructions, which 4006 // previously inserted a bitcast as a placeholder, and do the logic as if 4007 // the new result ptr type was available. 4008 const placeholders = ia2.prongs.items(.placeholder); 4009 const gpa = sema.gpa; 4010 4011 var trash_block = block.makeSubBlock(); 4012 trash_block.is_comptime = false; 4013 defer trash_block.instructions.deinit(gpa); 4014 4015 const mut_final_ptr_ty = try mod.ptrType(.{ 4016 .child = final_elem_ty.toIntern(), 4017 .flags = .{ 4018 .alignment = ia1.alignment, 4019 .address_space = target_util.defaultAddressSpace(target, .local), 4020 }, 4021 }); 4022 const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty); 4023 const empty_trash_count = trash_block.instructions.items.len; 4024 4025 for (peer_inst_list, placeholders) |peer_inst, placeholder_inst| { 4026 const sub_ptr_ty = sema.typeOf(Air.indexToRef(placeholder_inst)); 4027 4028 if (mut_final_ptr_ty.eql(sub_ptr_ty, mod)) { 4029 // New result location type is the same as the old one; nothing 4030 // to do here. 4031 continue; 4032 } 4033 4034 var replacement_block = block.makeSubBlock(); 4035 defer replacement_block.instructions.deinit(gpa); 4036 4037 const result = switch (sema.air_instructions.items(.tag)[placeholder_inst]) { 4038 .bitcast => result: { 4039 trash_block.instructions.shrinkRetainingCapacity(empty_trash_count); 4040 const sub_ptr = try sema.coerceResultPtr(&replacement_block, src, ptr, dummy_ptr, peer_inst, &trash_block); 4041 4042 assert(replacement_block.instructions.items.len > 0); 4043 break :result sub_ptr; 4044 }, 4045 .store, .store_safe => result: { 4046 const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op; 4047 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast); 4048 break :result .void_value; 4049 }, 4050 else => unreachable, 4051 }; 4052 4053 // If only one instruction is produced then we can replace the bitcast 4054 // placeholder instruction with this instruction; no need for an entire block. 4055 if (replacement_block.instructions.items.len == 1) { 4056 const only_inst = replacement_block.instructions.items[0]; 4057 sema.air_instructions.set(placeholder_inst, sema.air_instructions.get(only_inst)); 4058 continue; 4059 } 4060 4061 // Here we replace the placeholder bitcast instruction with a block 4062 // that does the coerce_result_ptr logic. 4063 _ = try replacement_block.addBr(placeholder_inst, result); 4064 const ty_inst = if (result == .void_value) 4065 .void_type 4066 else 4067 sema.air_instructions.items(.data)[placeholder_inst].ty_op.ty; 4068 try sema.air_extra.ensureUnusedCapacity( 4069 gpa, 4070 @typeInfo(Air.Block).Struct.fields.len + replacement_block.instructions.items.len, 4071 ); 4072 sema.air_instructions.set(placeholder_inst, .{ 4073 .tag = .block, 4074 .data = .{ .ty_pl = .{ 4075 .ty = ty_inst, 4076 .payload = sema.addExtraAssumeCapacity(Air.Block{ 4077 .body_len = @as(u32, @intCast(replacement_block.instructions.items.len)), 4078 }), 4079 } }, 4080 }); 4081 sema.air_extra.appendSliceAssumeCapacity(replacement_block.instructions.items); 4082 } 4083 }, 4084 else => unreachable, 4085 } 4086 } 4087 4088 fn zirArrayBasePtr( 4089 sema: *Sema, 4090 block: *Block, 4091 inst: Zir.Inst.Index, 4092 ) CompileError!Air.Inst.Ref { 4093 const mod = sema.mod; 4094 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 4095 const src = inst_data.src(); 4096 4097 const start_ptr = try sema.resolveInst(inst_data.operand); 4098 var base_ptr = start_ptr; 4099 while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) { 4100 .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), 4101 .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), 4102 else => break, 4103 }; 4104 4105 const elem_ty = sema.typeOf(base_ptr).childType(mod); 4106 switch (elem_ty.zigTypeTag(mod)) { 4107 .Array, .Vector => return base_ptr, 4108 .Struct => if (elem_ty.isTuple(mod)) { 4109 // TODO validate element count 4110 return base_ptr; 4111 }, 4112 else => {}, 4113 } 4114 return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); 4115 } 4116 4117 fn zirFieldBasePtr( 4118 sema: *Sema, 4119 block: *Block, 4120 inst: Zir.Inst.Index, 4121 ) CompileError!Air.Inst.Ref { 4122 const mod = sema.mod; 4123 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 4124 const src = inst_data.src(); 4125 4126 const start_ptr = try sema.resolveInst(inst_data.operand); 4127 var base_ptr = start_ptr; 4128 while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) { 4129 .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), 4130 .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), 4131 else => break, 4132 }; 4133 4134 const elem_ty = sema.typeOf(base_ptr).childType(mod); 4135 switch (elem_ty.zigTypeTag(mod)) { 4136 .Struct, .Union => return base_ptr, 4137 else => {}, 4138 } 4139 return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); 4140 } 4141 4142 fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 4143 const mod = sema.mod; 4144 const gpa = sema.gpa; 4145 const ip = &mod.intern_pool; 4146 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 4147 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 4148 const args = sema.code.refSlice(extra.end, extra.data.operands_len); 4149 const src = inst_data.src(); 4150 4151 var len: Air.Inst.Ref = .none; 4152 var len_val: ?Value = null; 4153 var len_idx: u32 = undefined; 4154 var any_runtime = false; 4155 4156 const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, args.len); 4157 defer gpa.free(runtime_arg_lens); 4158 4159 // First pass to look for comptime values. 4160 for (args, 0..) |zir_arg, i_usize| { 4161 const i = @as(u32, @intCast(i_usize)); 4162 runtime_arg_lens[i] = .none; 4163 if (zir_arg == .none) continue; 4164 const object = try sema.resolveInst(zir_arg); 4165 const object_ty = sema.typeOf(object); 4166 // Each arg could be an indexable, or a range, in which case the length 4167 // is passed directly as an integer. 4168 const is_int = switch (object_ty.zigTypeTag(mod)) { 4169 .Int, .ComptimeInt => true, 4170 else => false, 4171 }; 4172 const arg_src: LazySrcLoc = .{ .for_input = .{ 4173 .for_node_offset = inst_data.src_node, 4174 .input_index = i, 4175 } }; 4176 const arg_len_uncoerced = if (is_int) object else l: { 4177 if (!object_ty.isIndexable(mod)) { 4178 // Instead of using checkIndexable we customize this error. 4179 const msg = msg: { 4180 const msg = try sema.errMsg(block, arg_src, "type '{}' is not indexable and not a range", .{object_ty.fmt(sema.mod)}); 4181 errdefer msg.destroy(sema.gpa); 4182 try sema.errNote(block, arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{}); 4183 break :msg msg; 4184 }; 4185 return sema.failWithOwnedErrorMsg(msg); 4186 } 4187 if (!object_ty.indexableHasLen(mod)) continue; 4188 4189 break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, "len"), arg_src); 4190 }; 4191 const arg_len = try sema.coerce(block, Type.usize, arg_len_uncoerced, arg_src); 4192 if (len == .none) { 4193 len = arg_len; 4194 len_idx = i; 4195 } 4196 if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| { 4197 if (len_val) |v| { 4198 if (!(try sema.valuesEqual(arg_val, v, Type.usize))) { 4199 const msg = msg: { 4200 const msg = try sema.errMsg(block, src, "non-matching for loop lengths", .{}); 4201 errdefer msg.destroy(gpa); 4202 const a_src: LazySrcLoc = .{ .for_input = .{ 4203 .for_node_offset = inst_data.src_node, 4204 .input_index = len_idx, 4205 } }; 4206 try sema.errNote(block, a_src, msg, "length {} here", .{ 4207 v.fmtValue(Type.usize, sema.mod), 4208 }); 4209 try sema.errNote(block, arg_src, msg, "length {} here", .{ 4210 arg_val.fmtValue(Type.usize, sema.mod), 4211 }); 4212 break :msg msg; 4213 }; 4214 return sema.failWithOwnedErrorMsg(msg); 4215 } 4216 } else { 4217 len = arg_len; 4218 len_val = arg_val; 4219 len_idx = i; 4220 } 4221 continue; 4222 } 4223 runtime_arg_lens[i] = arg_len; 4224 any_runtime = true; 4225 } 4226 4227 if (len == .none) { 4228 const msg = msg: { 4229 const msg = try sema.errMsg(block, src, "unbounded for loop", .{}); 4230 errdefer msg.destroy(gpa); 4231 for (args, 0..) |zir_arg, i_usize| { 4232 const i = @as(u32, @intCast(i_usize)); 4233 if (zir_arg == .none) continue; 4234 const object = try sema.resolveInst(zir_arg); 4235 const object_ty = sema.typeOf(object); 4236 // Each arg could be an indexable, or a range, in which case the length 4237 // is passed directly as an integer. 4238 switch (object_ty.zigTypeTag(mod)) { 4239 .Int, .ComptimeInt => continue, 4240 else => {}, 4241 } 4242 const arg_src: LazySrcLoc = .{ .for_input = .{ 4243 .for_node_offset = inst_data.src_node, 4244 .input_index = i, 4245 } }; 4246 try sema.errNote(block, arg_src, msg, "type '{}' has no upper bound", .{ 4247 object_ty.fmt(sema.mod), 4248 }); 4249 } 4250 break :msg msg; 4251 }; 4252 return sema.failWithOwnedErrorMsg(msg); 4253 } 4254 4255 // Now for the runtime checks. 4256 if (any_runtime and block.wantSafety()) { 4257 for (runtime_arg_lens, 0..) |arg_len, i| { 4258 if (arg_len == .none) continue; 4259 if (i == len_idx) continue; 4260 const ok = try block.addBinOp(.cmp_eq, len, arg_len); 4261 try sema.addSafetyCheck(block, ok, .for_len_mismatch); 4262 } 4263 } 4264 4265 return len; 4266 } 4267 4268 fn validateArrayInitTy( 4269 sema: *Sema, 4270 block: *Block, 4271 inst: Zir.Inst.Index, 4272 ) CompileError!void { 4273 const mod = sema.mod; 4274 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 4275 const src = inst_data.src(); 4276 const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node }; 4277 const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data; 4278 const ty = try sema.resolveType(block, ty_src, extra.ty); 4279 4280 switch (ty.zigTypeTag(mod)) { 4281 .Array => { 4282 const array_len = ty.arrayLen(mod); 4283 if (extra.init_count != array_len) { 4284 return sema.fail(block, src, "expected {d} array elements; found {d}", .{ 4285 array_len, extra.init_count, 4286 }); 4287 } 4288 return; 4289 }, 4290 .Vector => { 4291 const array_len = ty.arrayLen(mod); 4292 if (extra.init_count != array_len) { 4293 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{ 4294 array_len, extra.init_count, 4295 }); 4296 } 4297 return; 4298 }, 4299 .Struct => if (ty.isTuple(mod)) { 4300 _ = try sema.resolveTypeFields(ty); 4301 const array_len = ty.arrayLen(mod); 4302 if (extra.init_count > array_len) { 4303 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ 4304 array_len, extra.init_count, 4305 }); 4306 } 4307 return; 4308 }, 4309 else => {}, 4310 } 4311 return sema.failWithArrayInitNotSupported(block, ty_src, ty); 4312 } 4313 4314 fn validateStructInitTy( 4315 sema: *Sema, 4316 block: *Block, 4317 inst: Zir.Inst.Index, 4318 ) CompileError!void { 4319 const mod = sema.mod; 4320 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 4321 const src = inst_data.src(); 4322 const ty = try sema.resolveType(block, src, inst_data.operand); 4323 4324 switch (ty.zigTypeTag(mod)) { 4325 .Struct, .Union => return, 4326 else => {}, 4327 } 4328 return sema.failWithStructInitNotSupported(block, src, ty); 4329 } 4330 4331 fn zirValidateStructInit( 4332 sema: *Sema, 4333 block: *Block, 4334 inst: Zir.Inst.Index, 4335 ) CompileError!void { 4336 const tracy = trace(@src()); 4337 defer tracy.end(); 4338 4339 const mod = sema.mod; 4340 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node; 4341 const init_src = validate_inst.src(); 4342 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); 4343 const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len]; 4344 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; 4345 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4346 const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); 4347 const agg_ty = sema.typeOf(object_ptr).childType(mod); 4348 switch (agg_ty.zigTypeTag(mod)) { 4349 .Struct => return sema.validateStructInit( 4350 block, 4351 agg_ty, 4352 init_src, 4353 instrs, 4354 ), 4355 .Union => return sema.validateUnionInit( 4356 block, 4357 agg_ty, 4358 init_src, 4359 instrs, 4360 object_ptr, 4361 ), 4362 else => unreachable, 4363 } 4364 } 4365 4366 fn validateUnionInit( 4367 sema: *Sema, 4368 block: *Block, 4369 union_ty: Type, 4370 init_src: LazySrcLoc, 4371 instrs: []const Zir.Inst.Index, 4372 union_ptr: Air.Inst.Ref, 4373 ) CompileError!void { 4374 const mod = sema.mod; 4375 const gpa = sema.gpa; 4376 4377 if (instrs.len != 1) { 4378 const msg = msg: { 4379 const msg = try sema.errMsg( 4380 block, 4381 init_src, 4382 "cannot initialize multiple union fields at once; unions can only have one active field", 4383 .{}, 4384 ); 4385 errdefer msg.destroy(gpa); 4386 4387 for (instrs[1..]) |inst| { 4388 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 4389 const inst_src: LazySrcLoc = .{ .node_offset_initializer = inst_data.src_node }; 4390 try sema.errNote(block, inst_src, msg, "additional initializer here", .{}); 4391 } 4392 try sema.addDeclaredHereNote(msg, union_ty); 4393 break :msg msg; 4394 }; 4395 return sema.failWithOwnedErrorMsg(msg); 4396 } 4397 4398 if (block.is_comptime and 4399 (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null) 4400 { 4401 // In this case, comptime machinery already did everything. No work to do here. 4402 return; 4403 } 4404 4405 const field_ptr = instrs[0]; 4406 const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; 4407 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node }; 4408 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4409 const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start)); 4410 // Validate the field access but ignore the index since we want the tag enum field index. 4411 _ = try sema.unionFieldIndex(block, union_ty, field_name, field_src); 4412 const air_tags = sema.air_instructions.items(.tag); 4413 const air_datas = sema.air_instructions.items(.data); 4414 const field_ptr_air_ref = sema.inst_map.get(field_ptr).?; 4415 4416 // Our task here is to determine if the union is comptime-known. In such case, 4417 // we erase the runtime AIR instructions for initializing the union, and replace 4418 // the mapping with the comptime value. Either way, we will need to populate the tag. 4419 4420 // We expect to see something like this in the current block AIR: 4421 // %a = alloc(*const U) 4422 // %b = bitcast(*U, %a) 4423 // %c = field_ptr(..., %b) 4424 // %e!= store(%c!, %d!) 4425 // If %d is a comptime operand, the union is comptime. 4426 // If the union is comptime, we want `first_block_index` 4427 // to point at %c so that the bitcast becomes the last instruction in the block. 4428 // 4429 // In the case of a comptime-known pointer to a union, the 4430 // the field_ptr instruction is missing, so we have to pattern-match 4431 // based only on the store instructions. 4432 // `first_block_index` needs to point to the `field_ptr` if it exists; 4433 // the `store` otherwise. 4434 // 4435 // It's also possible for there to be no store instruction, in the case 4436 // of nested `coerce_result_ptr` instructions. If we see the `field_ptr` 4437 // but we have not found a `store`, treat as a runtime-known field. 4438 var first_block_index = block.instructions.items.len; 4439 var block_index = block.instructions.items.len - 1; 4440 var init_val: ?Value = null; 4441 var make_runtime = false; 4442 while (block_index > 0) : (block_index -= 1) { 4443 const store_inst = block.instructions.items[block_index]; 4444 if (Air.indexToRef(store_inst) == field_ptr_air_ref) break; 4445 switch (air_tags[store_inst]) { 4446 .store, .store_safe => {}, 4447 else => continue, 4448 } 4449 const bin_op = air_datas[store_inst].bin_op; 4450 var lhs = bin_op.lhs; 4451 if (Air.refToIndex(lhs)) |lhs_index| { 4452 if (air_tags[lhs_index] == .bitcast) { 4453 lhs = air_datas[lhs_index].ty_op.operand; 4454 block_index -= 1; 4455 } 4456 } 4457 if (lhs != field_ptr_air_ref) continue; 4458 while (block_index > 0) : (block_index -= 1) { 4459 const block_inst = block.instructions.items[block_index - 1]; 4460 if (air_tags[block_inst] != .dbg_stmt) break; 4461 } 4462 if (block_index > 0 and 4463 field_ptr_air_ref == Air.indexToRef(block.instructions.items[block_index - 1])) 4464 { 4465 first_block_index = @min(first_block_index, block_index - 1); 4466 } else { 4467 first_block_index = @min(first_block_index, block_index); 4468 } 4469 init_val = try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(bin_op.rhs, &make_runtime); 4470 break; 4471 } 4472 4473 const tag_ty = union_ty.unionTagTypeHypothetical(mod); 4474 const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?)); 4475 const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); 4476 4477 if (init_val) |val| { 4478 // Our task is to delete all the `field_ptr` and `store` instructions, and insert 4479 // instead a single `store` to the result ptr with a comptime union value. 4480 block.instructions.shrinkRetainingCapacity(first_block_index); 4481 4482 var union_val = try mod.intern(.{ .un = .{ 4483 .ty = union_ty.toIntern(), 4484 .tag = tag_val.toIntern(), 4485 .val = val.toIntern(), 4486 } }); 4487 if (make_runtime) union_val = try mod.intern(.{ .runtime_value = .{ 4488 .ty = union_ty.toIntern(), 4489 .val = union_val, 4490 } }); 4491 const union_init = try sema.addConstant(union_val.toValue()); 4492 try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store); 4493 return; 4494 } else if (try sema.typeRequiresComptime(union_ty)) { 4495 return sema.failWithNeededComptime(block, field_ptr_data.src(), "initializer of comptime only union must be comptime-known"); 4496 } 4497 4498 const new_tag = try sema.addConstant(tag_val); 4499 _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag); 4500 } 4501 4502 fn validateStructInit( 4503 sema: *Sema, 4504 block: *Block, 4505 struct_ty: Type, 4506 init_src: LazySrcLoc, 4507 instrs: []const Zir.Inst.Index, 4508 ) CompileError!void { 4509 const mod = sema.mod; 4510 const gpa = sema.gpa; 4511 const ip = &mod.intern_pool; 4512 4513 // Maps field index to field_ptr index of where it was already initialized. 4514 const found_fields = try gpa.alloc(Zir.Inst.Index, struct_ty.structFieldCount(mod)); 4515 defer gpa.free(found_fields); 4516 @memset(found_fields, 0); 4517 4518 var struct_ptr_zir_ref: Zir.Inst.Ref = undefined; 4519 4520 for (instrs) |field_ptr| { 4521 const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; 4522 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node }; 4523 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; 4524 struct_ptr_zir_ref = field_ptr_extra.lhs; 4525 const field_name = try ip.getOrPutString( 4526 gpa, 4527 sema.code.nullTerminatedString(field_ptr_extra.field_name_start), 4528 ); 4529 const field_index = if (struct_ty.isTuple(mod)) 4530 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src) 4531 else 4532 try sema.structFieldIndex(block, struct_ty, field_name, field_src); 4533 if (found_fields[field_index] != 0) { 4534 const other_field_ptr = found_fields[field_index]; 4535 const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node; 4536 const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_ptr_data.src_node }; 4537 const msg = msg: { 4538 const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); 4539 errdefer msg.destroy(gpa); 4540 try sema.errNote(block, other_field_src, msg, "other field here", .{}); 4541 break :msg msg; 4542 }; 4543 return sema.failWithOwnedErrorMsg(msg); 4544 } 4545 found_fields[field_index] = field_ptr; 4546 } 4547 4548 var root_msg: ?*Module.ErrorMsg = null; 4549 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 4550 4551 const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref); 4552 if (block.is_comptime and 4553 (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null) 4554 { 4555 try sema.resolveStructLayout(struct_ty); 4556 // In this case the only thing we need to do is evaluate the implicit 4557 // store instructions for default field values, and report any missing fields. 4558 // Avoid the cost of the extra machinery for detecting a comptime struct init value. 4559 for (found_fields, 0..) |field_ptr, i| { 4560 if (field_ptr != 0) continue; 4561 4562 const default_val = struct_ty.structFieldDefaultValue(i, mod); 4563 if (default_val.toIntern() == .unreachable_value) { 4564 if (struct_ty.isTuple(mod)) { 4565 const template = "missing tuple field with index {d}"; 4566 if (root_msg) |msg| { 4567 try sema.errNote(block, init_src, msg, template, .{i}); 4568 } else { 4569 root_msg = try sema.errMsg(block, init_src, template, .{i}); 4570 } 4571 continue; 4572 } 4573 const field_name = struct_ty.structFieldName(i, mod); 4574 const template = "missing struct field: {}"; 4575 const args = .{field_name.fmt(ip)}; 4576 if (root_msg) |msg| { 4577 try sema.errNote(block, init_src, msg, template, args); 4578 } else { 4579 root_msg = try sema.errMsg(block, init_src, template, args); 4580 } 4581 continue; 4582 } 4583 4584 const field_src = init_src; // TODO better source location 4585 const default_field_ptr = if (struct_ty.isTuple(mod)) 4586 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @as(u32, @intCast(i)), true) 4587 else 4588 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @as(u32, @intCast(i)), field_src, struct_ty, true); 4589 const init = try sema.addConstant(default_val); 4590 try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); 4591 } 4592 4593 if (root_msg) |msg| { 4594 if (mod.typeToStruct(struct_ty)) |struct_obj| { 4595 const fqn = try struct_obj.getFullyQualifiedName(mod); 4596 try mod.errNoteNonLazy( 4597 struct_obj.srcLoc(mod), 4598 msg, 4599 "struct '{}' declared here", 4600 .{fqn.fmt(ip)}, 4601 ); 4602 } 4603 root_msg = null; 4604 return sema.failWithOwnedErrorMsg(msg); 4605 } 4606 4607 return; 4608 } 4609 4610 var struct_is_comptime = true; 4611 var first_block_index = block.instructions.items.len; 4612 var make_runtime = false; 4613 4614 const require_comptime = try sema.typeRequiresComptime(struct_ty); 4615 const air_tags = sema.air_instructions.items(.tag); 4616 const air_datas = sema.air_instructions.items(.data); 4617 4618 // We collect the comptime field values in case the struct initialization 4619 // ends up being comptime-known. 4620 const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod)); 4621 4622 field: for (found_fields, 0..) |field_ptr, i| { 4623 if (field_ptr != 0) { 4624 // Determine whether the value stored to this pointer is comptime-known. 4625 const field_ty = struct_ty.structFieldType(i, mod); 4626 if (try sema.typeHasOnePossibleValue(field_ty)) |opv| { 4627 field_values[i] = opv.toIntern(); 4628 continue; 4629 } 4630 4631 const field_ptr_air_ref = sema.inst_map.get(field_ptr).?; 4632 4633 //std.debug.print("validateStructInit (field_ptr_air_inst=%{d}):\n", .{ 4634 // field_ptr_air_inst, 4635 //}); 4636 //for (block.instructions.items) |item| { 4637 // std.debug.print(" %{d} = {s}\n", .{item, @tagName(air_tags[item])}); 4638 //} 4639 4640 // We expect to see something like this in the current block AIR: 4641 // %a = field_ptr(...) 4642 // store(%a, %b) 4643 // With an optional bitcast between the store and the field_ptr. 4644 // If %b is a comptime operand, this field is comptime. 4645 // 4646 // However, in the case of a comptime-known pointer to a struct, the 4647 // the field_ptr instruction is missing, so we have to pattern-match 4648 // based only on the store instructions. 4649 // `first_block_index` needs to point to the `field_ptr` if it exists; 4650 // the `store` otherwise. 4651 // 4652 // It's also possible for there to be no store instruction, in the case 4653 // of nested `coerce_result_ptr` instructions. If we see the `field_ptr` 4654 // but we have not found a `store`, treat as a runtime-known field. 4655 4656 // Possible performance enhancement: save the `block_index` between iterations 4657 // of the for loop. 4658 var block_index = block.instructions.items.len - 1; 4659 while (block_index > 0) : (block_index -= 1) { 4660 const store_inst = block.instructions.items[block_index]; 4661 if (Air.indexToRef(store_inst) == field_ptr_air_ref) { 4662 struct_is_comptime = false; 4663 continue :field; 4664 } 4665 switch (air_tags[store_inst]) { 4666 .store, .store_safe => {}, 4667 else => continue, 4668 } 4669 const bin_op = air_datas[store_inst].bin_op; 4670 var lhs = bin_op.lhs; 4671 { 4672 const lhs_index = Air.refToIndex(lhs) orelse continue; 4673 if (air_tags[lhs_index] == .bitcast) { 4674 lhs = air_datas[lhs_index].ty_op.operand; 4675 block_index -= 1; 4676 } 4677 } 4678 if (lhs != field_ptr_air_ref) continue; 4679 while (block_index > 0) : (block_index -= 1) { 4680 const block_inst = block.instructions.items[block_index - 1]; 4681 if (air_tags[block_inst] != .dbg_stmt) break; 4682 } 4683 if (block_index > 0 and 4684 field_ptr_air_ref == Air.indexToRef(block.instructions.items[block_index - 1])) 4685 { 4686 first_block_index = @min(first_block_index, block_index - 1); 4687 } else { 4688 first_block_index = @min(first_block_index, block_index); 4689 } 4690 if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(bin_op.rhs, &make_runtime)) |val| { 4691 field_values[i] = val.toIntern(); 4692 } else if (require_comptime) { 4693 const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; 4694 return sema.failWithNeededComptime(block, field_ptr_data.src(), "initializer of comptime only struct must be comptime-known"); 4695 } else { 4696 struct_is_comptime = false; 4697 } 4698 continue :field; 4699 } 4700 struct_is_comptime = false; 4701 continue :field; 4702 } 4703 4704 const default_val = struct_ty.structFieldDefaultValue(i, mod); 4705 if (default_val.toIntern() == .unreachable_value) { 4706 if (struct_ty.isTuple(mod)) { 4707 const template = "missing tuple field with index {d}"; 4708 if (root_msg) |msg| { 4709 try sema.errNote(block, init_src, msg, template, .{i}); 4710 } else { 4711 root_msg = try sema.errMsg(block, init_src, template, .{i}); 4712 } 4713 continue; 4714 } 4715 const field_name = struct_ty.structFieldName(i, mod); 4716 const template = "missing struct field: {}"; 4717 const args = .{field_name.fmt(ip)}; 4718 if (root_msg) |msg| { 4719 try sema.errNote(block, init_src, msg, template, args); 4720 } else { 4721 root_msg = try sema.errMsg(block, init_src, template, args); 4722 } 4723 continue; 4724 } 4725 field_values[i] = default_val.toIntern(); 4726 } 4727 4728 if (root_msg) |msg| { 4729 if (mod.typeToStruct(struct_ty)) |struct_obj| { 4730 const fqn = try struct_obj.getFullyQualifiedName(mod); 4731 try mod.errNoteNonLazy( 4732 struct_obj.srcLoc(mod), 4733 msg, 4734 "struct '{}' declared here", 4735 .{fqn.fmt(ip)}, 4736 ); 4737 } 4738 root_msg = null; 4739 return sema.failWithOwnedErrorMsg(msg); 4740 } 4741 4742 if (struct_is_comptime) { 4743 // Our task is to delete all the `field_ptr` and `store` instructions, and insert 4744 // instead a single `store` to the struct_ptr with a comptime struct value. 4745 4746 block.instructions.shrinkRetainingCapacity(first_block_index); 4747 var struct_val = try mod.intern(.{ .aggregate = .{ 4748 .ty = struct_ty.toIntern(), 4749 .storage = .{ .elems = field_values }, 4750 } }); 4751 if (make_runtime) struct_val = try mod.intern(.{ .runtime_value = .{ 4752 .ty = struct_ty.toIntern(), 4753 .val = struct_val, 4754 } }); 4755 const struct_init = try sema.addConstant(struct_val.toValue()); 4756 try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store); 4757 return; 4758 } 4759 try sema.resolveStructLayout(struct_ty); 4760 4761 // Our task is to insert `store` instructions for all the default field values. 4762 for (found_fields, 0..) |field_ptr, i| { 4763 if (field_ptr != 0) continue; 4764 4765 const field_src = init_src; // TODO better source location 4766 const default_field_ptr = if (struct_ty.isTuple(mod)) 4767 try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @as(u32, @intCast(i)), true) 4768 else 4769 try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @as(u32, @intCast(i)), field_src, struct_ty, true); 4770 const init = try sema.addConstant(field_values[i].toValue()); 4771 try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); 4772 } 4773 } 4774 4775 fn zirValidateArrayInit( 4776 sema: *Sema, 4777 block: *Block, 4778 inst: Zir.Inst.Index, 4779 ) CompileError!void { 4780 const mod = sema.mod; 4781 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node; 4782 const init_src = validate_inst.src(); 4783 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); 4784 const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len]; 4785 const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; 4786 const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; 4787 const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr); 4788 const array_ty = sema.typeOf(array_ptr).childType(mod); 4789 const array_len = array_ty.arrayLen(mod); 4790 4791 if (instrs.len != array_len) switch (array_ty.zigTypeTag(mod)) { 4792 .Struct => { 4793 var root_msg: ?*Module.ErrorMsg = null; 4794 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 4795 4796 var i = instrs.len; 4797 while (i < array_len) : (i += 1) { 4798 const default_val = array_ty.structFieldDefaultValue(i, mod); 4799 if (default_val.toIntern() == .unreachable_value) { 4800 const template = "missing tuple field with index {d}"; 4801 if (root_msg) |msg| { 4802 try sema.errNote(block, init_src, msg, template, .{i}); 4803 } else { 4804 root_msg = try sema.errMsg(block, init_src, template, .{i}); 4805 } 4806 } 4807 } 4808 4809 if (root_msg) |msg| { 4810 root_msg = null; 4811 return sema.failWithOwnedErrorMsg(msg); 4812 } 4813 }, 4814 .Array => { 4815 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{ 4816 array_len, instrs.len, 4817 }); 4818 }, 4819 .Vector => { 4820 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{ 4821 array_len, instrs.len, 4822 }); 4823 }, 4824 else => unreachable, 4825 }; 4826 4827 if (block.is_comptime and 4828 (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null) 4829 { 4830 // In this case the comptime machinery will have evaluated the store instructions 4831 // at comptime so we have almost nothing to do here. However, in case of a 4832 // sentinel-terminated array, the sentinel will not have been populated by 4833 // any ZIR instructions at comptime; we need to do that here. 4834 if (array_ty.sentinel(mod)) |sentinel_val| { 4835 const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len); 4836 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true); 4837 const sentinel = try sema.addConstant(sentinel_val); 4838 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store); 4839 } 4840 return; 4841 } 4842 4843 // If the array has one possible value, the value is always comptime-known. 4844 if (try sema.typeHasOnePossibleValue(array_ty)) |array_opv| { 4845 const array_init = try sema.addConstant(array_opv); 4846 try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store); 4847 return; 4848 } 4849 4850 var array_is_comptime = true; 4851 var first_block_index = block.instructions.items.len; 4852 var make_runtime = false; 4853 4854 // Collect the comptime element values in case the array literal ends up 4855 // being comptime-known. 4856 const element_vals = try sema.arena.alloc( 4857 InternPool.Index, 4858 try sema.usizeCast(block, init_src, array_len), 4859 ); 4860 const air_tags = sema.air_instructions.items(.tag); 4861 const air_datas = sema.air_instructions.items(.data); 4862 4863 outer: for (instrs, 0..) |elem_ptr, i| { 4864 // Determine whether the value stored to this pointer is comptime-known. 4865 4866 if (array_ty.isTuple(mod)) { 4867 if (try array_ty.structFieldValueComptime(mod, i)) |opv| { 4868 element_vals[i] = opv.toIntern(); 4869 continue; 4870 } 4871 } 4872 4873 const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?; 4874 4875 // We expect to see something like this in the current block AIR: 4876 // %a = elem_ptr(...) 4877 // store(%a, %b) 4878 // With an optional bitcast between the store and the elem_ptr. 4879 // If %b is a comptime operand, this element is comptime. 4880 // 4881 // However, in the case of a comptime-known pointer to an array, the 4882 // the elem_ptr instruction is missing, so we have to pattern-match 4883 // based only on the store instructions. 4884 // `first_block_index` needs to point to the `elem_ptr` if it exists; 4885 // the `store` otherwise. 4886 // 4887 // It's also possible for there to be no store instruction, in the case 4888 // of nested `coerce_result_ptr` instructions. If we see the `elem_ptr` 4889 // but we have not found a `store`, treat as a runtime-known element. 4890 // 4891 // This is nearly identical to similar logic in `validateStructInit`. 4892 4893 // Possible performance enhancement: save the `block_index` between iterations 4894 // of the for loop. 4895 var block_index = block.instructions.items.len - 1; 4896 while (block_index > 0) : (block_index -= 1) { 4897 const store_inst = block.instructions.items[block_index]; 4898 if (Air.indexToRef(store_inst) == elem_ptr_air_ref) { 4899 array_is_comptime = false; 4900 continue :outer; 4901 } 4902 switch (air_tags[store_inst]) { 4903 .store, .store_safe => {}, 4904 else => continue, 4905 } 4906 const bin_op = air_datas[store_inst].bin_op; 4907 var lhs = bin_op.lhs; 4908 { 4909 const lhs_index = Air.refToIndex(lhs) orelse continue; 4910 if (air_tags[lhs_index] == .bitcast) { 4911 lhs = air_datas[lhs_index].ty_op.operand; 4912 block_index -= 1; 4913 } 4914 } 4915 if (lhs != elem_ptr_air_ref) continue; 4916 while (block_index > 0) : (block_index -= 1) { 4917 const block_inst = block.instructions.items[block_index - 1]; 4918 if (air_tags[block_inst] != .dbg_stmt) break; 4919 } 4920 if (block_index > 0 and 4921 elem_ptr_air_ref == Air.indexToRef(block.instructions.items[block_index - 1])) 4922 { 4923 first_block_index = @min(first_block_index, block_index - 1); 4924 } else { 4925 first_block_index = @min(first_block_index, block_index); 4926 } 4927 if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(bin_op.rhs, &make_runtime)) |val| { 4928 element_vals[i] = val.toIntern(); 4929 } else { 4930 array_is_comptime = false; 4931 } 4932 continue :outer; 4933 } 4934 array_is_comptime = false; 4935 continue :outer; 4936 } 4937 4938 if (array_is_comptime) { 4939 if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| { 4940 switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) { 4941 .ptr => |ptr| switch (ptr.addr) { 4942 .comptime_field => return, // This store was validated by the individual elem ptrs. 4943 else => {}, 4944 }, 4945 else => {}, 4946 } 4947 } 4948 4949 // Our task is to delete all the `elem_ptr` and `store` instructions, and insert 4950 // instead a single `store` to the array_ptr with a comptime struct value. 4951 block.instructions.shrinkRetainingCapacity(first_block_index); 4952 4953 var array_val = try mod.intern(.{ .aggregate = .{ 4954 .ty = array_ty.toIntern(), 4955 .storage = .{ .elems = element_vals }, 4956 } }); 4957 if (make_runtime) array_val = try mod.intern(.{ .runtime_value = .{ 4958 .ty = array_ty.toIntern(), 4959 .val = array_val, 4960 } }); 4961 const array_init = try sema.addConstant(array_val.toValue()); 4962 try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store); 4963 } 4964 } 4965 4966 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 4967 const mod = sema.mod; 4968 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 4969 const src = inst_data.src(); 4970 const operand = try sema.resolveInst(inst_data.operand); 4971 const operand_ty = sema.typeOf(operand); 4972 4973 if (operand_ty.zigTypeTag(mod) != .Pointer) { 4974 return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(mod)}); 4975 } else switch (operand_ty.ptrSize(mod)) { 4976 .One, .C => {}, 4977 .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(mod)}), 4978 .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(mod)}), 4979 } 4980 4981 if ((try sema.typeHasOnePossibleValue(operand_ty.childType(mod))) != null) { 4982 // No need to validate the actual pointer value, we don't need it! 4983 return; 4984 } 4985 4986 const elem_ty = operand_ty.elemType2(mod); 4987 if (try sema.resolveMaybeUndefVal(operand)) |val| { 4988 if (val.isUndef(mod)) { 4989 return sema.fail(block, src, "cannot dereference undefined value", .{}); 4990 } 4991 } else if (!(try sema.validateRunTimeType(elem_ty, false))) { 4992 const msg = msg: { 4993 const msg = try sema.errMsg( 4994 block, 4995 src, 4996 "values of type '{}' must be comptime-known, but operand value is runtime-known", 4997 .{elem_ty.fmt(mod)}, 4998 ); 4999 errdefer msg.destroy(sema.gpa); 5000 5001 const src_decl = mod.declPtr(block.src_decl); 5002 try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), elem_ty); 5003 break :msg msg; 5004 }; 5005 return sema.failWithOwnedErrorMsg(msg); 5006 } 5007 } 5008 5009 fn failWithBadMemberAccess( 5010 sema: *Sema, 5011 block: *Block, 5012 agg_ty: Type, 5013 field_src: LazySrcLoc, 5014 field_name: InternPool.NullTerminatedString, 5015 ) CompileError { 5016 const mod = sema.mod; 5017 const kw_name = switch (agg_ty.zigTypeTag(mod)) { 5018 .Union => "union", 5019 .Struct => "struct", 5020 .Opaque => "opaque", 5021 .Enum => "enum", 5022 else => unreachable, 5023 }; 5024 if (agg_ty.getOwnerDeclOrNull(mod)) |some| if (mod.declIsRoot(some)) { 5025 return sema.fail(block, field_src, "root struct of file '{}' has no member named '{}'", .{ 5026 agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool), 5027 }); 5028 }; 5029 const msg = msg: { 5030 const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{}'", .{ 5031 kw_name, agg_ty.fmt(mod), field_name.fmt(&mod.intern_pool), 5032 }); 5033 errdefer msg.destroy(sema.gpa); 5034 try sema.addDeclaredHereNote(msg, agg_ty); 5035 break :msg msg; 5036 }; 5037 return sema.failWithOwnedErrorMsg(msg); 5038 } 5039 5040 fn failWithBadStructFieldAccess( 5041 sema: *Sema, 5042 block: *Block, 5043 struct_obj: *Module.Struct, 5044 field_src: LazySrcLoc, 5045 field_name: InternPool.NullTerminatedString, 5046 ) CompileError { 5047 const mod = sema.mod; 5048 const gpa = sema.gpa; 5049 5050 const fqn = try struct_obj.getFullyQualifiedName(mod); 5051 5052 const msg = msg: { 5053 const msg = try sema.errMsg( 5054 block, 5055 field_src, 5056 "no field named '{}' in struct '{}'", 5057 .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) }, 5058 ); 5059 errdefer msg.destroy(gpa); 5060 try mod.errNoteNonLazy(struct_obj.srcLoc(mod), msg, "struct declared here", .{}); 5061 break :msg msg; 5062 }; 5063 return sema.failWithOwnedErrorMsg(msg); 5064 } 5065 5066 fn failWithBadUnionFieldAccess( 5067 sema: *Sema, 5068 block: *Block, 5069 union_obj: *Module.Union, 5070 field_src: LazySrcLoc, 5071 field_name: InternPool.NullTerminatedString, 5072 ) CompileError { 5073 const mod = sema.mod; 5074 const gpa = sema.gpa; 5075 5076 const fqn = try union_obj.getFullyQualifiedName(mod); 5077 5078 const msg = msg: { 5079 const msg = try sema.errMsg( 5080 block, 5081 field_src, 5082 "no field named '{}' in union '{}'", 5083 .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) }, 5084 ); 5085 errdefer msg.destroy(gpa); 5086 try mod.errNoteNonLazy(union_obj.srcLoc(mod), msg, "union declared here", .{}); 5087 break :msg msg; 5088 }; 5089 return sema.failWithOwnedErrorMsg(msg); 5090 } 5091 5092 fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void { 5093 const mod = sema.mod; 5094 const src_loc = decl_ty.declSrcLocOrNull(mod) orelse return; 5095 const category = switch (decl_ty.zigTypeTag(mod)) { 5096 .Union => "union", 5097 .Struct => "struct", 5098 .Enum => "enum", 5099 .Opaque => "opaque", 5100 .ErrorSet => "error set", 5101 else => unreachable, 5102 }; 5103 try mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category}); 5104 } 5105 5106 fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5107 const tracy = trace(@src()); 5108 defer tracy.end(); 5109 5110 const bin_inst = sema.code.instructions.items(.data)[inst].bin; 5111 const ptr = sema.inst_map.get(Zir.refToIndex(bin_inst.lhs).?) orelse { 5112 // This is an elided instruction, but AstGen was unable to omit it. 5113 return; 5114 }; 5115 const operand = try sema.resolveInst(bin_inst.rhs); 5116 const src: LazySrcLoc = sema.src; 5117 blk: { 5118 const ptr_inst = Air.refToIndex(ptr) orelse break :blk; 5119 switch (sema.air_instructions.items(.tag)[ptr_inst]) { 5120 .inferred_alloc_comptime => { 5121 const iac = &sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime; 5122 return sema.storeToInferredAllocComptime(block, src, operand, iac); 5123 }, 5124 .inferred_alloc => { 5125 const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; 5126 return sema.storeToInferredAlloc(block, ptr, operand, ia); 5127 }, 5128 else => break :blk, 5129 } 5130 } 5131 5132 return sema.storePtr(block, src, ptr, operand); 5133 } 5134 5135 fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5136 const tracy = trace(@src()); 5137 defer tracy.end(); 5138 5139 const src: LazySrcLoc = sema.src; 5140 const bin_inst = sema.code.instructions.items(.data)[inst].bin; 5141 const ptr = try sema.resolveInst(bin_inst.lhs); 5142 const operand = try sema.resolveInst(bin_inst.rhs); 5143 const ptr_inst = Air.refToIndex(ptr).?; 5144 const air_datas = sema.air_instructions.items(.data); 5145 5146 switch (sema.air_instructions.items(.tag)[ptr_inst]) { 5147 .inferred_alloc_comptime => { 5148 const iac = &air_datas[ptr_inst].inferred_alloc_comptime; 5149 return sema.storeToInferredAllocComptime(block, src, operand, iac); 5150 }, 5151 .inferred_alloc => { 5152 const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; 5153 return sema.storeToInferredAlloc(block, ptr, operand, ia); 5154 }, 5155 else => unreachable, 5156 } 5157 } 5158 5159 fn storeToInferredAlloc( 5160 sema: *Sema, 5161 block: *Block, 5162 ptr: Air.Inst.Ref, 5163 operand: Air.Inst.Ref, 5164 inferred_alloc: *InferredAlloc, 5165 ) CompileError!void { 5166 // Create a store instruction as a placeholder. This will be replaced by a 5167 // proper store sequence once we know the stored type. 5168 const dummy_store = try block.addBinOp(.store, ptr, operand); 5169 // Add the stored instruction to the set we will use to resolve peer types 5170 // for the inferred allocation. 5171 try inferred_alloc.prongs.append(sema.arena, .{ 5172 .stored_inst = operand, 5173 .placeholder = Air.refToIndex(dummy_store).?, 5174 }); 5175 } 5176 5177 fn storeToInferredAllocComptime( 5178 sema: *Sema, 5179 block: *Block, 5180 src: LazySrcLoc, 5181 operand: Air.Inst.Ref, 5182 iac: *Air.Inst.Data.InferredAllocComptime, 5183 ) CompileError!void { 5184 const operand_ty = sema.typeOf(operand); 5185 // There will be only one store_to_inferred_ptr because we are running at comptime. 5186 // The alloc will turn into a Decl. 5187 if (try sema.resolveMaybeUndefValAllowVariables(operand)) |operand_val| store: { 5188 if (operand_val.getVariable(sema.mod) != null) break :store; 5189 var anon_decl = try block.startAnonDecl(); 5190 defer anon_decl.deinit(); 5191 iac.decl_index = try anon_decl.finish(operand_ty, operand_val, iac.alignment); 5192 try sema.comptime_mutable_decls.append(iac.decl_index); 5193 return; 5194 } 5195 5196 return sema.failWithNeededComptime(block, src, "value being stored to a comptime variable must be comptime-known"); 5197 } 5198 5199 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5200 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 5201 const src = inst_data.src(); 5202 const quota = @as(u32, @intCast(try sema.resolveInt(block, src, inst_data.operand, Type.u32, "eval branch quota must be comptime-known"))); 5203 sema.branch_quota = @max(sema.branch_quota, quota); 5204 } 5205 5206 fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5207 const tracy = trace(@src()); 5208 defer tracy.end(); 5209 5210 const bin_inst = sema.code.instructions.items(.data)[inst].bin; 5211 const ptr = try sema.resolveInst(bin_inst.lhs); 5212 const value = try sema.resolveInst(bin_inst.rhs); 5213 return sema.storePtr(block, sema.src, ptr, value); 5214 } 5215 5216 fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5217 const tracy = trace(@src()); 5218 defer tracy.end(); 5219 5220 const mod = sema.mod; 5221 const zir_tags = sema.code.instructions.items(.tag); 5222 const zir_datas = sema.code.instructions.items(.data); 5223 const inst_data = zir_datas[inst].pl_node; 5224 const src = inst_data.src(); 5225 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 5226 const ptr = try sema.resolveInst(extra.lhs); 5227 const operand = try sema.resolveInst(extra.rhs); 5228 5229 const is_ret = if (Zir.refToIndex(extra.lhs)) |ptr_index| 5230 zir_tags[ptr_index] == .ret_ptr 5231 else 5232 false; 5233 5234 // Check for the possibility of this pattern: 5235 // %a = ret_ptr 5236 // %b = store(%a, %c) 5237 // Where %c is an error union or error set. In such case we need to add 5238 // to the current function's inferred error set, if any. 5239 if (is_ret and (sema.typeOf(operand).zigTypeTag(mod) == .ErrorUnion or 5240 sema.typeOf(operand).zigTypeTag(mod) == .ErrorSet) and 5241 sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) 5242 { 5243 try sema.addToInferredErrorSet(operand); 5244 } 5245 5246 const ptr_src: LazySrcLoc = .{ .node_offset_store_ptr = inst_data.src_node }; 5247 const operand_src: LazySrcLoc = .{ .node_offset_store_operand = inst_data.src_node }; 5248 const air_tag: Air.Inst.Tag = if (is_ret) 5249 .ret_ptr 5250 else if (block.wantSafety()) 5251 .store_safe 5252 else 5253 .store; 5254 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 5255 } 5256 5257 fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5258 const tracy = trace(@src()); 5259 defer tracy.end(); 5260 5261 const bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code); 5262 return sema.addStrLit(block, bytes); 5263 } 5264 5265 fn addStrLit(sema: *Sema, block: *Block, bytes: []const u8) CompileError!Air.Inst.Ref { 5266 const mod = sema.mod; 5267 const gpa = sema.gpa; 5268 // TODO: write something like getCoercedInts to avoid needing to dupe 5269 const duped_bytes = try sema.arena.dupe(u8, bytes); 5270 const ty = try mod.arrayType(.{ 5271 .len = bytes.len, 5272 .sentinel = .zero_u8, 5273 .child = .u8_type, 5274 }); 5275 const val = try mod.intern(.{ .aggregate = .{ 5276 .ty = ty.toIntern(), 5277 .storage = .{ .bytes = duped_bytes }, 5278 } }); 5279 const gop = try mod.memoized_decls.getOrPut(gpa, val); 5280 if (!gop.found_existing) { 5281 const new_decl_index = try mod.createAnonymousDecl(block, .{ 5282 .ty = ty, 5283 .val = val.toValue(), 5284 }); 5285 gop.value_ptr.* = new_decl_index; 5286 try mod.finalizeAnonDecl(new_decl_index); 5287 } 5288 return sema.analyzeDeclRef(gop.value_ptr.*); 5289 } 5290 5291 fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5292 _ = block; 5293 const tracy = trace(@src()); 5294 defer tracy.end(); 5295 5296 const int = sema.code.instructions.items(.data)[inst].int; 5297 return sema.addIntUnsigned(Type.comptime_int, int); 5298 } 5299 5300 fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5301 _ = block; 5302 const tracy = trace(@src()); 5303 defer tracy.end(); 5304 5305 const mod = sema.mod; 5306 const int = sema.code.instructions.items(.data)[inst].str; 5307 const byte_count = int.len * @sizeOf(std.math.big.Limb); 5308 const limb_bytes = sema.code.string_bytes[int.start..][0..byte_count]; 5309 5310 // TODO: this allocation and copy is only needed because the limbs may be unaligned. 5311 // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these 5312 // two lines can be removed. 5313 const limbs = try sema.arena.alloc(std.math.big.Limb, int.len); 5314 @memcpy(mem.sliceAsBytes(limbs), limb_bytes); 5315 5316 return sema.addConstant( 5317 try mod.intValue_big(Type.comptime_int, .{ 5318 .limbs = limbs, 5319 .positive = true, 5320 }), 5321 ); 5322 } 5323 5324 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5325 _ = block; 5326 const number = sema.code.instructions.items(.data)[inst].float; 5327 return sema.addConstant( 5328 try sema.mod.floatValue(Type.comptime_float, number), 5329 ); 5330 } 5331 5332 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5333 _ = block; 5334 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 5335 const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data; 5336 const number = extra.get(); 5337 return sema.addConstant( 5338 try sema.mod.floatValue(Type.comptime_float, number), 5339 ); 5340 } 5341 5342 fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 5343 const tracy = trace(@src()); 5344 defer tracy.end(); 5345 5346 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 5347 const src = inst_data.src(); 5348 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 5349 const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, "compile error string must be comptime-known"); 5350 return sema.fail(block, src, "{s}", .{msg}); 5351 } 5352 5353 fn zirCompileLog( 5354 sema: *Sema, 5355 extended: Zir.Inst.Extended.InstData, 5356 ) CompileError!Air.Inst.Ref { 5357 const mod = sema.mod; 5358 5359 var managed = mod.compile_log_text.toManaged(sema.gpa); 5360 defer sema.mod.compile_log_text = managed.moveToUnmanaged(); 5361 const writer = managed.writer(); 5362 5363 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 5364 const src_node = extra.data.src_node; 5365 const args = sema.code.refSlice(extra.end, extended.small); 5366 5367 for (args, 0..) |arg_ref, i| { 5368 if (i != 0) try writer.print(", ", .{}); 5369 5370 const arg = try sema.resolveInst(arg_ref); 5371 const arg_ty = sema.typeOf(arg); 5372 if (try sema.resolveMaybeUndefLazyVal(arg)) |val| { 5373 try writer.print("@as({}, {})", .{ 5374 arg_ty.fmt(mod), val.fmtValue(arg_ty, mod), 5375 }); 5376 } else { 5377 try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(mod)}); 5378 } 5379 } 5380 try writer.print("\n", .{}); 5381 5382 const decl_index = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; 5383 const gop = try mod.compile_log_decls.getOrPut(sema.gpa, decl_index); 5384 if (!gop.found_existing) { 5385 gop.value_ptr.* = src_node; 5386 } 5387 return Air.Inst.Ref.void_value; 5388 } 5389 5390 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 5391 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 5392 const src = inst_data.src(); 5393 const msg_inst = try sema.resolveInst(inst_data.operand); 5394 5395 if (block.is_comptime) { 5396 return sema.fail(block, src, "encountered @panic at comptime", .{}); 5397 } 5398 try sema.panicWithMsg(block, msg_inst); 5399 return always_noreturn; 5400 } 5401 5402 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 5403 const src_node = sema.code.instructions.items(.data)[inst].node; 5404 const src = LazySrcLoc.nodeOffset(src_node); 5405 sema.src = src; 5406 _ = try block.addNoOp(.trap); 5407 return always_noreturn; 5408 } 5409 5410 fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5411 const tracy = trace(@src()); 5412 defer tracy.end(); 5413 5414 const mod = sema.mod; 5415 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 5416 const src = inst_data.src(); 5417 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 5418 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 5419 const gpa = sema.gpa; 5420 5421 // AIR expects a block outside the loop block too. 5422 // Reserve space for a Loop instruction so that generated Break instructions can 5423 // point to it, even if it doesn't end up getting used because the code ends up being 5424 // comptime evaluated. 5425 const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 5426 const loop_inst = block_inst + 1; 5427 try sema.air_instructions.ensureUnusedCapacity(gpa, 2); 5428 sema.air_instructions.appendAssumeCapacity(.{ 5429 .tag = .block, 5430 .data = undefined, 5431 }); 5432 sema.air_instructions.appendAssumeCapacity(.{ 5433 .tag = .loop, 5434 .data = .{ .ty_pl = .{ 5435 .ty = .noreturn_type, 5436 .payload = undefined, 5437 } }, 5438 }); 5439 var label: Block.Label = .{ 5440 .zir_block = inst, 5441 .merges = .{ 5442 .src_locs = .{}, 5443 .results = .{}, 5444 .br_list = .{}, 5445 .block_inst = block_inst, 5446 }, 5447 }; 5448 var child_block = parent_block.makeSubBlock(); 5449 child_block.label = &label; 5450 child_block.runtime_cond = null; 5451 child_block.runtime_loop = src; 5452 child_block.runtime_index.increment(); 5453 const merges = &child_block.label.?.merges; 5454 5455 defer child_block.instructions.deinit(gpa); 5456 defer merges.deinit(gpa); 5457 5458 var loop_block = child_block.makeSubBlock(); 5459 defer loop_block.instructions.deinit(gpa); 5460 5461 try sema.analyzeBody(&loop_block, body); 5462 5463 const loop_block_len = loop_block.instructions.items.len; 5464 if (loop_block_len > 0 and sema.typeOf(Air.indexToRef(loop_block.instructions.items[loop_block_len - 1])).isNoReturn(mod)) { 5465 // If the loop ended with a noreturn terminator, then there is no way for it to loop, 5466 // so we can just use the block instead. 5467 try child_block.instructions.appendSlice(gpa, loop_block.instructions.items); 5468 } else { 5469 try child_block.instructions.append(gpa, loop_inst); 5470 5471 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + loop_block_len); 5472 sema.air_instructions.items(.data)[loop_inst].ty_pl.payload = sema.addExtraAssumeCapacity( 5473 Air.Block{ .body_len = @as(u32, @intCast(loop_block_len)) }, 5474 ); 5475 sema.air_extra.appendSliceAssumeCapacity(loop_block.instructions.items); 5476 } 5477 return sema.analyzeBlockBody(parent_block, src, &child_block, merges); 5478 } 5479 5480 fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5481 const tracy = trace(@src()); 5482 defer tracy.end(); 5483 5484 const pl_node = sema.code.instructions.items(.data)[inst].pl_node; 5485 const src = pl_node.src(); 5486 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 5487 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 5488 5489 // we check this here to avoid undefined symbols 5490 if (!@import("build_options").have_llvm) 5491 return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{}); 5492 5493 var c_import_buf = std.ArrayList(u8).init(sema.gpa); 5494 defer c_import_buf.deinit(); 5495 5496 var comptime_reason: Block.ComptimeReason = .{ .c_import = .{ 5497 .block = parent_block, 5498 .src = src, 5499 } }; 5500 var child_block: Block = .{ 5501 .parent = parent_block, 5502 .sema = sema, 5503 .src_decl = parent_block.src_decl, 5504 .namespace = parent_block.namespace, 5505 .wip_capture_scope = parent_block.wip_capture_scope, 5506 .instructions = .{}, 5507 .inlining = parent_block.inlining, 5508 .is_comptime = true, 5509 .comptime_reason = &comptime_reason, 5510 .c_import_buf = &c_import_buf, 5511 .runtime_cond = parent_block.runtime_cond, 5512 .runtime_loop = parent_block.runtime_loop, 5513 .runtime_index = parent_block.runtime_index, 5514 }; 5515 defer child_block.instructions.deinit(sema.gpa); 5516 5517 // Ignore the result, all the relevant operations have written to c_import_buf already. 5518 _ = try sema.analyzeBodyBreak(&child_block, body); 5519 5520 const mod = sema.mod; 5521 const c_import_res = mod.comp.cImport(c_import_buf.items) catch |err| 5522 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5523 5524 if (c_import_res.errors.len != 0) { 5525 const msg = msg: { 5526 defer @import("clang.zig").ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len); 5527 5528 const msg = try sema.errMsg(&child_block, src, "C import failed", .{}); 5529 errdefer msg.destroy(sema.gpa); 5530 5531 if (!mod.comp.bin_file.options.link_libc) 5532 try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{}); 5533 5534 const gop = try mod.cimport_errors.getOrPut(sema.gpa, sema.owner_decl_index); 5535 if (!gop.found_existing) { 5536 var errs = try std.ArrayListUnmanaged(Module.CImportError).initCapacity(sema.gpa, c_import_res.errors.len); 5537 errdefer { 5538 for (errs.items) |err| err.deinit(sema.gpa); 5539 errs.deinit(sema.gpa); 5540 } 5541 5542 for (c_import_res.errors) |c_error| { 5543 const path = if (c_error.filename_ptr) |some| 5544 try sema.gpa.dupeZ(u8, some[0..c_error.filename_len]) 5545 else 5546 null; 5547 errdefer if (path) |some| sema.gpa.free(some); 5548 5549 const c_msg = try sema.gpa.dupeZ(u8, c_error.msg_ptr[0..c_error.msg_len]); 5550 errdefer sema.gpa.free(c_msg); 5551 5552 const line = line: { 5553 const source = c_error.source orelse break :line null; 5554 var start = c_error.offset; 5555 while (start > 0) : (start -= 1) { 5556 if (source[start - 1] == '\n') break; 5557 } 5558 var end = c_error.offset; 5559 while (true) : (end += 1) { 5560 if (source[end] == 0) break; 5561 if (source[end] == '\n') break; 5562 } 5563 break :line try sema.gpa.dupeZ(u8, source[start..end]); 5564 }; 5565 errdefer if (line) |some| sema.gpa.free(some); 5566 5567 errs.appendAssumeCapacity(.{ 5568 .path = path orelse null, 5569 .source_line = line orelse null, 5570 .line = c_error.line, 5571 .column = c_error.column, 5572 .offset = c_error.offset, 5573 .msg = c_msg, 5574 }); 5575 } 5576 gop.value_ptr.* = errs.items; 5577 } 5578 break :msg msg; 5579 }; 5580 return sema.failWithOwnedErrorMsg(msg); 5581 } 5582 const c_import_pkg = Package.create( 5583 sema.gpa, 5584 null, 5585 c_import_res.out_zig_path, 5586 ) catch |err| switch (err) { 5587 error.OutOfMemory => return error.OutOfMemory, 5588 else => unreachable, // we pass null for root_src_dir_path 5589 }; 5590 5591 const result = mod.importPkg(c_import_pkg) catch |err| 5592 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5593 5594 mod.astGenFile(result.file) catch |err| 5595 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); 5596 5597 try mod.semaFile(result.file); 5598 const file_root_decl_index = result.file.root_decl.unwrap().?; 5599 const file_root_decl = mod.declPtr(file_root_decl_index); 5600 try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index); 5601 return sema.addConstant(file_root_decl.val); 5602 } 5603 5604 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 5605 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 5606 const src = inst_data.src(); 5607 return sema.failWithUseOfAsync(parent_block, src); 5608 } 5609 5610 fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_comptime: bool) CompileError!Air.Inst.Ref { 5611 const tracy = trace(@src()); 5612 defer tracy.end(); 5613 5614 const pl_node = sema.code.instructions.items(.data)[inst].pl_node; 5615 const src = pl_node.src(); 5616 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 5617 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 5618 const gpa = sema.gpa; 5619 5620 // Reserve space for a Block instruction so that generated Break instructions can 5621 // point to it, even if it doesn't end up getting used because the code ends up being 5622 // comptime evaluated or is an unlabeled block. 5623 const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 5624 try sema.air_instructions.append(gpa, .{ 5625 .tag = .block, 5626 .data = undefined, 5627 }); 5628 5629 var label: Block.Label = .{ 5630 .zir_block = inst, 5631 .merges = .{ 5632 .src_locs = .{}, 5633 .results = .{}, 5634 .br_list = .{}, 5635 .block_inst = block_inst, 5636 }, 5637 }; 5638 5639 var child_block: Block = .{ 5640 .parent = parent_block, 5641 .sema = sema, 5642 .src_decl = parent_block.src_decl, 5643 .namespace = parent_block.namespace, 5644 .wip_capture_scope = parent_block.wip_capture_scope, 5645 .instructions = .{}, 5646 .label = &label, 5647 .inlining = parent_block.inlining, 5648 .is_comptime = parent_block.is_comptime or force_comptime, 5649 .comptime_reason = parent_block.comptime_reason, 5650 .is_typeof = parent_block.is_typeof, 5651 .want_safety = parent_block.want_safety, 5652 .float_mode = parent_block.float_mode, 5653 .c_import_buf = parent_block.c_import_buf, 5654 .runtime_cond = parent_block.runtime_cond, 5655 .runtime_loop = parent_block.runtime_loop, 5656 .runtime_index = parent_block.runtime_index, 5657 .error_return_trace_index = parent_block.error_return_trace_index, 5658 }; 5659 5660 defer child_block.instructions.deinit(gpa); 5661 defer label.merges.deinit(gpa); 5662 5663 return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges); 5664 } 5665 5666 fn resolveBlockBody( 5667 sema: *Sema, 5668 parent_block: *Block, 5669 src: LazySrcLoc, 5670 child_block: *Block, 5671 body: []const Zir.Inst.Index, 5672 /// This is the instruction that a break instruction within `body` can 5673 /// use to return from the body. 5674 body_inst: Zir.Inst.Index, 5675 merges: *Block.Merges, 5676 ) CompileError!Air.Inst.Ref { 5677 if (child_block.is_comptime) { 5678 return sema.resolveBody(child_block, body, body_inst); 5679 } else { 5680 if (sema.analyzeBodyInner(child_block, body)) |_| { 5681 return sema.analyzeBlockBody(parent_block, src, child_block, merges); 5682 } else |err| switch (err) { 5683 error.ComptimeBreak => { 5684 // Comptime control flow is happening, however child_block may still contain 5685 // runtime instructions which need to be copied to the parent block. 5686 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items); 5687 5688 const break_inst = sema.comptime_break_inst; 5689 const break_data = sema.code.instructions.items(.data)[break_inst].@"break"; 5690 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; 5691 if (extra.block_inst == body_inst) { 5692 return try sema.resolveInst(break_data.operand); 5693 } else { 5694 return error.ComptimeBreak; 5695 } 5696 }, 5697 else => |e| return e, 5698 } 5699 } 5700 } 5701 5702 fn analyzeBlockBody( 5703 sema: *Sema, 5704 parent_block: *Block, 5705 src: LazySrcLoc, 5706 child_block: *Block, 5707 merges: *Block.Merges, 5708 ) CompileError!Air.Inst.Ref { 5709 const tracy = trace(@src()); 5710 defer tracy.end(); 5711 5712 const gpa = sema.gpa; 5713 const mod = sema.mod; 5714 5715 // Blocks must terminate with noreturn instruction. 5716 assert(child_block.instructions.items.len != 0); 5717 assert(sema.typeOf(Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1])).isNoReturn(mod)); 5718 5719 if (merges.results.items.len == 0) { 5720 // No need for a block instruction. We can put the new instructions 5721 // directly into the parent block. 5722 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items); 5723 return Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1]); 5724 } 5725 if (merges.results.items.len == 1) { 5726 const last_inst_index = child_block.instructions.items.len - 1; 5727 const last_inst = child_block.instructions.items[last_inst_index]; 5728 if (sema.getBreakBlock(last_inst)) |br_block| { 5729 if (br_block == merges.block_inst) { 5730 // No need for a block instruction. We can put the new instructions directly 5731 // into the parent block. Here we omit the break instruction. 5732 const without_break = child_block.instructions.items[0..last_inst_index]; 5733 try parent_block.instructions.appendSlice(gpa, without_break); 5734 return merges.results.items[0]; 5735 } 5736 } 5737 } 5738 // It is impossible to have the number of results be > 1 in a comptime scope. 5739 assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition. 5740 5741 // Need to set the type and emit the Block instruction. This allows machine code generation 5742 // to emit a jump instruction to after the block when it encounters the break. 5743 try parent_block.instructions.append(gpa, merges.block_inst); 5744 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items }); 5745 // TODO add note "missing else causes void value" 5746 5747 const type_src = src; // TODO: better source location 5748 const valid_rt = try sema.validateRunTimeType(resolved_ty, false); 5749 if (!valid_rt) { 5750 const msg = msg: { 5751 const msg = try sema.errMsg(child_block, type_src, "value with comptime-only type '{}' depends on runtime control flow", .{resolved_ty.fmt(mod)}); 5752 errdefer msg.destroy(sema.gpa); 5753 5754 const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?; 5755 try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{}); 5756 5757 const child_src_decl = mod.declPtr(child_block.src_decl); 5758 try sema.explainWhyTypeIsComptime(msg, type_src.toSrcLoc(child_src_decl, mod), resolved_ty); 5759 5760 break :msg msg; 5761 }; 5762 return sema.failWithOwnedErrorMsg(msg); 5763 } 5764 const ty_inst = try sema.addType(resolved_ty); 5765 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + 5766 child_block.instructions.items.len); 5767 sema.air_instructions.items(.data)[merges.block_inst] = .{ .ty_pl = .{ 5768 .ty = ty_inst, 5769 .payload = sema.addExtraAssumeCapacity(Air.Block{ 5770 .body_len = @as(u32, @intCast(child_block.instructions.items.len)), 5771 }), 5772 } }; 5773 sema.air_extra.appendSliceAssumeCapacity(child_block.instructions.items); 5774 // Now that the block has its type resolved, we need to go back into all the break 5775 // instructions, and insert type coercion on the operands. 5776 for (merges.br_list.items) |br| { 5777 const br_operand = sema.air_instructions.items(.data)[br].br.operand; 5778 const br_operand_src = src; 5779 const br_operand_ty = sema.typeOf(br_operand); 5780 if (br_operand_ty.eql(resolved_ty, mod)) { 5781 // No type coercion needed. 5782 continue; 5783 } 5784 var coerce_block = parent_block.makeSubBlock(); 5785 defer coerce_block.instructions.deinit(gpa); 5786 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src); 5787 // If no instructions were produced, such as in the case of a coercion of a 5788 // constant value to a new type, we can simply point the br operand to it. 5789 if (coerce_block.instructions.items.len == 0) { 5790 sema.air_instructions.items(.data)[br].br.operand = coerced_operand; 5791 continue; 5792 } 5793 assert(Air.indexToRef(coerce_block.instructions.items[coerce_block.instructions.items.len - 1]) == coerced_operand); 5794 5795 // Convert the br instruction to a block instruction that has the coercion 5796 // and then a new br inside that returns the coerced instruction. 5797 const sub_block_len = @as(u32, @intCast(coerce_block.instructions.items.len + 1)); 5798 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + 5799 sub_block_len); 5800 try sema.air_instructions.ensureUnusedCapacity(gpa, 1); 5801 const sub_br_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 5802 5803 sema.air_instructions.items(.tag)[br] = .block; 5804 sema.air_instructions.items(.data)[br] = .{ .ty_pl = .{ 5805 .ty = Air.Inst.Ref.noreturn_type, 5806 .payload = sema.addExtraAssumeCapacity(Air.Block{ 5807 .body_len = sub_block_len, 5808 }), 5809 } }; 5810 sema.air_extra.appendSliceAssumeCapacity(coerce_block.instructions.items); 5811 sema.air_extra.appendAssumeCapacity(sub_br_inst); 5812 5813 sema.air_instructions.appendAssumeCapacity(.{ 5814 .tag = .br, 5815 .data = .{ .br = .{ 5816 .block_inst = merges.block_inst, 5817 .operand = coerced_operand, 5818 } }, 5819 }); 5820 } 5821 return Air.indexToRef(merges.block_inst); 5822 } 5823 5824 fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5825 const tracy = trace(@src()); 5826 defer tracy.end(); 5827 5828 const mod = sema.mod; 5829 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 5830 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; 5831 const src = inst_data.src(); 5832 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 5833 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 5834 const decl_name = try mod.intern_pool.getOrPutString(mod.gpa, sema.code.nullTerminatedString(extra.decl_name)); 5835 const decl_index = if (extra.namespace != .none) index_blk: { 5836 const container_ty = try sema.resolveType(block, operand_src, extra.namespace); 5837 const container_namespace = container_ty.getNamespaceIndex(mod).unwrap().?; 5838 5839 const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false); 5840 break :index_blk maybe_index orelse 5841 return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name); 5842 } else try sema.lookupIdentifier(block, operand_src, decl_name); 5843 const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) { 5844 error.NeededSourceLocation => { 5845 _ = try sema.resolveExportOptions(block, options_src, extra.options); 5846 unreachable; 5847 }, 5848 else => |e| return e, 5849 }; 5850 { 5851 try mod.ensureDeclAnalyzed(decl_index); 5852 const exported_decl = mod.declPtr(decl_index); 5853 if (exported_decl.val.getFunction(mod)) |function| { 5854 return sema.analyzeExport(block, src, options, function.owner_decl); 5855 } 5856 } 5857 try sema.analyzeExport(block, src, options, decl_index); 5858 } 5859 5860 fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 5861 const tracy = trace(@src()); 5862 defer tracy.end(); 5863 5864 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 5865 const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data; 5866 const src = inst_data.src(); 5867 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 5868 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 5869 const operand = try sema.resolveInstConst(block, operand_src, extra.operand, "export target must be comptime-known"); 5870 const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) { 5871 error.NeededSourceLocation => { 5872 _ = try sema.resolveExportOptions(block, options_src, extra.options); 5873 unreachable; 5874 }, 5875 else => |e| return e, 5876 }; 5877 const decl_index = if (operand.val.getFunction(sema.mod)) |function| function.owner_decl else blk: { 5878 var anon_decl = try block.startAnonDecl(); 5879 defer anon_decl.deinit(); 5880 break :blk try anon_decl.finish(operand.ty, operand.val, .none); 5881 }; 5882 try sema.analyzeExport(block, src, options, decl_index); 5883 } 5884 5885 pub fn analyzeExport( 5886 sema: *Sema, 5887 block: *Block, 5888 src: LazySrcLoc, 5889 options: Module.Export.Options, 5890 exported_decl_index: Decl.Index, 5891 ) !void { 5892 const Export = Module.Export; 5893 const mod = sema.mod; 5894 5895 if (options.linkage == .Internal) { 5896 return; 5897 } 5898 5899 try mod.ensureDeclAnalyzed(exported_decl_index); 5900 const exported_decl = mod.declPtr(exported_decl_index); 5901 5902 if (!try sema.validateExternType(exported_decl.ty, .other)) { 5903 const msg = msg: { 5904 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(mod)}); 5905 errdefer msg.destroy(sema.gpa); 5906 5907 const src_decl = mod.declPtr(block.src_decl); 5908 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other); 5909 5910 try sema.addDeclaredHereNote(msg, exported_decl.ty); 5911 break :msg msg; 5912 }; 5913 return sema.failWithOwnedErrorMsg(msg); 5914 } 5915 5916 // TODO: some backends might support re-exporting extern decls 5917 if (exported_decl.isExtern(mod)) { 5918 return sema.fail(block, src, "export target cannot be extern", .{}); 5919 } 5920 5921 // This decl is alive no matter what, since it's being exported 5922 try mod.markDeclAlive(exported_decl); 5923 try sema.maybeQueueFuncBodyAnalysis(exported_decl_index); 5924 5925 const gpa = sema.gpa; 5926 5927 try mod.decl_exports.ensureUnusedCapacity(gpa, 1); 5928 try mod.export_owners.ensureUnusedCapacity(gpa, 1); 5929 5930 const new_export = try gpa.create(Export); 5931 errdefer gpa.destroy(new_export); 5932 5933 new_export.* = .{ 5934 .opts = options, 5935 .src = src, 5936 .owner_decl = sema.owner_decl_index, 5937 .src_decl = block.src_decl, 5938 .exported_decl = exported_decl_index, 5939 .status = .in_progress, 5940 }; 5941 5942 // Add to export_owners table. 5943 const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index); 5944 if (!eo_gop.found_existing) { 5945 eo_gop.value_ptr.* = .{}; 5946 } 5947 try eo_gop.value_ptr.append(gpa, new_export); 5948 errdefer _ = eo_gop.value_ptr.pop(); 5949 5950 // Add to exported_decl table. 5951 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index); 5952 if (!de_gop.found_existing) { 5953 de_gop.value_ptr.* = .{}; 5954 } 5955 try de_gop.value_ptr.append(gpa, new_export); 5956 errdefer _ = de_gop.value_ptr.pop(); 5957 } 5958 5959 fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 5960 const mod = sema.mod; 5961 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 5962 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 5963 const src = LazySrcLoc.nodeOffset(extra.node); 5964 const alignment = try sema.resolveAlign(block, operand_src, extra.operand); 5965 if (alignment.order(Alignment.fromNonzeroByteUnits(256)).compare(.gt)) { 5966 return sema.fail(block, src, "attempt to @setAlignStack({d}); maximum is 256", .{ 5967 alignment.toByteUnitsOptional().?, 5968 }); 5969 } 5970 const func_index = sema.func_index.unwrap() orelse 5971 return sema.fail(block, src, "@setAlignStack outside function body", .{}); 5972 const func = mod.funcPtr(func_index); 5973 5974 const fn_owner_decl = mod.declPtr(func.owner_decl); 5975 switch (fn_owner_decl.ty.fnCallingConvention(mod)) { 5976 .Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}), 5977 .Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}), 5978 else => if (block.inlining != null) { 5979 return sema.fail(block, src, "@setAlignStack in inline call", .{}); 5980 }, 5981 } 5982 5983 const gop = try mod.align_stack_fns.getOrPut(sema.gpa, func_index); 5984 if (gop.found_existing) { 5985 const msg = msg: { 5986 const msg = try sema.errMsg(block, src, "multiple @setAlignStack in the same function body", .{}); 5987 errdefer msg.destroy(sema.gpa); 5988 try sema.errNote(block, gop.value_ptr.src, msg, "other instance here", .{}); 5989 break :msg msg; 5990 }; 5991 return sema.failWithOwnedErrorMsg(msg); 5992 } 5993 gop.value_ptr.* = .{ .alignment = alignment, .src = src }; 5994 } 5995 5996 fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 5997 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 5998 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 5999 const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, "operand to @setCold must be comptime-known"); 6000 const func = sema.func orelse return; // does nothing outside a function 6001 func.is_cold = is_cold; 6002 } 6003 6004 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 6005 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 6006 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 6007 block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode", "operand to @setFloatMode must be comptime-known"); 6008 } 6009 6010 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6011 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 6012 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 6013 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setRuntimeSafety must be comptime-known"); 6014 } 6015 6016 fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { 6017 if (block.is_comptime) return; 6018 6019 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 6020 const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 6021 const order = try sema.resolveAtomicOrder(block, order_src, extra.operand, "atomic order of @fence must be comptime-known"); 6022 6023 if (@intFromEnum(order) < @intFromEnum(std.builtin.AtomicOrder.Acquire)) { 6024 return sema.fail(block, order_src, "atomic ordering must be Acquire or stricter", .{}); 6025 } 6026 6027 _ = try block.addInst(.{ 6028 .tag = .fence, 6029 .data = .{ .fence = order }, 6030 }); 6031 } 6032 6033 fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 6034 const tracy = trace(@src()); 6035 defer tracy.end(); 6036 6037 const inst_data = sema.code.instructions.items(.data)[inst].@"break"; 6038 const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; 6039 const operand = try sema.resolveInst(inst_data.operand); 6040 const zir_block = extra.block_inst; 6041 6042 var block = start_block; 6043 while (true) { 6044 if (block.label) |label| { 6045 if (label.zir_block == zir_block) { 6046 const br_ref = try start_block.addBr(label.merges.block_inst, operand); 6047 const src_loc = if (extra.operand_src_node != Zir.Inst.Break.no_src_node) 6048 LazySrcLoc.nodeOffset(extra.operand_src_node) 6049 else 6050 null; 6051 try label.merges.src_locs.append(sema.gpa, src_loc); 6052 try label.merges.results.append(sema.gpa, operand); 6053 try label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?); 6054 block.runtime_index.increment(); 6055 if (block.runtime_cond == null and block.runtime_loop == null) { 6056 block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; 6057 block.runtime_loop = start_block.runtime_loop; 6058 } 6059 return inst; 6060 } 6061 } 6062 block = block.parent.?; 6063 } 6064 } 6065 6066 fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 6067 // We do not set sema.src here because dbg_stmt instructions are only emitted for 6068 // ZIR code that possibly will need to generate runtime code. So error messages 6069 // and other source locations must not rely on sema.src being set from dbg_stmt 6070 // instructions. 6071 if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; 6072 6073 const inst_data = sema.code.instructions.items(.data)[inst].dbg_stmt; 6074 6075 if (block.instructions.items.len != 0) { 6076 const idx = block.instructions.items[block.instructions.items.len - 1]; 6077 if (sema.air_instructions.items(.tag)[idx] == .dbg_stmt) { 6078 // The previous dbg_stmt didn't correspond to any actual code, so replace it. 6079 sema.air_instructions.items(.data)[idx].dbg_stmt = .{ 6080 .line = inst_data.line, 6081 .column = inst_data.column, 6082 }; 6083 return; 6084 } 6085 } 6086 6087 _ = try block.addInst(.{ 6088 .tag = .dbg_stmt, 6089 .data = .{ .dbg_stmt = .{ 6090 .line = inst_data.line, 6091 .column = inst_data.column, 6092 } }, 6093 }); 6094 } 6095 6096 fn zirDbgBlockBegin(sema: *Sema, block: *Block) CompileError!void { 6097 if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; 6098 6099 _ = try block.addInst(.{ 6100 .tag = .dbg_block_begin, 6101 .data = undefined, 6102 }); 6103 } 6104 6105 fn zirDbgBlockEnd(sema: *Sema, block: *Block) CompileError!void { 6106 if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; 6107 6108 _ = try block.addInst(.{ 6109 .tag = .dbg_block_end, 6110 .data = undefined, 6111 }); 6112 } 6113 6114 fn zirDbgVar( 6115 sema: *Sema, 6116 block: *Block, 6117 inst: Zir.Inst.Index, 6118 air_tag: Air.Inst.Tag, 6119 ) CompileError!void { 6120 if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; 6121 6122 const str_op = sema.code.instructions.items(.data)[inst].str_op; 6123 const operand = try sema.resolveInst(str_op.operand); 6124 const name = str_op.getStr(sema.code); 6125 try sema.addDbgVar(block, operand, air_tag, name); 6126 } 6127 6128 fn addDbgVar( 6129 sema: *Sema, 6130 block: *Block, 6131 operand: Air.Inst.Ref, 6132 air_tag: Air.Inst.Tag, 6133 name: []const u8, 6134 ) CompileError!void { 6135 const mod = sema.mod; 6136 const operand_ty = sema.typeOf(operand); 6137 switch (air_tag) { 6138 .dbg_var_ptr => { 6139 if (!(try sema.typeHasRuntimeBits(operand_ty.childType(mod)))) return; 6140 }, 6141 .dbg_var_val => { 6142 if (!(try sema.typeHasRuntimeBits(operand_ty))) return; 6143 }, 6144 else => unreachable, 6145 } 6146 6147 try sema.queueFullTypeResolution(operand_ty); 6148 6149 // Add the name to the AIR. 6150 const name_extra_index = @as(u32, @intCast(sema.air_extra.items.len)); 6151 const elements_used = name.len / 4 + 1; 6152 try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements_used); 6153 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 6154 @memcpy(buffer[0..name.len], name); 6155 buffer[name.len] = 0; 6156 sema.air_extra.items.len += elements_used; 6157 6158 _ = try block.addInst(.{ 6159 .tag = air_tag, 6160 .data = .{ .pl_op = .{ 6161 .payload = name_extra_index, 6162 .operand = operand, 6163 } }, 6164 }); 6165 } 6166 6167 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 6168 const mod = sema.mod; 6169 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 6170 const src = inst_data.src(); 6171 const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 6172 const decl_index = try sema.lookupIdentifier(block, src, decl_name); 6173 try sema.addReferencedBy(block, src, decl_index); 6174 return sema.analyzeDeclRef(decl_index); 6175 } 6176 6177 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 6178 const mod = sema.mod; 6179 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 6180 const src = inst_data.src(); 6181 const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 6182 const decl = try sema.lookupIdentifier(block, src, decl_name); 6183 return sema.analyzeDeclVal(block, src, decl); 6184 } 6185 6186 fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: InternPool.NullTerminatedString) !Decl.Index { 6187 const mod = sema.mod; 6188 var namespace = block.namespace; 6189 while (true) { 6190 if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| { 6191 return decl_index; 6192 } 6193 namespace = mod.namespacePtr(namespace).parent.unwrap() orelse break; 6194 } 6195 unreachable; // AstGen detects use of undeclared identifier errors. 6196 } 6197 6198 /// This looks up a member of a specific namespace. It is affected by `usingnamespace` but 6199 /// only for ones in the specified namespace. 6200 fn lookupInNamespace( 6201 sema: *Sema, 6202 block: *Block, 6203 src: LazySrcLoc, 6204 namespace_index: Namespace.Index, 6205 ident_name: InternPool.NullTerminatedString, 6206 observe_usingnamespace: bool, 6207 ) CompileError!?Decl.Index { 6208 const mod = sema.mod; 6209 6210 const namespace = mod.namespacePtr(namespace_index); 6211 const namespace_decl_index = namespace.getDeclIndex(mod); 6212 const namespace_decl = mod.declPtr(namespace_decl_index); 6213 if (namespace_decl.analysis == .file_failure) { 6214 try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index); 6215 return error.AnalysisFail; 6216 } 6217 6218 if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) { 6219 const src_file = mod.namespacePtr(block.namespace).file_scope; 6220 6221 const gpa = sema.gpa; 6222 var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, bool) = .{}; 6223 defer checked_namespaces.deinit(gpa); 6224 6225 // Keep track of name conflicts for error notes. 6226 var candidates: std.ArrayListUnmanaged(Decl.Index) = .{}; 6227 defer candidates.deinit(gpa); 6228 6229 try checked_namespaces.put(gpa, namespace, namespace.file_scope == src_file); 6230 var check_i: usize = 0; 6231 6232 while (check_i < checked_namespaces.count()) : (check_i += 1) { 6233 const check_ns = checked_namespaces.keys()[check_i]; 6234 if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| { 6235 // Skip decls which are not marked pub, which are in a different 6236 // file than the `a.b`/`@hasDecl` syntax. 6237 const decl = mod.declPtr(decl_index); 6238 if (decl.is_pub or (src_file == decl.getFileScope(mod) and checked_namespaces.values()[check_i])) { 6239 try candidates.append(gpa, decl_index); 6240 } 6241 } 6242 var it = check_ns.usingnamespace_set.iterator(); 6243 while (it.next()) |entry| { 6244 const sub_usingnamespace_decl_index = entry.key_ptr.*; 6245 // Skip the decl we're currently analysing. 6246 if (sub_usingnamespace_decl_index == sema.owner_decl_index) continue; 6247 const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index); 6248 const sub_is_pub = entry.value_ptr.*; 6249 if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope(mod)) { 6250 // Skip usingnamespace decls which are not marked pub, which are in 6251 // a different file than the `a.b`/`@hasDecl` syntax. 6252 continue; 6253 } 6254 try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index); 6255 const ns_ty = sub_usingnamespace_decl.val.toType(); 6256 const sub_ns = ns_ty.getNamespace(mod).?; 6257 try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope(mod)); 6258 } 6259 } 6260 6261 { 6262 var i: usize = 0; 6263 while (i < candidates.items.len) { 6264 if (candidates.items[i] == sema.owner_decl_index) { 6265 _ = candidates.orderedRemove(i); 6266 } else { 6267 i += 1; 6268 } 6269 } 6270 } 6271 6272 switch (candidates.items.len) { 6273 0 => {}, 6274 1 => { 6275 const decl_index = candidates.items[0]; 6276 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 6277 return decl_index; 6278 }, 6279 else => { 6280 const msg = msg: { 6281 const msg = try sema.errMsg(block, src, "ambiguous reference", .{}); 6282 errdefer msg.destroy(gpa); 6283 for (candidates.items) |candidate_index| { 6284 const candidate = mod.declPtr(candidate_index); 6285 const src_loc = candidate.srcLoc(mod); 6286 try mod.errNoteNonLazy(src_loc, msg, "declared here", .{}); 6287 } 6288 break :msg msg; 6289 }; 6290 return sema.failWithOwnedErrorMsg(msg); 6291 }, 6292 } 6293 } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| { 6294 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 6295 return decl_index; 6296 } 6297 6298 // TODO This dependency is too strong. Really, it should only be a dependency 6299 // on the non-existence of `ident_name` in the namespace. We can lessen the number of 6300 // outdated declarations by making this dependency more sophisticated. 6301 try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index); 6302 return null; 6303 } 6304 6305 fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?*Decl { 6306 const mod = sema.mod; 6307 const func_val = (try sema.resolveMaybeUndefVal(func_inst)) orelse return null; 6308 if (func_val.isUndef(mod)) return null; 6309 const owner_decl_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { 6310 .extern_func => |extern_func| extern_func.decl, 6311 .func => |func| mod.funcPtr(func.index).owner_decl, 6312 .ptr => |ptr| switch (ptr.addr) { 6313 .decl => |decl| mod.declPtr(decl).val.getFunction(mod).?.owner_decl, 6314 else => return null, 6315 }, 6316 else => return null, 6317 }; 6318 return mod.declPtr(owner_decl_index); 6319 } 6320 6321 pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref { 6322 const mod = sema.mod; 6323 const gpa = sema.gpa; 6324 const src = sema.src; 6325 6326 if (!mod.backendSupportsFeature(.error_return_trace)) return .none; 6327 if (!mod.comp.bin_file.options.error_return_tracing) return .none; 6328 6329 if (block.is_comptime) 6330 return .none; 6331 6332 const unresolved_stack_trace_ty = sema.getBuiltinType("StackTrace") catch |err| switch (err) { 6333 error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, 6334 else => |e| return e, 6335 }; 6336 const stack_trace_ty = sema.resolveTypeFields(unresolved_stack_trace_ty) catch |err| switch (err) { 6337 error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, 6338 else => |e| return e, 6339 }; 6340 const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); 6341 const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, src) catch |err| switch (err) { 6342 error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, 6343 else => |e| return e, 6344 }; 6345 6346 return try block.addInst(.{ 6347 .tag = .save_err_return_trace_index, 6348 .data = .{ .ty_pl = .{ 6349 .ty = try sema.addType(stack_trace_ty), 6350 .payload = @as(u32, @intCast(field_index)), 6351 } }, 6352 }); 6353 } 6354 6355 /// Add instructions to block to "pop" the error return trace. 6356 /// If `operand` is provided, only pops if operand is non-error. 6357 fn popErrorReturnTrace( 6358 sema: *Sema, 6359 block: *Block, 6360 src: LazySrcLoc, 6361 operand: Air.Inst.Ref, 6362 saved_error_trace_index: Air.Inst.Ref, 6363 ) CompileError!void { 6364 const mod = sema.mod; 6365 const gpa = sema.gpa; 6366 var is_non_error: ?bool = null; 6367 var is_non_error_inst: Air.Inst.Ref = undefined; 6368 if (operand != .none) { 6369 is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand); 6370 if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val| 6371 is_non_error = cond_val.toBool(); 6372 } else is_non_error = true; // no operand means pop unconditionally 6373 6374 if (is_non_error == true) { 6375 // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or 6376 // the result is comptime-known to be a non-error. Either way, pop unconditionally. 6377 6378 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 6379 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 6380 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 6381 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); 6382 const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); 6383 const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true); 6384 try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store); 6385 } else if (is_non_error == null) { 6386 // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need 6387 // to pop any error trace that may have been propagated from our arguments. 6388 6389 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len); 6390 const cond_block_inst = try block.addInstAsIndex(.{ 6391 .tag = .block, 6392 .data = .{ 6393 .ty_pl = .{ 6394 .ty = Air.Inst.Ref.void_type, 6395 .payload = undefined, // updated below 6396 }, 6397 }, 6398 }); 6399 6400 var then_block = block.makeSubBlock(); 6401 defer then_block.instructions.deinit(gpa); 6402 6403 // If non-error, then pop the error return trace by restoring the index. 6404 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 6405 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 6406 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 6407 const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); 6408 const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); 6409 const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true); 6410 try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store); 6411 _ = try then_block.addBr(cond_block_inst, Air.Inst.Ref.void_value); 6412 6413 // Otherwise, do nothing 6414 var else_block = block.makeSubBlock(); 6415 defer else_block.instructions.deinit(gpa); 6416 _ = try else_block.addBr(cond_block_inst, Air.Inst.Ref.void_value); 6417 6418 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 6419 then_block.instructions.items.len + else_block.instructions.items.len + 6420 @typeInfo(Air.Block).Struct.fields.len + 1); // +1 for the sole .cond_br instruction in the .block 6421 6422 const cond_br_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 6423 try sema.air_instructions.append(gpa, .{ .tag = .cond_br, .data = .{ .pl_op = .{ 6424 .operand = is_non_error_inst, 6425 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 6426 .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)), 6427 .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)), 6428 }), 6429 } } }); 6430 sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items); 6431 sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); 6432 6433 sema.air_instructions.items(.data)[cond_block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); 6434 sema.air_extra.appendAssumeCapacity(cond_br_inst); 6435 } 6436 } 6437 6438 fn zirCall( 6439 sema: *Sema, 6440 block: *Block, 6441 inst: Zir.Inst.Index, 6442 comptime kind: enum { direct, field }, 6443 ) CompileError!Air.Inst.Ref { 6444 const tracy = trace(@src()); 6445 defer tracy.end(); 6446 6447 const mod = sema.mod; 6448 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 6449 const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; 6450 const call_src = inst_data.src(); 6451 const ExtraType = switch (kind) { 6452 .direct => Zir.Inst.Call, 6453 .field => Zir.Inst.FieldCall, 6454 }; 6455 const extra = sema.code.extraData(ExtraType, inst_data.payload_index); 6456 const args_len = extra.data.flags.args_len; 6457 6458 const modifier = @as(std.builtin.CallModifier, @enumFromInt(extra.data.flags.packed_modifier)); 6459 const ensure_result_used = extra.data.flags.ensure_result_used; 6460 const pop_error_return_trace = extra.data.flags.pop_error_return_trace; 6461 6462 const callee: ResolvedFieldCallee = switch (kind) { 6463 .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, 6464 .field => blk: { 6465 const object_ptr = try sema.resolveInst(extra.data.obj_ptr); 6466 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.data.field_name_start)); 6467 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; 6468 break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); 6469 }, 6470 }; 6471 var resolved_args: []Air.Inst.Ref = undefined; 6472 var bound_arg_src: ?LazySrcLoc = null; 6473 var func: Air.Inst.Ref = undefined; 6474 var arg_index: u32 = 0; 6475 switch (callee) { 6476 .direct => |func_inst| { 6477 resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len); 6478 func = func_inst; 6479 }, 6480 .method => |method| { 6481 resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1); 6482 func = method.func_inst; 6483 resolved_args[0] = method.arg0_inst; 6484 arg_index += 1; 6485 bound_arg_src = callee_src; 6486 }, 6487 } 6488 6489 const callee_ty = sema.typeOf(func); 6490 const total_args = args_len + @intFromBool(bound_arg_src != null); 6491 const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, bound_arg_src != null); 6492 6493 const args_body = sema.code.extra[extra.end..]; 6494 6495 var input_is_error = false; 6496 const block_index = @as(Air.Inst.Index, @intCast(block.instructions.items.len)); 6497 6498 const fn_params_len = mod.typeToFunc(func_ty).?.param_types.len; 6499 const parent_comptime = block.is_comptime; 6500 // `extra_index` and `arg_index` are separate since the bound function is passed as the first argument. 6501 var extra_index: usize = 0; 6502 var arg_start: u32 = args_len; 6503 while (extra_index < args_len) : ({ 6504 extra_index += 1; 6505 arg_index += 1; 6506 }) { 6507 const func_ty_info = mod.typeToFunc(func_ty).?; 6508 const arg_end = sema.code.extra[extra.end + extra_index]; 6509 defer arg_start = arg_end; 6510 6511 // Generate args to comptime params in comptime block. 6512 defer block.is_comptime = parent_comptime; 6513 if (arg_index < @min(fn_params_len, 32) and func_ty_info.paramIsComptime(@as(u5, @intCast(arg_index)))) { 6514 block.is_comptime = true; 6515 // TODO set comptime_reason 6516 } 6517 6518 sema.inst_map.putAssumeCapacity(inst, inst: { 6519 if (arg_index >= fn_params_len) 6520 break :inst Air.Inst.Ref.var_args_param_type; 6521 6522 if (func_ty_info.param_types[arg_index] == .generic_poison_type) 6523 break :inst Air.Inst.Ref.generic_poison_type; 6524 6525 break :inst try sema.addType(func_ty_info.param_types[arg_index].toType()); 6526 }); 6527 6528 const resolved = try sema.resolveBody(block, args_body[arg_start..arg_end], inst); 6529 const resolved_ty = sema.typeOf(resolved); 6530 if (resolved_ty.zigTypeTag(mod) == .NoReturn) { 6531 return resolved; 6532 } 6533 if (resolved_ty.isError(mod)) { 6534 input_is_error = true; 6535 } 6536 resolved_args[arg_index] = resolved; 6537 } 6538 if (sema.owner_func == null or !sema.owner_func.?.calls_or_awaits_errorable_fn) { 6539 input_is_error = false; // input was an error type, but no errorable fn's were actually called 6540 } 6541 6542 // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction. 6543 const call_dbg_node = inst - 1; 6544 6545 if (mod.backendSupportsFeature(.error_return_trace) and mod.comp.bin_file.options.error_return_tracing and 6546 !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace)) 6547 { 6548 const call_inst: Air.Inst.Ref = if (modifier == .always_tail) undefined else b: { 6549 break :b try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); 6550 }; 6551 6552 const return_ty = sema.typeOf(call_inst); 6553 if (modifier != .always_tail and return_ty.isNoReturn(mod)) 6554 return call_inst; // call to "fn(...) noreturn", don't pop 6555 6556 // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only 6557 // need to clean-up our own trace if we were passed to a non-error-handling expression. 6558 if (input_is_error or (pop_error_return_trace and modifier != .always_tail and return_ty.isError(mod))) { 6559 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 6560 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 6561 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index"); 6562 const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); 6563 6564 // Insert a save instruction before the arg resolution + call instructions we just generated 6565 const save_inst = try block.insertInst(block_index, .{ 6566 .tag = .save_err_return_trace_index, 6567 .data = .{ .ty_pl = .{ 6568 .ty = try sema.addType(stack_trace_ty), 6569 .payload = @as(u32, @intCast(field_index)), 6570 } }, 6571 }); 6572 6573 // Pop the error return trace, testing the result for non-error if necessary 6574 const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst; 6575 try sema.popErrorReturnTrace(block, call_src, operand, save_inst); 6576 } 6577 6578 if (modifier == .always_tail) // Perform the call *after* the restore, so that a tail call is possible. 6579 return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); 6580 6581 return call_inst; 6582 } else { 6583 return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node); 6584 } 6585 } 6586 6587 fn checkCallArgumentCount( 6588 sema: *Sema, 6589 block: *Block, 6590 func: Air.Inst.Ref, 6591 func_src: LazySrcLoc, 6592 callee_ty: Type, 6593 total_args: usize, 6594 member_fn: bool, 6595 ) !Type { 6596 const mod = sema.mod; 6597 const func_ty = func_ty: { 6598 switch (callee_ty.zigTypeTag(mod)) { 6599 .Fn => break :func_ty callee_ty, 6600 .Pointer => { 6601 const ptr_info = callee_ty.ptrInfo(mod); 6602 if (ptr_info.flags.size == .One and ptr_info.child.toType().zigTypeTag(mod) == .Fn) { 6603 break :func_ty ptr_info.child.toType(); 6604 } 6605 }, 6606 .Optional => { 6607 const opt_child = callee_ty.optionalChild(mod); 6608 if (opt_child.zigTypeTag(mod) == .Fn or (opt_child.isSinglePointer(mod) and 6609 opt_child.childType(mod).zigTypeTag(mod) == .Fn)) 6610 { 6611 const msg = msg: { 6612 const msg = try sema.errMsg(block, func_src, "cannot call optional type '{}'", .{ 6613 callee_ty.fmt(mod), 6614 }); 6615 errdefer msg.destroy(sema.gpa); 6616 try sema.errNote(block, func_src, msg, "consider using '.?', 'orelse' or 'if'", .{}); 6617 break :msg msg; 6618 }; 6619 return sema.failWithOwnedErrorMsg(msg); 6620 } 6621 }, 6622 else => {}, 6623 } 6624 return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(mod)}); 6625 }; 6626 6627 const func_ty_info = mod.typeToFunc(func_ty).?; 6628 const fn_params_len = func_ty_info.param_types.len; 6629 const args_len = total_args - @intFromBool(member_fn); 6630 if (func_ty_info.is_var_args) { 6631 assert(func_ty_info.cc == .C); 6632 if (total_args >= fn_params_len) return func_ty; 6633 } else if (fn_params_len == total_args) { 6634 return func_ty; 6635 } 6636 6637 const maybe_decl = try sema.funcDeclSrc(func); 6638 const member_str = if (member_fn) "member function " else ""; 6639 const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; 6640 const msg = msg: { 6641 const msg = try sema.errMsg( 6642 block, 6643 func_src, 6644 "{s}expected {s}{d} argument(s), found {d}", 6645 .{ 6646 member_str, 6647 variadic_str, 6648 fn_params_len - @intFromBool(member_fn), 6649 args_len, 6650 }, 6651 ); 6652 errdefer msg.destroy(sema.gpa); 6653 6654 if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{}); 6655 break :msg msg; 6656 }; 6657 return sema.failWithOwnedErrorMsg(msg); 6658 } 6659 6660 fn callBuiltin( 6661 sema: *Sema, 6662 block: *Block, 6663 builtin_fn: Air.Inst.Ref, 6664 modifier: std.builtin.CallModifier, 6665 args: []const Air.Inst.Ref, 6666 ) !void { 6667 const mod = sema.mod; 6668 const callee_ty = sema.typeOf(builtin_fn); 6669 const func_ty = func_ty: { 6670 switch (callee_ty.zigTypeTag(mod)) { 6671 .Fn => break :func_ty callee_ty, 6672 .Pointer => { 6673 const ptr_info = callee_ty.ptrInfo(mod); 6674 if (ptr_info.flags.size == .One and ptr_info.child.toType().zigTypeTag(mod) == .Fn) { 6675 break :func_ty ptr_info.child.toType(); 6676 } 6677 }, 6678 else => {}, 6679 } 6680 std.debug.panic("type '{}' is not a function calling builtin fn", .{callee_ty.fmt(mod)}); 6681 }; 6682 6683 const func_ty_info = mod.typeToFunc(func_ty).?; 6684 const fn_params_len = func_ty_info.param_types.len; 6685 if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) { 6686 std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len }); 6687 } 6688 _ = try sema.analyzeCall(block, builtin_fn, func_ty, sema.src, sema.src, modifier, false, args, null, null); 6689 } 6690 6691 fn analyzeCall( 6692 sema: *Sema, 6693 block: *Block, 6694 func: Air.Inst.Ref, 6695 func_ty: Type, 6696 func_src: LazySrcLoc, 6697 call_src: LazySrcLoc, 6698 modifier: std.builtin.CallModifier, 6699 ensure_result_used: bool, 6700 uncasted_args: []const Air.Inst.Ref, 6701 bound_arg_src: ?LazySrcLoc, 6702 call_dbg_node: ?Zir.Inst.Index, 6703 ) CompileError!Air.Inst.Ref { 6704 const mod = sema.mod; 6705 6706 const callee_ty = sema.typeOf(func); 6707 const func_ty_info = mod.typeToFunc(func_ty).?; 6708 const fn_params_len = func_ty_info.param_types.len; 6709 const cc = func_ty_info.cc; 6710 if (cc == .Naked) { 6711 const maybe_decl = try sema.funcDeclSrc(func); 6712 const msg = msg: { 6713 const msg = try sema.errMsg( 6714 block, 6715 func_src, 6716 "unable to call function with naked calling convention", 6717 .{}, 6718 ); 6719 errdefer msg.destroy(sema.gpa); 6720 6721 if (maybe_decl) |fn_decl| try mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{}); 6722 break :msg msg; 6723 }; 6724 return sema.failWithOwnedErrorMsg(msg); 6725 } 6726 6727 const call_tag: Air.Inst.Tag = switch (modifier) { 6728 .auto, 6729 .always_inline, 6730 .compile_time, 6731 .no_async, 6732 => Air.Inst.Tag.call, 6733 6734 .never_tail => Air.Inst.Tag.call_never_tail, 6735 .never_inline => Air.Inst.Tag.call_never_inline, 6736 .always_tail => Air.Inst.Tag.call_always_tail, 6737 6738 .async_kw => return sema.failWithUseOfAsync(block, call_src), 6739 }; 6740 6741 if (modifier == .never_inline and func_ty_info.cc == .Inline) { 6742 return sema.fail(block, call_src, "'never_inline' call of inline function", .{}); 6743 } 6744 if (modifier == .always_inline and func_ty_info.is_noinline) { 6745 return sema.fail(block, call_src, "'always_inline' call of noinline function", .{}); 6746 } 6747 6748 const gpa = sema.gpa; 6749 6750 var is_generic_call = func_ty_info.is_generic; 6751 var is_comptime_call = block.is_comptime or modifier == .compile_time; 6752 var comptime_reason_buf: Block.ComptimeReason = undefined; 6753 var comptime_reason: ?*const Block.ComptimeReason = null; 6754 if (!is_comptime_call) { 6755 if (sema.typeRequiresComptime(func_ty_info.return_type.toType())) |ct| { 6756 is_comptime_call = ct; 6757 if (ct) { 6758 // stage1 can't handle doing this directly 6759 comptime_reason_buf = .{ .comptime_ret_ty = .{ 6760 .block = block, 6761 .func = func, 6762 .func_src = func_src, 6763 .return_ty = func_ty_info.return_type.toType(), 6764 } }; 6765 comptime_reason = &comptime_reason_buf; 6766 } 6767 } else |err| switch (err) { 6768 error.GenericPoison => is_generic_call = true, 6769 else => |e| return e, 6770 } 6771 } 6772 var is_inline_call = is_comptime_call or modifier == .always_inline or 6773 func_ty_info.cc == .Inline; 6774 6775 if (!is_inline_call and is_generic_call) { 6776 if (sema.instantiateGenericCall( 6777 block, 6778 func, 6779 func_src, 6780 call_src, 6781 func_ty, 6782 ensure_result_used, 6783 uncasted_args, 6784 call_tag, 6785 bound_arg_src, 6786 call_dbg_node, 6787 )) |some| { 6788 return some; 6789 } else |err| switch (err) { 6790 error.GenericPoison => { 6791 is_inline_call = true; 6792 }, 6793 error.ComptimeReturn => { 6794 is_inline_call = true; 6795 is_comptime_call = true; 6796 // stage1 can't handle doing this directly 6797 comptime_reason_buf = .{ .comptime_ret_ty = .{ 6798 .block = block, 6799 .func = func, 6800 .func_src = func_src, 6801 .return_ty = func_ty_info.return_type.toType(), 6802 } }; 6803 comptime_reason = &comptime_reason_buf; 6804 }, 6805 else => |e| return e, 6806 } 6807 } 6808 6809 if (is_comptime_call and modifier == .never_inline) { 6810 return sema.fail(block, call_src, "unable to perform 'never_inline' call at compile-time", .{}); 6811 } 6812 6813 const result: Air.Inst.Ref = if (is_inline_call) res: { 6814 const func_val = sema.resolveConstValue(block, func_src, func, "function being called at comptime must be comptime-known") catch |err| { 6815 if (err == error.AnalysisFail and comptime_reason != null) try comptime_reason.?.explain(sema, sema.err); 6816 return err; 6817 }; 6818 const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { 6819 .extern_func => return sema.fail(block, call_src, "{s} call of extern function", .{ 6820 @as([]const u8, if (is_comptime_call) "comptime" else "inline"), 6821 }), 6822 .func => |function| function.index, 6823 .ptr => |ptr| switch (ptr.addr) { 6824 .decl => |decl| mod.declPtr(decl).val.getFunctionIndex(mod).unwrap().?, 6825 else => { 6826 assert(callee_ty.isPtrAtRuntime(mod)); 6827 return sema.fail(block, call_src, "{s} call of function pointer", .{ 6828 @as([]const u8, if (is_comptime_call) "comptime" else "inline"), 6829 }); 6830 }, 6831 }, 6832 else => unreachable, 6833 }; 6834 if (func_ty_info.is_var_args) { 6835 return sema.fail(block, call_src, "{s} call of variadic function", .{ 6836 @as([]const u8, if (is_comptime_call) "comptime" else "inline"), 6837 }); 6838 } 6839 6840 // Analyze the ZIR. The same ZIR gets analyzed into a runtime function 6841 // or an inlined call depending on what union tag the `label` field is 6842 // set to in the `Block`. 6843 // This block instruction will be used to capture the return value from the 6844 // inlined function. 6845 const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 6846 try sema.air_instructions.append(gpa, .{ 6847 .tag = .block, 6848 .data = undefined, 6849 }); 6850 // This one is shared among sub-blocks within the same callee, but not 6851 // shared among the entire inline/comptime call stack. 6852 var inlining: Block.Inlining = .{ 6853 .func = null, 6854 .comptime_result = undefined, 6855 .merges = .{ 6856 .src_locs = .{}, 6857 .results = .{}, 6858 .br_list = .{}, 6859 .block_inst = block_inst, 6860 }, 6861 }; 6862 // In order to save a bit of stack space, directly modify Sema rather 6863 // than create a child one. 6864 const parent_zir = sema.code; 6865 const module_fn = mod.funcPtr(module_fn_index); 6866 const fn_owner_decl = mod.declPtr(module_fn.owner_decl); 6867 sema.code = fn_owner_decl.getFileScope(mod).zir; 6868 defer sema.code = parent_zir; 6869 6870 try mod.declareDeclDependencyType(sema.owner_decl_index, module_fn.owner_decl, .function_body); 6871 6872 const parent_inst_map = sema.inst_map; 6873 sema.inst_map = .{}; 6874 defer { 6875 sema.src = call_src; 6876 sema.inst_map.deinit(gpa); 6877 sema.inst_map = parent_inst_map; 6878 } 6879 6880 const parent_func = sema.func; 6881 const parent_func_index = sema.func_index; 6882 sema.func = module_fn; 6883 sema.func_index = module_fn_index.toOptional(); 6884 defer sema.func = parent_func; 6885 defer sema.func_index = parent_func_index; 6886 6887 const parent_err_ret_index = sema.error_return_trace_index_on_fn_entry; 6888 sema.error_return_trace_index_on_fn_entry = block.error_return_trace_index; 6889 defer sema.error_return_trace_index_on_fn_entry = parent_err_ret_index; 6890 6891 var wip_captures = try WipCaptureScope.init(gpa, fn_owner_decl.src_scope); 6892 defer wip_captures.deinit(); 6893 6894 var child_block: Block = .{ 6895 .parent = null, 6896 .sema = sema, 6897 .src_decl = module_fn.owner_decl, 6898 .namespace = fn_owner_decl.src_namespace, 6899 .wip_capture_scope = wip_captures.scope, 6900 .instructions = .{}, 6901 .label = null, 6902 .inlining = &inlining, 6903 .is_typeof = block.is_typeof, 6904 .is_comptime = is_comptime_call, 6905 .comptime_reason = comptime_reason, 6906 .error_return_trace_index = block.error_return_trace_index, 6907 }; 6908 6909 const merges = &child_block.inlining.?.merges; 6910 6911 defer child_block.instructions.deinit(gpa); 6912 defer merges.deinit(gpa); 6913 6914 try sema.emitBackwardBranch(block, call_src); 6915 6916 // Whether this call should be memoized, set to false if the call can mutate comptime state. 6917 var should_memoize = true; 6918 6919 // If it's a comptime function call, we need to memoize it as long as no external 6920 // comptime memory is mutated. 6921 const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); 6922 6923 var new_fn_info = mod.typeToFunc(fn_owner_decl.ty).?; 6924 new_fn_info.param_types = try sema.arena.alloc(InternPool.Index, new_fn_info.param_types.len); 6925 new_fn_info.comptime_bits = 0; 6926 6927 // This will have return instructions analyzed as break instructions to 6928 // the block_inst above. Here we are performing "comptime/inline semantic analysis" 6929 // for a function body, which means we must map the parameter ZIR instructions to 6930 // the AIR instructions of the callsite. The callee could be a generic function 6931 // which means its parameter type expressions must be resolved in order and used 6932 // to successively coerce the arguments. 6933 const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst); 6934 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, fn_info.param_body); 6935 6936 var has_comptime_args = false; 6937 var arg_i: usize = 0; 6938 for (fn_info.param_body) |inst| { 6939 sema.analyzeInlineCallArg( 6940 block, 6941 &child_block, 6942 .unneeded, 6943 inst, 6944 &new_fn_info, 6945 &arg_i, 6946 uncasted_args, 6947 is_comptime_call, 6948 &should_memoize, 6949 memoized_arg_values, 6950 mod.typeToFunc(func_ty).?.param_types, 6951 func, 6952 &has_comptime_args, 6953 ) catch |err| switch (err) { 6954 error.NeededSourceLocation => { 6955 _ = sema.inst_map.remove(inst); 6956 const decl = mod.declPtr(block.src_decl); 6957 try sema.analyzeInlineCallArg( 6958 block, 6959 &child_block, 6960 mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src), 6961 inst, 6962 &new_fn_info, 6963 &arg_i, 6964 uncasted_args, 6965 is_comptime_call, 6966 &should_memoize, 6967 memoized_arg_values, 6968 mod.typeToFunc(func_ty).?.param_types, 6969 func, 6970 &has_comptime_args, 6971 ); 6972 unreachable; 6973 }, 6974 else => |e| return e, 6975 }; 6976 } 6977 6978 if (!has_comptime_args and module_fn.state == .sema_failure) return error.AnalysisFail; 6979 6980 const recursive_msg = "inline call is recursive"; 6981 var head = if (!has_comptime_args) block else null; 6982 while (head) |some| { 6983 const parent_inlining = some.inlining orelse break; 6984 if (parent_inlining.func == module_fn) { 6985 return sema.fail(block, call_src, recursive_msg, .{}); 6986 } 6987 head = some.parent; 6988 } 6989 if (!has_comptime_args) inlining.func = module_fn; 6990 6991 // In case it is a generic function with an expression for the return type that depends 6992 // on parameters, we must now do the same for the return type as we just did with 6993 // each of the parameters, resolving the return type and providing it to the child 6994 // `Sema` so that it can be used for the `ret_ptr` instruction. 6995 const ret_ty_inst = if (fn_info.ret_ty_body.len != 0) 6996 try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst) 6997 else 6998 try sema.resolveInst(fn_info.ret_ty_ref); 6999 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 7000 const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst); 7001 // Create a fresh inferred error set type for inline/comptime calls. 7002 const fn_ret_ty = blk: { 7003 if (module_fn.hasInferredErrorSet(mod)) { 7004 const ies_index = try mod.intern_pool.createInferredErrorSet(gpa, .{ 7005 .func = module_fn_index, 7006 }); 7007 const error_set_ty = try mod.intern(.{ .inferred_error_set_type = ies_index }); 7008 break :blk try mod.errorUnionType(error_set_ty.toType(), bare_return_type); 7009 } 7010 break :blk bare_return_type; 7011 }; 7012 new_fn_info.return_type = fn_ret_ty.toIntern(); 7013 const parent_fn_ret_ty = sema.fn_ret_ty; 7014 sema.fn_ret_ty = fn_ret_ty; 7015 defer sema.fn_ret_ty = parent_fn_ret_ty; 7016 7017 // This `res2` is here instead of directly breaking from `res` due to a stage1 7018 // bug generating invalid LLVM IR. 7019 const res2: Air.Inst.Ref = res2: { 7020 if (should_memoize and is_comptime_call) { 7021 if (mod.intern_pool.getIfExists(.{ .memoized_call = .{ 7022 .func = module_fn_index, 7023 .arg_values = memoized_arg_values, 7024 .result = .none, 7025 } })) |memoized_call_index| { 7026 const memoized_call = mod.intern_pool.indexToKey(memoized_call_index).memoized_call; 7027 break :res2 try sema.addConstant( 7028 memoized_call.result.toValue(), 7029 ); 7030 } 7031 } 7032 7033 const new_func_resolved_ty = try mod.funcType(new_fn_info); 7034 if (!is_comptime_call and !block.is_typeof) { 7035 try sema.emitDbgInline(block, parent_func_index.unwrap().?, module_fn_index, new_func_resolved_ty, .dbg_inline_begin); 7036 7037 const zir_tags = sema.code.instructions.items(.tag); 7038 for (fn_info.param_body) |param| switch (zir_tags[param]) { 7039 .param, .param_comptime => { 7040 const inst_data = sema.code.instructions.items(.data)[param].pl_tok; 7041 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); 7042 const param_name = sema.code.nullTerminatedString(extra.data.name); 7043 const inst = sema.inst_map.get(param).?; 7044 7045 try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name); 7046 }, 7047 .param_anytype, .param_anytype_comptime => { 7048 const inst_data = sema.code.instructions.items(.data)[param].str_tok; 7049 const param_name = inst_data.get(sema.code); 7050 const inst = sema.inst_map.get(param).?; 7051 7052 try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name); 7053 }, 7054 else => continue, 7055 }; 7056 } 7057 7058 if (is_comptime_call and ensure_result_used) { 7059 try sema.ensureResultUsed(block, fn_ret_ty, call_src); 7060 } 7061 7062 const result = result: { 7063 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) { 7064 error.ComptimeReturn => break :result inlining.comptime_result, 7065 error.AnalysisFail => { 7066 const err_msg = sema.err orelse return err; 7067 if (mem.eql(u8, err_msg.msg, recursive_msg)) return err; 7068 try sema.errNote(block, call_src, err_msg, "called from here", .{}); 7069 err_msg.clearTrace(sema.gpa); 7070 return err; 7071 }, 7072 else => |e| return e, 7073 }; 7074 break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); 7075 }; 7076 7077 if (!is_comptime_call and !block.is_typeof and sema.typeOf(result).zigTypeTag(mod) != .NoReturn) { 7078 try sema.emitDbgInline( 7079 block, 7080 module_fn_index, 7081 parent_func_index.unwrap().?, 7082 mod.declPtr(parent_func.?.owner_decl).ty, 7083 .dbg_inline_end, 7084 ); 7085 } 7086 7087 if (should_memoize and is_comptime_call) { 7088 const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, ""); 7089 7090 // TODO: check whether any external comptime memory was mutated by the 7091 // comptime function call. If so, then do not memoize the call here. 7092 _ = try mod.intern(.{ .memoized_call = .{ 7093 .func = module_fn_index, 7094 .arg_values = memoized_arg_values, 7095 .result = try result_val.intern(fn_ret_ty, mod), 7096 } }); 7097 } 7098 7099 break :res2 result; 7100 }; 7101 7102 try wip_captures.finalize(); 7103 7104 break :res res2; 7105 } else res: { 7106 assert(!func_ty_info.is_generic); 7107 7108 const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); 7109 for (uncasted_args, 0..) |uncasted_arg, i| { 7110 if (i < fn_params_len) { 7111 const opts: CoerceOpts = .{ .param_src = .{ 7112 .func_inst = func, 7113 .param_i = @as(u32, @intCast(i)), 7114 } }; 7115 const param_ty = mod.typeToFunc(func_ty).?.param_types[i].toType(); 7116 args[i] = sema.analyzeCallArg( 7117 block, 7118 .unneeded, 7119 param_ty, 7120 uncasted_arg, 7121 opts, 7122 ) catch |err| switch (err) { 7123 error.NeededSourceLocation => { 7124 const decl = mod.declPtr(block.src_decl); 7125 _ = try sema.analyzeCallArg( 7126 block, 7127 mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src), 7128 param_ty, 7129 uncasted_arg, 7130 opts, 7131 ); 7132 unreachable; 7133 }, 7134 else => |e| return e, 7135 }; 7136 } else { 7137 args[i] = sema.coerceVarArgParam(block, uncasted_arg, .unneeded) catch |err| switch (err) { 7138 error.NeededSourceLocation => { 7139 const decl = mod.declPtr(block.src_decl); 7140 _ = try sema.coerceVarArgParam( 7141 block, 7142 uncasted_arg, 7143 mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src), 7144 ); 7145 unreachable; 7146 }, 7147 else => |e| return e, 7148 }; 7149 } 7150 } 7151 7152 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); 7153 7154 try sema.queueFullTypeResolution(func_ty_info.return_type.toType()); 7155 if (sema.owner_func != null and func_ty_info.return_type.toType().isError(mod)) { 7156 sema.owner_func.?.calls_or_awaits_errorable_fn = true; 7157 } 7158 7159 if (try sema.resolveMaybeUndefVal(func)) |func_val| { 7160 if (mod.intern_pool.indexToFunc(func_val.toIntern()).unwrap()) |func_index| { 7161 try mod.ensureFuncBodyAnalysisQueued(func_index); 7162 } 7163 } 7164 7165 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + 7166 args.len); 7167 const func_inst = try block.addInst(.{ 7168 .tag = call_tag, 7169 .data = .{ .pl_op = .{ 7170 .operand = func, 7171 .payload = sema.addExtraAssumeCapacity(Air.Call{ 7172 .args_len = @as(u32, @intCast(args.len)), 7173 }), 7174 } }, 7175 }); 7176 sema.appendRefsAssumeCapacity(args); 7177 7178 if (call_tag == .call_always_tail) { 7179 if (ensure_result_used) { 7180 try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src); 7181 } 7182 return sema.handleTailCall(block, call_src, func_ty, func_inst); 7183 } 7184 if (block.wantSafety() and func_ty_info.return_type == .noreturn_type) skip_safety: { 7185 // Function pointers and extern functions aren't guaranteed to 7186 // actually be noreturn so we add a safety check for them. 7187 if (try sema.resolveMaybeUndefVal(func)) |func_val| { 7188 switch (mod.intern_pool.indexToKey(func_val.toIntern())) { 7189 .func => break :skip_safety, 7190 .ptr => |ptr| switch (ptr.addr) { 7191 .decl => |decl| if (!mod.declPtr(decl).isExtern(mod)) break :skip_safety, 7192 else => {}, 7193 }, 7194 else => {}, 7195 } 7196 } 7197 try sema.safetyPanic(block, .noreturn_returned); 7198 return Air.Inst.Ref.unreachable_value; 7199 } 7200 if (func_ty_info.return_type == .noreturn_type) { 7201 _ = try block.addNoOp(.unreach); 7202 return Air.Inst.Ref.unreachable_value; 7203 } 7204 break :res func_inst; 7205 }; 7206 7207 if (ensure_result_used) { 7208 try sema.ensureResultUsed(block, sema.typeOf(result), call_src); 7209 } 7210 return result; 7211 } 7212 7213 fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref { 7214 const mod = sema.mod; 7215 const target = mod.getTarget(); 7216 const backend = mod.comp.getZigBackend(); 7217 if (!target_util.supportsTailCall(target, backend)) { 7218 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", .{ 7219 @tagName(backend), @tagName(target.cpu.arch), 7220 }); 7221 } 7222 const func_decl = mod.declPtr(sema.owner_func.?.owner_decl); 7223 if (!func_ty.eql(func_decl.ty, mod)) { 7224 return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{ 7225 func_ty.fmt(mod), func_decl.ty.fmt(mod), 7226 }); 7227 } 7228 _ = try block.addUnOp(.ret, result); 7229 return Air.Inst.Ref.unreachable_value; 7230 } 7231 7232 fn analyzeInlineCallArg( 7233 sema: *Sema, 7234 arg_block: *Block, 7235 param_block: *Block, 7236 arg_src: LazySrcLoc, 7237 inst: Zir.Inst.Index, 7238 new_fn_info: *InternPool.Key.FuncType, 7239 arg_i: *usize, 7240 uncasted_args: []const Air.Inst.Ref, 7241 is_comptime_call: bool, 7242 should_memoize: *bool, 7243 memoized_arg_values: []InternPool.Index, 7244 raw_param_types: []const InternPool.Index, 7245 func_inst: Air.Inst.Ref, 7246 has_comptime_args: *bool, 7247 ) !void { 7248 const mod = sema.mod; 7249 const zir_tags = sema.code.instructions.items(.tag); 7250 switch (zir_tags[inst]) { 7251 .param_comptime, .param_anytype_comptime => has_comptime_args.* = true, 7252 else => {}, 7253 } 7254 switch (zir_tags[inst]) { 7255 .param, .param_comptime => { 7256 // Evaluate the parameter type expression now that previous ones have 7257 // been mapped, and coerce the corresponding argument to it. 7258 const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok; 7259 const param_src = pl_tok.src(); 7260 const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index); 7261 const param_body = sema.code.extra[extra.end..][0..extra.data.body_len]; 7262 const param_ty = param_ty: { 7263 const raw_param_ty = raw_param_types[arg_i.*]; 7264 if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty; 7265 const param_ty_inst = try sema.resolveBody(param_block, param_body, inst); 7266 const param_ty = try sema.analyzeAsType(param_block, param_src, param_ty_inst); 7267 break :param_ty param_ty.toIntern(); 7268 }; 7269 new_fn_info.param_types[arg_i.*] = param_ty; 7270 const uncasted_arg = uncasted_args[arg_i.*]; 7271 if (try sema.typeRequiresComptime(param_ty.toType())) { 7272 _ = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to parameter with comptime-only type must be comptime-known") catch |err| { 7273 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); 7274 return err; 7275 }; 7276 } else if (!is_comptime_call and zir_tags[inst] == .param_comptime) { 7277 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); 7278 } 7279 const casted_arg = sema.coerceExtra(arg_block, param_ty.toType(), uncasted_arg, arg_src, .{ .param_src = .{ 7280 .func_inst = func_inst, 7281 .param_i = @as(u32, @intCast(arg_i.*)), 7282 } }) catch |err| switch (err) { 7283 error.NotCoercible => unreachable, 7284 else => |e| return e, 7285 }; 7286 7287 if (is_comptime_call) { 7288 sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); 7289 const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, casted_arg, "argument to function being called at comptime must be comptime-known") catch |err| { 7290 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); 7291 return err; 7292 }; 7293 switch (arg_val.toIntern()) { 7294 .generic_poison, .generic_poison_type => { 7295 // This function is currently evaluated as part of an as-of-yet unresolvable 7296 // parameter or return type. 7297 return error.GenericPoison; 7298 }, 7299 else => {}, 7300 } 7301 // Needed so that lazy values do not trigger 7302 // assertion due to type not being resolved 7303 // when the hash function is called. 7304 const resolved_arg_val = try sema.resolveLazyValue(arg_val); 7305 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod); 7306 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(param_ty.toType(), mod); 7307 } else { 7308 sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); 7309 } 7310 7311 if (try sema.resolveMaybeUndefVal(casted_arg)) |_| { 7312 has_comptime_args.* = true; 7313 } 7314 7315 arg_i.* += 1; 7316 }, 7317 .param_anytype, .param_anytype_comptime => { 7318 // No coercion needed. 7319 const uncasted_arg = uncasted_args[arg_i.*]; 7320 new_fn_info.param_types[arg_i.*] = sema.typeOf(uncasted_arg).toIntern(); 7321 7322 if (is_comptime_call) { 7323 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); 7324 const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime-known") catch |err| { 7325 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); 7326 return err; 7327 }; 7328 switch (arg_val.toIntern()) { 7329 .generic_poison, .generic_poison_type => { 7330 // This function is currently evaluated as part of an as-of-yet unresolvable 7331 // parameter or return type. 7332 return error.GenericPoison; 7333 }, 7334 else => {}, 7335 } 7336 // Needed so that lazy values do not trigger 7337 // assertion due to type not being resolved 7338 // when the hash function is called. 7339 const resolved_arg_val = try sema.resolveLazyValue(arg_val); 7340 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod); 7341 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(sema.typeOf(uncasted_arg), mod); 7342 } else { 7343 if (zir_tags[inst] == .param_anytype_comptime) { 7344 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); 7345 } 7346 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); 7347 } 7348 7349 if (try sema.resolveMaybeUndefVal(uncasted_arg)) |_| { 7350 has_comptime_args.* = true; 7351 } 7352 7353 arg_i.* += 1; 7354 }, 7355 else => {}, 7356 } 7357 } 7358 7359 fn analyzeCallArg( 7360 sema: *Sema, 7361 block: *Block, 7362 arg_src: LazySrcLoc, 7363 param_ty: Type, 7364 uncasted_arg: Air.Inst.Ref, 7365 opts: CoerceOpts, 7366 ) !Air.Inst.Ref { 7367 try sema.resolveTypeFully(param_ty); 7368 return sema.coerceExtra(block, param_ty, uncasted_arg, arg_src, opts) catch |err| switch (err) { 7369 error.NotCoercible => unreachable, 7370 else => |e| return e, 7371 }; 7372 } 7373 7374 fn analyzeGenericCallArg( 7375 sema: *Sema, 7376 block: *Block, 7377 arg_src: LazySrcLoc, 7378 uncasted_arg: Air.Inst.Ref, 7379 comptime_arg: TypedValue, 7380 runtime_args: []Air.Inst.Ref, 7381 new_fn_info: InternPool.Key.FuncType, 7382 runtime_i: *u32, 7383 ) !void { 7384 const mod = sema.mod; 7385 const is_runtime = comptime_arg.val.isGenericPoison() and 7386 comptime_arg.ty.hasRuntimeBits(mod) and 7387 !(try sema.typeRequiresComptime(comptime_arg.ty)); 7388 if (is_runtime) { 7389 const param_ty = new_fn_info.param_types[runtime_i.*].toType(); 7390 const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); 7391 try sema.queueFullTypeResolution(param_ty); 7392 runtime_args[runtime_i.*] = casted_arg; 7393 runtime_i.* += 1; 7394 } else if (try sema.typeHasOnePossibleValue(comptime_arg.ty)) |_| { 7395 _ = try sema.coerce(block, comptime_arg.ty, uncasted_arg, arg_src); 7396 } 7397 } 7398 7399 fn analyzeGenericCallArgVal( 7400 sema: *Sema, 7401 block: *Block, 7402 arg_src: LazySrcLoc, 7403 arg_ty: Type, 7404 uncasted_arg: Air.Inst.Ref, 7405 reason: []const u8, 7406 ) !Value { 7407 const casted_arg = try sema.coerce(block, arg_ty, uncasted_arg, arg_src); 7408 return sema.resolveLazyValue(try sema.resolveValue(block, arg_src, casted_arg, reason)); 7409 } 7410 7411 fn instantiateGenericCall( 7412 sema: *Sema, 7413 block: *Block, 7414 func: Air.Inst.Ref, 7415 func_src: LazySrcLoc, 7416 call_src: LazySrcLoc, 7417 generic_func_ty: Type, 7418 ensure_result_used: bool, 7419 uncasted_args: []const Air.Inst.Ref, 7420 call_tag: Air.Inst.Tag, 7421 bound_arg_src: ?LazySrcLoc, 7422 call_dbg_node: ?Zir.Inst.Index, 7423 ) CompileError!Air.Inst.Ref { 7424 const mod = sema.mod; 7425 const gpa = sema.gpa; 7426 7427 const func_val = try sema.resolveConstValue(block, func_src, func, "generic function being called must be comptime-known"); 7428 const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { 7429 .func => |function| function.index, 7430 .ptr => |ptr| mod.declPtr(ptr.addr.decl).val.getFunctionIndex(mod).unwrap().?, 7431 else => unreachable, 7432 }; 7433 const module_fn = mod.funcPtr(module_fn_index); 7434 // Check the Module's generic function map with an adapted context, so that we 7435 // can match against `uncasted_args` rather than doing the work below to create a 7436 // generic Scope only to junk it if it matches an existing instantiation. 7437 const fn_owner_decl = mod.declPtr(module_fn.owner_decl); 7438 const namespace_index = fn_owner_decl.src_namespace; 7439 const namespace = mod.namespacePtr(namespace_index); 7440 const fn_zir = namespace.file_scope.zir; 7441 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst); 7442 const zir_tags = fn_zir.instructions.items(.tag); 7443 7444 const monomorphed_args = try sema.arena.alloc(InternPool.Index, mod.typeToFunc(generic_func_ty).?.param_types.len); 7445 const callee_index = callee: { 7446 var arg_i: usize = 0; 7447 var monomorphed_arg_i: u32 = 0; 7448 var known_unique = false; 7449 for (fn_info.param_body) |inst| { 7450 const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?; 7451 var is_comptime = false; 7452 var is_anytype = false; 7453 switch (zir_tags[inst]) { 7454 .param => { 7455 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i))); 7456 }, 7457 .param_comptime => { 7458 is_comptime = true; 7459 }, 7460 .param_anytype => { 7461 is_anytype = true; 7462 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i))); 7463 }, 7464 .param_anytype_comptime => { 7465 is_anytype = true; 7466 is_comptime = true; 7467 }, 7468 else => continue, 7469 } 7470 7471 defer arg_i += 1; 7472 const param_ty = generic_func_ty_info.param_types[arg_i]; 7473 const is_generic = !is_anytype and param_ty == .generic_poison_type; 7474 7475 if (known_unique) { 7476 if (is_comptime or is_anytype or is_generic) { 7477 monomorphed_arg_i += 1; 7478 } 7479 continue; 7480 } 7481 7482 const uncasted_arg = uncasted_args[arg_i]; 7483 const arg_ty = if (is_generic) mod.monomorphed_funcs.getAdapted( 7484 Module.MonomorphedFuncAdaptedKey{ 7485 .func = module_fn_index, 7486 .args = monomorphed_args[0..monomorphed_arg_i], 7487 }, 7488 Module.MonomorphedFuncsAdaptedContext{ .mod = mod }, 7489 ) orelse { 7490 known_unique = true; 7491 monomorphed_arg_i += 1; 7492 continue; 7493 } else if (is_anytype) sema.typeOf(uncasted_arg).toIntern() else param_ty; 7494 const was_comptime = is_comptime; 7495 if (!is_comptime and try sema.typeRequiresComptime(arg_ty.toType())) is_comptime = true; 7496 if (is_comptime or is_anytype) { 7497 // Tuple default values are a part of the type and need to be 7498 // resolved to hash the type. 7499 try sema.resolveTupleLazyValues(block, call_src, arg_ty.toType()); 7500 } 7501 7502 if (is_comptime) { 7503 const casted_arg = sema.analyzeGenericCallArgVal(block, .unneeded, arg_ty.toType(), uncasted_arg, "") catch |err| switch (err) { 7504 error.NeededSourceLocation => { 7505 const decl = mod.declPtr(block.src_decl); 7506 const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src); 7507 _ = try sema.analyzeGenericCallArgVal( 7508 block, 7509 arg_src, 7510 arg_ty.toType(), 7511 uncasted_arg, 7512 if (was_comptime) 7513 "parameter is comptime" 7514 else 7515 "argument to parameter with comptime-only type must be comptime-known", 7516 ); 7517 unreachable; 7518 }, 7519 else => |e| return e, 7520 }; 7521 monomorphed_args[monomorphed_arg_i] = casted_arg.toIntern(); 7522 monomorphed_arg_i += 1; 7523 } else if (is_anytype or is_generic) { 7524 monomorphed_args[monomorphed_arg_i] = try mod.intern(.{ .undef = arg_ty }); 7525 monomorphed_arg_i += 1; 7526 } 7527 } 7528 7529 if (!known_unique) { 7530 if (mod.monomorphed_funcs.getAdapted( 7531 Module.MonomorphedFuncAdaptedKey{ 7532 .func = module_fn_index, 7533 .args = monomorphed_args[0..monomorphed_arg_i], 7534 }, 7535 Module.MonomorphedFuncsAdaptedContext{ .mod = mod }, 7536 )) |callee_func| break :callee mod.intern_pool.indexToKey(callee_func).func.index; 7537 } 7538 7539 const new_module_func_index = try mod.createFunc(undefined); 7540 const new_module_func = mod.funcPtr(new_module_func_index); 7541 7542 new_module_func.generic_owner_decl = module_fn.owner_decl.toOptional(); 7543 new_module_func.comptime_args = null; 7544 7545 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); 7546 7547 // Create a Decl for the new function. 7548 const src_decl_index = namespace.getDeclIndex(mod); 7549 const src_decl = mod.declPtr(src_decl_index); 7550 const new_decl_index = try mod.allocateNewDecl(namespace_index, fn_owner_decl.src_node, src_decl.src_scope); 7551 const new_decl = mod.declPtr(new_decl_index); 7552 // TODO better names for generic function instantiations 7553 const decl_name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}__anon_{d}", .{ 7554 fn_owner_decl.name.fmt(&mod.intern_pool), @intFromEnum(new_decl_index), 7555 }); 7556 new_decl.name = decl_name; 7557 new_decl.src_line = fn_owner_decl.src_line; 7558 new_decl.is_pub = fn_owner_decl.is_pub; 7559 new_decl.is_exported = fn_owner_decl.is_exported; 7560 new_decl.has_align = fn_owner_decl.has_align; 7561 new_decl.has_linksection_or_addrspace = fn_owner_decl.has_linksection_or_addrspace; 7562 new_decl.@"linksection" = fn_owner_decl.@"linksection"; 7563 new_decl.@"addrspace" = fn_owner_decl.@"addrspace"; 7564 new_decl.zir_decl_index = fn_owner_decl.zir_decl_index; 7565 new_decl.alive = true; // This Decl is called at runtime. 7566 new_decl.analysis = .in_progress; 7567 new_decl.generation = mod.generation; 7568 7569 namespace.anon_decls.putAssumeCapacityNoClobber(new_decl_index, {}); 7570 7571 // The generic function Decl is guaranteed to be the first dependency 7572 // of each of its instantiations. 7573 assert(new_decl.dependencies.keys().len == 0); 7574 try mod.declareDeclDependencyType(new_decl_index, module_fn.owner_decl, .function_body); 7575 7576 const new_func = sema.resolveGenericInstantiationType( 7577 block, 7578 fn_zir, 7579 new_decl, 7580 new_decl_index, 7581 uncasted_args, 7582 monomorphed_arg_i, 7583 module_fn_index, 7584 new_module_func_index, 7585 namespace_index, 7586 generic_func_ty, 7587 call_src, 7588 bound_arg_src, 7589 ) catch |err| switch (err) { 7590 error.GenericPoison, error.ComptimeReturn => { 7591 // Resolving the new function type below will possibly declare more decl dependencies 7592 // and so we remove them all here in case of error. 7593 for (new_decl.dependencies.keys()) |dep_index| { 7594 const dep = mod.declPtr(dep_index); 7595 dep.removeDependant(new_decl_index); 7596 } 7597 assert(namespace.anon_decls.orderedRemove(new_decl_index)); 7598 mod.destroyDecl(new_decl_index); 7599 mod.destroyFunc(new_module_func_index); 7600 return err; 7601 }, 7602 else => { 7603 // TODO look up the compile error that happened here and attach a note to it 7604 // pointing here, at the generic instantiation callsite. 7605 if (sema.owner_func) |owner_func| { 7606 owner_func.state = .dependency_failure; 7607 } else { 7608 sema.owner_decl.analysis = .dependency_failure; 7609 } 7610 return err; 7611 }, 7612 }; 7613 7614 break :callee new_func; 7615 }; 7616 const callee = mod.funcPtr(callee_index); 7617 callee.branch_quota = @max(callee.branch_quota, sema.branch_quota); 7618 7619 const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); 7620 7621 // Make a runtime call to the new function, making sure to omit the comptime args. 7622 const comptime_args = callee.comptime_args.?; 7623 const func_ty = mod.declPtr(callee.owner_decl).ty; 7624 const runtime_args_len = @as(u32, @intCast(mod.typeToFunc(func_ty).?.param_types.len)); 7625 const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); 7626 { 7627 var runtime_i: u32 = 0; 7628 var total_i: u32 = 0; 7629 for (fn_info.param_body) |inst| { 7630 switch (zir_tags[inst]) { 7631 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, 7632 else => continue, 7633 } 7634 sema.analyzeGenericCallArg( 7635 block, 7636 .unneeded, 7637 uncasted_args[total_i], 7638 comptime_args[total_i], 7639 runtime_args, 7640 mod.typeToFunc(func_ty).?, 7641 &runtime_i, 7642 ) catch |err| switch (err) { 7643 error.NeededSourceLocation => { 7644 const decl = mod.declPtr(block.src_decl); 7645 _ = try sema.analyzeGenericCallArg( 7646 block, 7647 mod.argSrc(call_src.node_offset.x, decl, total_i, bound_arg_src), 7648 uncasted_args[total_i], 7649 comptime_args[total_i], 7650 runtime_args, 7651 mod.typeToFunc(func_ty).?, 7652 &runtime_i, 7653 ); 7654 unreachable; 7655 }, 7656 else => |e| return e, 7657 }; 7658 total_i += 1; 7659 } 7660 7661 try sema.queueFullTypeResolution(mod.typeToFunc(func_ty).?.return_type.toType()); 7662 } 7663 7664 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); 7665 7666 if (sema.owner_func != null and mod.typeToFunc(func_ty).?.return_type.toType().isError(mod)) { 7667 sema.owner_func.?.calls_or_awaits_errorable_fn = true; 7668 } 7669 7670 try mod.ensureFuncBodyAnalysisQueued(callee_index); 7671 7672 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + 7673 runtime_args_len); 7674 const result = try block.addInst(.{ 7675 .tag = call_tag, 7676 .data = .{ .pl_op = .{ 7677 .operand = callee_inst, 7678 .payload = sema.addExtraAssumeCapacity(Air.Call{ 7679 .args_len = runtime_args_len, 7680 }), 7681 } }, 7682 }); 7683 sema.appendRefsAssumeCapacity(runtime_args); 7684 7685 if (ensure_result_used) { 7686 try sema.ensureResultUsed(block, sema.typeOf(result), call_src); 7687 } 7688 if (call_tag == .call_always_tail) { 7689 return sema.handleTailCall(block, call_src, func_ty, result); 7690 } 7691 if (func_ty.fnReturnType(mod).isNoReturn(mod)) { 7692 _ = try block.addNoOp(.unreach); 7693 return Air.Inst.Ref.unreachable_value; 7694 } 7695 return result; 7696 } 7697 7698 fn resolveGenericInstantiationType( 7699 sema: *Sema, 7700 block: *Block, 7701 fn_zir: Zir, 7702 new_decl: *Decl, 7703 new_decl_index: Decl.Index, 7704 uncasted_args: []const Air.Inst.Ref, 7705 monomorphed_args_len: u32, 7706 module_fn_index: Module.Fn.Index, 7707 new_module_func: Module.Fn.Index, 7708 namespace: Namespace.Index, 7709 generic_func_ty: Type, 7710 call_src: LazySrcLoc, 7711 bound_arg_src: ?LazySrcLoc, 7712 ) !Module.Fn.Index { 7713 const mod = sema.mod; 7714 const gpa = sema.gpa; 7715 7716 const zir_tags = fn_zir.instructions.items(.tag); 7717 const module_fn = mod.funcPtr(module_fn_index); 7718 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst); 7719 7720 // Re-run the block that creates the function, with the comptime parameters 7721 // pre-populated inside `inst_map`. This causes `param_comptime` and 7722 // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a 7723 // new, monomorphized function, with the comptime parameters elided. 7724 var child_sema: Sema = .{ 7725 .mod = mod, 7726 .gpa = gpa, 7727 .arena = sema.arena, 7728 .code = fn_zir, 7729 .owner_decl = new_decl, 7730 .owner_decl_index = new_decl_index, 7731 .func = null, 7732 .func_index = .none, 7733 .fn_ret_ty = Type.void, 7734 .owner_func = null, 7735 .owner_func_index = .none, 7736 // TODO: fully migrate functions into InternPool 7737 .comptime_args = try mod.tmp_hack_arena.allocator().alloc(TypedValue, uncasted_args.len), 7738 .comptime_args_fn_inst = module_fn.zir_body_inst, 7739 .preallocated_new_func = new_module_func.toOptional(), 7740 .is_generic_instantiation = true, 7741 .branch_quota = sema.branch_quota, 7742 .branch_count = sema.branch_count, 7743 .comptime_mutable_decls = sema.comptime_mutable_decls, 7744 }; 7745 defer child_sema.deinit(); 7746 7747 var wip_captures = try WipCaptureScope.init(gpa, new_decl.src_scope); 7748 defer wip_captures.deinit(); 7749 7750 var child_block: Block = .{ 7751 .parent = null, 7752 .sema = &child_sema, 7753 .src_decl = new_decl_index, 7754 .namespace = namespace, 7755 .wip_capture_scope = wip_captures.scope, 7756 .instructions = .{}, 7757 .inlining = null, 7758 .is_comptime = true, 7759 }; 7760 defer { 7761 child_block.instructions.deinit(gpa); 7762 child_block.params.deinit(gpa); 7763 } 7764 7765 try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); 7766 7767 var arg_i: usize = 0; 7768 for (fn_info.param_body) |inst| { 7769 const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?; 7770 var is_comptime = false; 7771 var is_anytype = false; 7772 switch (zir_tags[inst]) { 7773 .param => { 7774 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i))); 7775 }, 7776 .param_comptime => { 7777 is_comptime = true; 7778 }, 7779 .param_anytype => { 7780 is_anytype = true; 7781 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i))); 7782 }, 7783 .param_anytype_comptime => { 7784 is_anytype = true; 7785 is_comptime = true; 7786 }, 7787 else => continue, 7788 } 7789 const arg = uncasted_args[arg_i]; 7790 if (is_comptime) { 7791 const arg_val = (try sema.resolveMaybeUndefVal(arg)).?; 7792 const child_arg = try child_sema.addConstant(arg_val); 7793 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); 7794 } else if (is_anytype) { 7795 const arg_ty = sema.typeOf(arg); 7796 if (try sema.typeRequiresComptime(arg_ty)) { 7797 const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) { 7798 error.NeededSourceLocation => { 7799 const decl = mod.declPtr(block.src_decl); 7800 const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src); 7801 _ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known"); 7802 unreachable; 7803 }, 7804 else => |e| return e, 7805 }; 7806 const child_arg = try child_sema.addConstant(arg_val); 7807 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); 7808 } else { 7809 // We insert into the map an instruction which is runtime-known 7810 // but has the type of the argument. 7811 const child_arg = try child_block.addInst(.{ 7812 .tag = .arg, 7813 .data = .{ .arg = .{ 7814 .ty = try child_sema.addType(arg_ty), 7815 .src_index = @as(u32, @intCast(arg_i)), 7816 } }, 7817 }); 7818 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); 7819 } 7820 } 7821 arg_i += 1; 7822 } 7823 7824 // Save the error trace as our first action in the function. 7825 // If this is unnecessary after all, Liveness will clean it up for us. 7826 const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block); 7827 child_sema.error_return_trace_index_on_fn_entry = error_return_trace_index; 7828 child_block.error_return_trace_index = error_return_trace_index; 7829 7830 const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst); 7831 const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable; 7832 const new_func = new_func_val.getFunctionIndex(mod).unwrap().?; 7833 assert(new_func == new_module_func); 7834 7835 const monomorphed_args_index = @as(u32, @intCast(mod.monomorphed_func_keys.items.len)); 7836 const monomorphed_args = try mod.monomorphed_func_keys.addManyAsSlice(gpa, monomorphed_args_len); 7837 var monomorphed_arg_i: u32 = 0; 7838 try mod.monomorphed_funcs.ensureUnusedCapacityContext(gpa, monomorphed_args_len + 1, .{ .mod = mod }); 7839 7840 arg_i = 0; 7841 for (fn_info.param_body) |inst| { 7842 const generic_func_ty_info = mod.typeToFunc(generic_func_ty).?; 7843 var is_comptime = false; 7844 var is_anytype = false; 7845 switch (zir_tags[inst]) { 7846 .param => { 7847 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i))); 7848 }, 7849 .param_comptime => { 7850 is_comptime = true; 7851 }, 7852 .param_anytype => { 7853 is_anytype = true; 7854 is_comptime = generic_func_ty_info.paramIsComptime(@as(u5, @intCast(arg_i))); 7855 }, 7856 .param_anytype_comptime => { 7857 is_anytype = true; 7858 is_comptime = true; 7859 }, 7860 else => continue, 7861 } 7862 7863 const param_ty = generic_func_ty_info.param_types[arg_i]; 7864 const is_generic = !is_anytype and param_ty == .generic_poison_type; 7865 7866 const arg = child_sema.inst_map.get(inst).?; 7867 const arg_ty = child_sema.typeOf(arg); 7868 7869 if (is_generic) if (mod.monomorphed_funcs.fetchPutAssumeCapacityContext(.{ 7870 .func = module_fn_index, 7871 .args_index = monomorphed_args_index, 7872 .args_len = monomorphed_arg_i, 7873 }, arg_ty.toIntern(), .{ .mod = mod })) |kv| assert(kv.value == arg_ty.toIntern()); 7874 if (!is_comptime and try sema.typeRequiresComptime(arg_ty)) is_comptime = true; 7875 7876 if (is_comptime) { 7877 const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(arg) catch unreachable).?; 7878 monomorphed_args[monomorphed_arg_i] = arg_val.toIntern(); 7879 monomorphed_arg_i += 1; 7880 child_sema.comptime_args[arg_i] = .{ .ty = arg_ty, .val = arg_val }; 7881 } else { 7882 if (is_anytype or is_generic) { 7883 monomorphed_args[monomorphed_arg_i] = try mod.intern(.{ .undef = arg_ty.toIntern() }); 7884 monomorphed_arg_i += 1; 7885 } 7886 child_sema.comptime_args[arg_i] = .{ .ty = arg_ty, .val = Value.generic_poison }; 7887 } 7888 7889 arg_i += 1; 7890 } 7891 7892 try wip_captures.finalize(); 7893 7894 // Populate the Decl ty/val with the function and its type. 7895 new_decl.ty = child_sema.typeOf(new_func_inst); 7896 // If the call evaluated to a return type that requires comptime, never mind 7897 // our generic instantiation. Instead we need to perform a comptime call. 7898 const new_fn_info = mod.typeToFunc(new_decl.ty).?; 7899 if (try sema.typeRequiresComptime(new_fn_info.return_type.toType())) { 7900 return error.ComptimeReturn; 7901 } 7902 // Similarly, if the call evaluated to a generic type we need to instead 7903 // call it inline. 7904 if (new_fn_info.is_generic or new_fn_info.cc == .Inline) { 7905 return error.GenericPoison; 7906 } 7907 7908 new_decl.val = (try mod.intern(.{ .func = .{ 7909 .ty = new_decl.ty.toIntern(), 7910 .index = new_func, 7911 } })).toValue(); 7912 new_decl.alignment = .none; 7913 new_decl.has_tv = true; 7914 new_decl.owns_tv = true; 7915 new_decl.analysis = .complete; 7916 7917 mod.monomorphed_funcs.putAssumeCapacityNoClobberContext(.{ 7918 .func = module_fn_index, 7919 .args_index = monomorphed_args_index, 7920 .args_len = monomorphed_arg_i, 7921 }, new_decl.val.toIntern(), .{ .mod = mod }); 7922 7923 // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field 7924 // will be populated, ensuring it will have `analyzeBody` called with the ZIR 7925 // parameters mapped appropriately. 7926 try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); 7927 return new_func; 7928 } 7929 7930 fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 7931 const mod = sema.mod; 7932 const tuple = switch (mod.intern_pool.indexToKey(ty.toIntern())) { 7933 .anon_struct_type => |tuple| tuple, 7934 else => return, 7935 }; 7936 for (tuple.types, tuple.values) |field_ty, field_val| { 7937 try sema.resolveTupleLazyValues(block, src, field_ty.toType()); 7938 if (field_val == .none) continue; 7939 // TODO: mutate in intern pool 7940 _ = try sema.resolveLazyValue(field_val.toValue()); 7941 } 7942 } 7943 7944 fn emitDbgInline( 7945 sema: *Sema, 7946 block: *Block, 7947 old_func: Module.Fn.Index, 7948 new_func: Module.Fn.Index, 7949 new_func_ty: Type, 7950 tag: Air.Inst.Tag, 7951 ) CompileError!void { 7952 const mod = sema.mod; 7953 if (mod.comp.bin_file.options.strip) return; 7954 7955 // Recursive inline call; no dbg_inline needed. 7956 if (old_func == new_func) return; 7957 7958 _ = try block.addInst(.{ 7959 .tag = tag, 7960 .data = .{ .ty_fn = .{ 7961 .ty = try sema.addType(new_func_ty), 7962 .func = new_func, 7963 } }, 7964 }); 7965 } 7966 7967 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7968 const mod = sema.mod; 7969 const int_type = sema.code.instructions.items(.data)[inst].int_type; 7970 const ty = try mod.intType(int_type.signedness, int_type.bit_count); 7971 return sema.addType(ty); 7972 } 7973 7974 fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7975 const tracy = trace(@src()); 7976 defer tracy.end(); 7977 7978 const mod = sema.mod; 7979 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 7980 const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 7981 const child_type = try sema.resolveType(block, operand_src, inst_data.operand); 7982 if (child_type.zigTypeTag(mod) == .Opaque) { 7983 return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(mod)}); 7984 } else if (child_type.zigTypeTag(mod) == .Null) { 7985 return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(mod)}); 7986 } 7987 const opt_type = try mod.optionalType(child_type.toIntern()); 7988 7989 return sema.addType(opt_type); 7990 } 7991 7992 fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 7993 const mod = sema.mod; 7994 const bin = sema.code.instructions.items(.data)[inst].bin; 7995 const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs); 7996 assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction 7997 if (indexable_ty.zigTypeTag(mod) == .Struct) { 7998 const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod); 7999 return sema.addType(elem_type); 8000 } else { 8001 const elem_type = indexable_ty.elemType2(mod); 8002 return sema.addType(elem_type); 8003 } 8004 } 8005 8006 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8007 const mod = sema.mod; 8008 const un_node = sema.code.instructions.items(.data)[inst].un_node; 8009 const ptr_ty = try sema.resolveType(block, .unneeded, un_node.operand); 8010 assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction 8011 return sema.addType(ptr_ty.childType(mod)); 8012 } 8013 8014 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8015 const mod = sema.mod; 8016 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 8017 const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 8018 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 8019 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8020 const len = @as(u32, @intCast(try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector length must be comptime-known"))); 8021 const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs); 8022 try sema.checkVectorElemType(block, elem_type_src, elem_type); 8023 const vector_type = try mod.vectorType(.{ 8024 .len = len, 8025 .child = elem_type.toIntern(), 8026 }); 8027 return sema.addType(vector_type); 8028 } 8029 8030 fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8031 const tracy = trace(@src()); 8032 defer tracy.end(); 8033 8034 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 8035 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8036 const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; 8037 const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; 8038 const len = try sema.resolveInt(block, len_src, extra.lhs, Type.usize, "array length must be comptime-known"); 8039 const elem_type = try sema.resolveType(block, elem_src, extra.rhs); 8040 try sema.validateArrayElemType(block, elem_type, elem_src); 8041 const array_ty = try sema.mod.arrayType(.{ 8042 .len = len, 8043 .child = elem_type.toIntern(), 8044 }); 8045 8046 return sema.addType(array_ty); 8047 } 8048 8049 fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8050 const tracy = trace(@src()); 8051 defer tracy.end(); 8052 8053 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 8054 const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; 8055 const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; 8056 const sentinel_src: LazySrcLoc = .{ .node_offset_array_type_sentinel = inst_data.src_node }; 8057 const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; 8058 const len = try sema.resolveInt(block, len_src, extra.len, Type.usize, "array length must be comptime-known"); 8059 const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); 8060 try sema.validateArrayElemType(block, elem_type, elem_src); 8061 const uncasted_sentinel = try sema.resolveInst(extra.sentinel); 8062 const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); 8063 const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel, "array sentinel value must be comptime-known"); 8064 const array_ty = try sema.mod.arrayType(.{ 8065 .len = len, 8066 .sentinel = sentinel_val.toIntern(), 8067 .child = elem_type.toIntern(), 8068 }); 8069 8070 return sema.addType(array_ty); 8071 } 8072 8073 fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void { 8074 const mod = sema.mod; 8075 if (elem_type.zigTypeTag(mod) == .Opaque) { 8076 return sema.fail(block, elem_src, "array of opaque type '{}' not allowed", .{elem_type.fmt(mod)}); 8077 } else if (elem_type.zigTypeTag(mod) == .NoReturn) { 8078 return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{}); 8079 } 8080 } 8081 8082 fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8083 const tracy = trace(@src()); 8084 defer tracy.end(); 8085 8086 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 8087 if (true) { 8088 return sema.failWithUseOfAsync(block, inst_data.src()); 8089 } 8090 const mod = sema.mod; 8091 const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node }; 8092 const return_type = try sema.resolveType(block, operand_src, inst_data.operand); 8093 const anyframe_type = try mod.anyframeType(return_type); 8094 8095 return sema.addType(anyframe_type); 8096 } 8097 8098 fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8099 const tracy = trace(@src()); 8100 defer tracy.end(); 8101 8102 const mod = sema.mod; 8103 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 8104 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8105 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 8106 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 8107 const error_set = try sema.resolveType(block, lhs_src, extra.lhs); 8108 const payload = try sema.resolveType(block, rhs_src, extra.rhs); 8109 8110 if (error_set.zigTypeTag(mod) != .ErrorSet) { 8111 return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{ 8112 error_set.fmt(mod), 8113 }); 8114 } 8115 try sema.validateErrorUnionPayloadType(block, payload, rhs_src); 8116 const err_union_ty = try mod.errorUnionType(error_set, payload); 8117 return sema.addType(err_union_ty); 8118 } 8119 8120 fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void { 8121 const mod = sema.mod; 8122 if (payload_ty.zigTypeTag(mod) == .Opaque) { 8123 return sema.fail(block, payload_src, "error union with payload of opaque type '{}' not allowed", .{ 8124 payload_ty.fmt(mod), 8125 }); 8126 } else if (payload_ty.zigTypeTag(mod) == .ErrorSet) { 8127 return sema.fail(block, payload_src, "error union with payload of error set type '{}' not allowed", .{ 8128 payload_ty.fmt(mod), 8129 }); 8130 } 8131 } 8132 8133 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8134 _ = block; 8135 const mod = sema.mod; 8136 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 8137 const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 8138 _ = try mod.getErrorValue(name); 8139 // Create an error set type with only this error value, and return the value. 8140 const error_set_type = try mod.singleErrorSetType(name); 8141 return sema.addConstant((try mod.intern(.{ .err = .{ 8142 .ty = error_set_type.toIntern(), 8143 .name = name, 8144 } })).toValue()); 8145 } 8146 8147 fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 8148 const tracy = trace(@src()); 8149 defer tracy.end(); 8150 8151 const mod = sema.mod; 8152 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 8153 const src = LazySrcLoc.nodeOffset(extra.node); 8154 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 8155 const uncasted_operand = try sema.resolveInst(extra.operand); 8156 const operand = try sema.coerce(block, Type.anyerror, uncasted_operand, operand_src); 8157 8158 if (try sema.resolveMaybeUndefVal(operand)) |val| { 8159 if (val.isUndef(mod)) { 8160 return sema.addConstUndef(Type.err_int); 8161 } 8162 const err_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; 8163 return sema.addConstant(try mod.intValue( 8164 Type.err_int, 8165 try mod.getErrorValue(err_name), 8166 )); 8167 } 8168 8169 const op_ty = sema.typeOf(uncasted_operand); 8170 try sema.resolveInferredErrorSetTy(block, src, op_ty); 8171 if (!op_ty.isAnyError(mod)) { 8172 const names = op_ty.errorSetNames(mod); 8173 switch (names.len) { 8174 0 => return sema.addConstant(try mod.intValue(Type.err_int, 0)), 8175 1 => { 8176 const int = @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(names[0]).?)); 8177 return sema.addIntUnsigned(Type.err_int, int); 8178 }, 8179 else => {}, 8180 } 8181 } 8182 8183 try sema.requireRuntimeBlock(block, src, operand_src); 8184 return block.addBitCast(Type.err_int, operand); 8185 } 8186 8187 fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 8188 const tracy = trace(@src()); 8189 defer tracy.end(); 8190 8191 const mod = sema.mod; 8192 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 8193 const src = LazySrcLoc.nodeOffset(extra.node); 8194 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 8195 const uncasted_operand = try sema.resolveInst(extra.operand); 8196 const operand = try sema.coerce(block, Type.err_int, uncasted_operand, operand_src); 8197 8198 if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { 8199 const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(mod)); 8200 if (int > mod.global_error_set.count() or int == 0) 8201 return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int}); 8202 return sema.addConstant((try mod.intern(.{ .err = .{ 8203 .ty = .anyerror_type, 8204 .name = mod.global_error_set.keys()[int], 8205 } })).toValue()); 8206 } 8207 try sema.requireRuntimeBlock(block, src, operand_src); 8208 if (block.wantSafety()) { 8209 const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); 8210 const zero_val = try sema.addConstant(try mod.intValue(Type.err_int, 0)); 8211 const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val); 8212 const ok = try block.addBinOp(.bit_and, is_lt_len, is_non_zero); 8213 try sema.addSafetyCheck(block, ok, .invalid_error_code); 8214 } 8215 return block.addInst(.{ 8216 .tag = .bitcast, 8217 .data = .{ .ty_op = .{ 8218 .ty = Air.Inst.Ref.anyerror_type, 8219 .operand = operand, 8220 } }, 8221 }); 8222 } 8223 8224 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8225 const tracy = trace(@src()); 8226 defer tracy.end(); 8227 8228 const mod = sema.mod; 8229 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 8230 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8231 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 8232 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 8233 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 8234 const lhs = try sema.resolveInst(extra.lhs); 8235 const rhs = try sema.resolveInst(extra.rhs); 8236 if (sema.typeOf(lhs).zigTypeTag(mod) == .Bool and sema.typeOf(rhs).zigTypeTag(mod) == .Bool) { 8237 const msg = msg: { 8238 const msg = try sema.errMsg(block, lhs_src, "expected error set type, found 'bool'", .{}); 8239 errdefer msg.destroy(sema.gpa); 8240 try sema.errNote(block, src, msg, "'||' merges error sets; 'or' performs boolean OR", .{}); 8241 break :msg msg; 8242 }; 8243 return sema.failWithOwnedErrorMsg(msg); 8244 } 8245 const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); 8246 const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); 8247 if (lhs_ty.zigTypeTag(mod) != .ErrorSet) 8248 return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{lhs_ty.fmt(mod)}); 8249 if (rhs_ty.zigTypeTag(mod) != .ErrorSet) 8250 return sema.fail(block, rhs_src, "expected error set type, found '{}'", .{rhs_ty.fmt(mod)}); 8251 8252 // Anything merged with anyerror is anyerror. 8253 if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) { 8254 return Air.Inst.Ref.anyerror_type; 8255 } 8256 8257 if (mod.typeToInferredErrorSetIndex(lhs_ty).unwrap()) |ies_index| { 8258 try sema.resolveInferredErrorSet(block, src, ies_index); 8259 // isAnyError might have changed from a false negative to a true positive after resolution. 8260 if (lhs_ty.isAnyError(mod)) { 8261 return Air.Inst.Ref.anyerror_type; 8262 } 8263 } 8264 if (mod.typeToInferredErrorSetIndex(rhs_ty).unwrap()) |ies_index| { 8265 try sema.resolveInferredErrorSet(block, src, ies_index); 8266 // isAnyError might have changed from a false negative to a true positive after resolution. 8267 if (rhs_ty.isAnyError(mod)) { 8268 return Air.Inst.Ref.anyerror_type; 8269 } 8270 } 8271 8272 const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty); 8273 return sema.addType(err_set_ty); 8274 } 8275 8276 fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8277 _ = block; 8278 const tracy = trace(@src()); 8279 defer tracy.end(); 8280 8281 const mod = sema.mod; 8282 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 8283 const name = inst_data.get(sema.code); 8284 return sema.addConstant((try mod.intern(.{ 8285 .enum_literal = try mod.intern_pool.getOrPutString(sema.gpa, name), 8286 })).toValue()); 8287 } 8288 8289 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8290 const mod = sema.mod; 8291 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 8292 const src = inst_data.src(); 8293 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 8294 const operand = try sema.resolveInst(inst_data.operand); 8295 const operand_ty = sema.typeOf(operand); 8296 8297 const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(mod)) { 8298 .Enum => operand, 8299 .Union => blk: { 8300 const union_ty = try sema.resolveTypeFields(operand_ty); 8301 const tag_ty = union_ty.unionTagType(mod) orelse { 8302 return sema.fail( 8303 block, 8304 operand_src, 8305 "untagged union '{}' cannot be converted to integer", 8306 .{src}, 8307 ); 8308 }; 8309 break :blk try sema.unionToTag(block, tag_ty, operand, operand_src); 8310 }, 8311 else => { 8312 return sema.fail(block, operand_src, "expected enum or tagged union, found '{}'", .{ 8313 operand_ty.fmt(mod), 8314 }); 8315 }, 8316 }; 8317 const enum_tag_ty = sema.typeOf(enum_tag); 8318 8319 const int_tag_ty = enum_tag_ty.intTagType(mod); 8320 8321 if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| { 8322 return sema.addConstant(try mod.getCoerced(opv, int_tag_ty)); 8323 } 8324 8325 if (try sema.resolveMaybeUndefVal(enum_tag)) |enum_tag_val| { 8326 const val = try enum_tag_val.intFromEnum(enum_tag_ty, mod); 8327 return sema.addConstant(val); 8328 } 8329 8330 try sema.requireRuntimeBlock(block, src, operand_src); 8331 return block.addBitCast(int_tag_ty, enum_tag); 8332 } 8333 8334 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8335 const mod = sema.mod; 8336 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 8337 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 8338 const src = inst_data.src(); 8339 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 8340 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt"); 8341 const operand = try sema.resolveInst(extra.rhs); 8342 8343 if (dest_ty.zigTypeTag(mod) != .Enum) { 8344 return sema.fail(block, src, "expected enum, found '{}'", .{dest_ty.fmt(mod)}); 8345 } 8346 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); 8347 8348 if (try sema.resolveMaybeUndefVal(operand)) |int_val| { 8349 if (dest_ty.isNonexhaustiveEnum(mod)) { 8350 const int_tag_ty = dest_ty.intTagType(mod); 8351 if (try sema.intFitsInType(int_val, int_tag_ty, null)) { 8352 return sema.addConstant(try mod.getCoerced(int_val, dest_ty)); 8353 } 8354 const msg = msg: { 8355 const msg = try sema.errMsg( 8356 block, 8357 src, 8358 "int value '{}' out of range of non-exhaustive enum '{}'", 8359 .{ int_val.fmtValue(sema.typeOf(operand), mod), dest_ty.fmt(mod) }, 8360 ); 8361 errdefer msg.destroy(sema.gpa); 8362 try sema.addDeclaredHereNote(msg, dest_ty); 8363 break :msg msg; 8364 }; 8365 return sema.failWithOwnedErrorMsg(msg); 8366 } 8367 if (int_val.isUndef(mod)) { 8368 return sema.failWithUseOfUndef(block, operand_src); 8369 } 8370 if (!(try sema.enumHasInt(dest_ty, int_val))) { 8371 const msg = msg: { 8372 const msg = try sema.errMsg( 8373 block, 8374 src, 8375 "enum '{}' has no tag with value '{}'", 8376 .{ dest_ty.fmt(mod), int_val.fmtValue(sema.typeOf(operand), mod) }, 8377 ); 8378 errdefer msg.destroy(sema.gpa); 8379 try sema.addDeclaredHereNote(msg, dest_ty); 8380 break :msg msg; 8381 }; 8382 return sema.failWithOwnedErrorMsg(msg); 8383 } 8384 return sema.addConstant(try mod.getCoerced(int_val, dest_ty)); 8385 } 8386 8387 if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| { 8388 const result = try sema.addConstant(opv); 8389 // The operand is runtime-known but the result is comptime-known. In 8390 // this case we still need a safety check. 8391 // TODO add a safety check here. we can't use is_named_enum_value - 8392 // it needs to convert the enum back to int and make sure it equals the operand int. 8393 return result; 8394 } 8395 8396 try sema.requireRuntimeBlock(block, src, operand_src); 8397 const result = try block.addTyOp(.intcast, dest_ty, operand); 8398 if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum(mod) and 8399 mod.backendSupportsFeature(.is_named_enum_value)) 8400 { 8401 const ok = try block.addUnOp(.is_named_enum_value, result); 8402 try sema.addSafetyCheck(block, ok, .invalid_enum_value); 8403 } 8404 return result; 8405 } 8406 8407 /// Pointer in, pointer out. 8408 fn zirOptionalPayloadPtr( 8409 sema: *Sema, 8410 block: *Block, 8411 inst: Zir.Inst.Index, 8412 safety_check: bool, 8413 ) CompileError!Air.Inst.Ref { 8414 const tracy = trace(@src()); 8415 defer tracy.end(); 8416 8417 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 8418 const optional_ptr = try sema.resolveInst(inst_data.operand); 8419 const src = inst_data.src(); 8420 8421 return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); 8422 } 8423 8424 fn analyzeOptionalPayloadPtr( 8425 sema: *Sema, 8426 block: *Block, 8427 src: LazySrcLoc, 8428 optional_ptr: Air.Inst.Ref, 8429 safety_check: bool, 8430 initializing: bool, 8431 ) CompileError!Air.Inst.Ref { 8432 const mod = sema.mod; 8433 const optional_ptr_ty = sema.typeOf(optional_ptr); 8434 assert(optional_ptr_ty.zigTypeTag(mod) == .Pointer); 8435 8436 const opt_type = optional_ptr_ty.childType(mod); 8437 if (opt_type.zigTypeTag(mod) != .Optional) { 8438 return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(mod)}); 8439 } 8440 8441 const child_type = opt_type.optionalChild(mod); 8442 const child_pointer = try mod.ptrType(.{ 8443 .child = child_type.toIntern(), 8444 .flags = .{ 8445 .is_const = optional_ptr_ty.isConstPtr(mod), 8446 .address_space = optional_ptr_ty.ptrAddressSpace(mod), 8447 }, 8448 }); 8449 8450 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| { 8451 if (initializing) { 8452 if (!ptr_val.isComptimeMutablePtr(mod)) { 8453 // If the pointer resulting from this function was stored at comptime, 8454 // the optional non-null bit would be set that way. But in this case, 8455 // we need to emit a runtime instruction to do it. 8456 _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); 8457 } 8458 return sema.addConstant((try mod.intern(.{ .ptr = .{ 8459 .ty = child_pointer.toIntern(), 8460 .addr = .{ .opt_payload = ptr_val.toIntern() }, 8461 } })).toValue()); 8462 } 8463 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| { 8464 if (val.isNull(mod)) { 8465 return sema.fail(block, src, "unable to unwrap null", .{}); 8466 } 8467 // The same Value represents the pointer to the optional and the payload. 8468 return sema.addConstant((try mod.intern(.{ .ptr = .{ 8469 .ty = child_pointer.toIntern(), 8470 .addr = .{ .opt_payload = ptr_val.toIntern() }, 8471 } })).toValue()); 8472 } 8473 } 8474 8475 try sema.requireRuntimeBlock(block, src, null); 8476 if (safety_check and block.wantSafety()) { 8477 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); 8478 try sema.addSafetyCheck(block, is_non_null, .unwrap_null); 8479 } 8480 const air_tag: Air.Inst.Tag = if (initializing) 8481 .optional_payload_ptr_set 8482 else 8483 .optional_payload_ptr; 8484 return block.addTyOp(air_tag, child_pointer, optional_ptr); 8485 } 8486 8487 /// Value in, value out. 8488 fn zirOptionalPayload( 8489 sema: *Sema, 8490 block: *Block, 8491 inst: Zir.Inst.Index, 8492 safety_check: bool, 8493 ) CompileError!Air.Inst.Ref { 8494 const tracy = trace(@src()); 8495 defer tracy.end(); 8496 8497 const mod = sema.mod; 8498 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 8499 const src = inst_data.src(); 8500 const operand = try sema.resolveInst(inst_data.operand); 8501 const operand_ty = sema.typeOf(operand); 8502 const result_ty = switch (operand_ty.zigTypeTag(mod)) { 8503 .Optional => operand_ty.optionalChild(mod), 8504 .Pointer => t: { 8505 if (operand_ty.ptrSize(mod) != .C) { 8506 return sema.failWithExpectedOptionalType(block, src, operand_ty); 8507 } 8508 // TODO https://github.com/ziglang/zig/issues/6597 8509 if (true) break :t operand_ty; 8510 const ptr_info = operand_ty.ptrInfo(mod); 8511 break :t try mod.ptrType(.{ 8512 .child = ptr_info.child, 8513 .flags = .{ 8514 .alignment = ptr_info.flags.alignment, 8515 .is_const = ptr_info.flags.is_const, 8516 .is_volatile = ptr_info.flags.is_volatile, 8517 .is_allowzero = ptr_info.flags.is_allowzero, 8518 .address_space = ptr_info.flags.address_space, 8519 }, 8520 }); 8521 }, 8522 else => return sema.failWithExpectedOptionalType(block, src, operand_ty), 8523 }; 8524 8525 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 8526 return if (val.optionalValue(mod)) |payload| 8527 sema.addConstant(payload) 8528 else 8529 sema.fail(block, src, "unable to unwrap null", .{}); 8530 } 8531 8532 try sema.requireRuntimeBlock(block, src, null); 8533 if (safety_check and block.wantSafety()) { 8534 const is_non_null = try block.addUnOp(.is_non_null, operand); 8535 try sema.addSafetyCheck(block, is_non_null, .unwrap_null); 8536 } 8537 return block.addTyOp(.optional_payload, result_ty, operand); 8538 } 8539 8540 /// Value in, value out 8541 fn zirErrUnionPayload( 8542 sema: *Sema, 8543 block: *Block, 8544 inst: Zir.Inst.Index, 8545 ) CompileError!Air.Inst.Ref { 8546 const tracy = trace(@src()); 8547 defer tracy.end(); 8548 8549 const mod = sema.mod; 8550 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 8551 const src = inst_data.src(); 8552 const operand = try sema.resolveInst(inst_data.operand); 8553 const operand_src = src; 8554 const err_union_ty = sema.typeOf(operand); 8555 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 8556 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ 8557 err_union_ty.fmt(mod), 8558 }); 8559 } 8560 return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); 8561 } 8562 8563 fn analyzeErrUnionPayload( 8564 sema: *Sema, 8565 block: *Block, 8566 src: LazySrcLoc, 8567 err_union_ty: Type, 8568 operand: Air.Inst.Ref, 8569 operand_src: LazySrcLoc, 8570 safety_check: bool, 8571 ) CompileError!Air.Inst.Ref { 8572 const mod = sema.mod; 8573 const payload_ty = err_union_ty.errorUnionPayload(mod); 8574 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 8575 if (val.getErrorName(mod).unwrap()) |name| { 8576 return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)}); 8577 } 8578 return sema.addConstant( 8579 mod.intern_pool.indexToKey(val.toIntern()).error_union.val.payload.toValue(), 8580 ); 8581 } 8582 8583 try sema.requireRuntimeBlock(block, src, null); 8584 8585 // If the error set has no fields then no safety check is needed. 8586 if (safety_check and block.wantSafety() and 8587 !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) 8588 { 8589 try sema.panicUnwrapError(block, operand, .unwrap_errunion_err, .is_non_err); 8590 } 8591 8592 return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); 8593 } 8594 8595 /// Pointer in, pointer out. 8596 fn zirErrUnionPayloadPtr( 8597 sema: *Sema, 8598 block: *Block, 8599 inst: Zir.Inst.Index, 8600 ) CompileError!Air.Inst.Ref { 8601 const tracy = trace(@src()); 8602 defer tracy.end(); 8603 8604 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 8605 const operand = try sema.resolveInst(inst_data.operand); 8606 const src = inst_data.src(); 8607 8608 return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); 8609 } 8610 8611 fn analyzeErrUnionPayloadPtr( 8612 sema: *Sema, 8613 block: *Block, 8614 src: LazySrcLoc, 8615 operand: Air.Inst.Ref, 8616 safety_check: bool, 8617 initializing: bool, 8618 ) CompileError!Air.Inst.Ref { 8619 const mod = sema.mod; 8620 const operand_ty = sema.typeOf(operand); 8621 assert(operand_ty.zigTypeTag(mod) == .Pointer); 8622 8623 if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) { 8624 return sema.fail(block, src, "expected error union type, found '{}'", .{ 8625 operand_ty.childType(mod).fmt(mod), 8626 }); 8627 } 8628 8629 const err_union_ty = operand_ty.childType(mod); 8630 const payload_ty = err_union_ty.errorUnionPayload(mod); 8631 const operand_pointer_ty = try mod.ptrType(.{ 8632 .child = payload_ty.toIntern(), 8633 .flags = .{ 8634 .is_const = operand_ty.isConstPtr(mod), 8635 .address_space = operand_ty.ptrAddressSpace(mod), 8636 }, 8637 }); 8638 8639 if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| { 8640 if (initializing) { 8641 if (!ptr_val.isComptimeMutablePtr(mod)) { 8642 // If the pointer resulting from this function was stored at comptime, 8643 // the error union error code would be set that way. But in this case, 8644 // we need to emit a runtime instruction to do it. 8645 try sema.requireRuntimeBlock(block, src, null); 8646 _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); 8647 } 8648 return sema.addConstant((try mod.intern(.{ .ptr = .{ 8649 .ty = operand_pointer_ty.toIntern(), 8650 .addr = .{ .eu_payload = ptr_val.toIntern() }, 8651 } })).toValue()); 8652 } 8653 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| { 8654 if (val.getErrorName(mod).unwrap()) |name| { 8655 return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&mod.intern_pool)}); 8656 } 8657 return sema.addConstant((try mod.intern(.{ .ptr = .{ 8658 .ty = operand_pointer_ty.toIntern(), 8659 .addr = .{ .eu_payload = ptr_val.toIntern() }, 8660 } })).toValue()); 8661 } 8662 } 8663 8664 try sema.requireRuntimeBlock(block, src, null); 8665 8666 // If the error set has no fields then no safety check is needed. 8667 if (safety_check and block.wantSafety() and 8668 !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) 8669 { 8670 try sema.panicUnwrapError(block, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); 8671 } 8672 8673 const air_tag: Air.Inst.Tag = if (initializing) 8674 .errunion_payload_ptr_set 8675 else 8676 .unwrap_errunion_payload_ptr; 8677 return block.addTyOp(air_tag, operand_pointer_ty, operand); 8678 } 8679 8680 /// Value in, value out 8681 fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8682 const tracy = trace(@src()); 8683 defer tracy.end(); 8684 8685 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 8686 const src = inst_data.src(); 8687 const operand = try sema.resolveInst(inst_data.operand); 8688 return sema.analyzeErrUnionCode(block, src, operand); 8689 } 8690 8691 fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { 8692 const mod = sema.mod; 8693 const operand_ty = sema.typeOf(operand); 8694 if (operand_ty.zigTypeTag(mod) != .ErrorUnion) { 8695 return sema.fail(block, src, "expected error union type, found '{}'", .{ 8696 operand_ty.fmt(mod), 8697 }); 8698 } 8699 8700 const result_ty = operand_ty.errorUnionSet(mod); 8701 8702 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 8703 return sema.addConstant((try mod.intern(.{ .err = .{ 8704 .ty = result_ty.toIntern(), 8705 .name = mod.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, 8706 } })).toValue()); 8707 } 8708 8709 try sema.requireRuntimeBlock(block, src, null); 8710 return block.addTyOp(.unwrap_errunion_err, result_ty, operand); 8711 } 8712 8713 /// Pointer in, value out 8714 fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 8715 const tracy = trace(@src()); 8716 defer tracy.end(); 8717 8718 const mod = sema.mod; 8719 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 8720 const src = inst_data.src(); 8721 const operand = try sema.resolveInst(inst_data.operand); 8722 const operand_ty = sema.typeOf(operand); 8723 assert(operand_ty.zigTypeTag(mod) == .Pointer); 8724 8725 if (operand_ty.childType(mod).zigTypeTag(mod) != .ErrorUnion) { 8726 return sema.fail(block, src, "expected error union type, found '{}'", .{ 8727 operand_ty.childType(mod).fmt(mod), 8728 }); 8729 } 8730 8731 const result_ty = operand_ty.childType(mod).errorUnionSet(mod); 8732 8733 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { 8734 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { 8735 assert(val.getErrorName(mod) != .none); 8736 return sema.addConstant(val); 8737 } 8738 } 8739 8740 try sema.requireRuntimeBlock(block, src, null); 8741 return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); 8742 } 8743 8744 fn zirFunc( 8745 sema: *Sema, 8746 block: *Block, 8747 inst: Zir.Inst.Index, 8748 inferred_error_set: bool, 8749 ) CompileError!Air.Inst.Ref { 8750 const tracy = trace(@src()); 8751 defer tracy.end(); 8752 8753 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 8754 const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); 8755 const target = sema.mod.getTarget(); 8756 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node }; 8757 8758 var extra_index = extra.end; 8759 8760 const ret_ty: Type = switch (extra.data.ret_body_len) { 8761 0 => Type.void, 8762 1 => blk: { 8763 const ret_ty_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 8764 extra_index += 1; 8765 if (sema.resolveType(block, ret_ty_src, ret_ty_ref)) |ret_ty| { 8766 break :blk ret_ty; 8767 } else |err| switch (err) { 8768 error.GenericPoison => { 8769 break :blk Type.generic_poison; 8770 }, 8771 else => |e| return e, 8772 } 8773 }, 8774 else => blk: { 8775 const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; 8776 extra_index += ret_ty_body.len; 8777 8778 const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type, "return type must be comptime-known"); 8779 break :blk ret_ty_val.toType(); 8780 }, 8781 }; 8782 8783 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 8784 const has_body = extra.data.body_len != 0; 8785 if (has_body) { 8786 extra_index += extra.data.body_len; 8787 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 8788 } 8789 8790 // If this instruction has a body it means it's the type of the `owner_decl` 8791 // otherwise it's a function type without a `callconv` attribute and should 8792 // never be `.C`. 8793 // NOTE: revisit when doing #1717 8794 const cc: std.builtin.CallingConvention = if (sema.owner_decl.is_exported and has_body) 8795 .C 8796 else 8797 .Unspecified; 8798 8799 return sema.funcCommon( 8800 block, 8801 inst_data.src_node, 8802 inst, 8803 .none, 8804 target_util.defaultAddressSpace(target, .function), 8805 FuncLinkSection.default, 8806 cc, 8807 ret_ty, 8808 false, 8809 inferred_error_set, 8810 false, 8811 has_body, 8812 src_locs, 8813 null, 8814 0, 8815 false, 8816 ); 8817 } 8818 8819 fn resolveGenericBody( 8820 sema: *Sema, 8821 block: *Block, 8822 src: LazySrcLoc, 8823 body: []const Zir.Inst.Index, 8824 func_inst: Zir.Inst.Index, 8825 dest_ty: Type, 8826 reason: []const u8, 8827 ) !Value { 8828 assert(body.len != 0); 8829 8830 const err = err: { 8831 // Make sure any nested param instructions don't clobber our work. 8832 const prev_params = block.params; 8833 block.params = .{}; 8834 defer { 8835 block.params.deinit(sema.gpa); 8836 block.params = prev_params; 8837 } 8838 8839 const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err; 8840 const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err; 8841 const val = sema.resolveConstValue(block, src, result, reason) catch |err| break :err err; 8842 return val; 8843 }; 8844 switch (err) { 8845 error.GenericPoison => { 8846 if (dest_ty.toIntern() == .type_type) { 8847 return Value.generic_poison_type; 8848 } else { 8849 return Value.generic_poison; 8850 } 8851 }, 8852 else => |e| return e, 8853 } 8854 } 8855 8856 /// Given a library name, examines if the library name should end up in 8857 /// `link.File.Options.system_libs` table (for example, libc is always 8858 /// specified via dedicated flag `link.File.Options.link_libc` instead), 8859 /// and puts it there if it doesn't exist. 8860 /// It also dupes the library name which can then be saved as part of the 8861 /// respective `Decl` (either `ExternFn` or `Var`). 8862 /// The liveness of the duped library name is tied to liveness of `Module`. 8863 /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). 8864 fn handleExternLibName( 8865 sema: *Sema, 8866 block: *Block, 8867 src_loc: LazySrcLoc, 8868 lib_name: []const u8, 8869 ) CompileError![:0]u8 { 8870 blk: { 8871 const mod = sema.mod; 8872 const comp = mod.comp; 8873 const target = mod.getTarget(); 8874 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); 8875 if (target_util.is_libc_lib_name(target, lib_name)) { 8876 if (!comp.bin_file.options.link_libc and !comp.bin_file.options.parent_compilation_link_libc) { 8877 return sema.fail( 8878 block, 8879 src_loc, 8880 "dependency on libc must be explicitly specified in the build command", 8881 .{}, 8882 ); 8883 } 8884 comp.bin_file.options.link_libc = true; 8885 break :blk; 8886 } 8887 if (target_util.is_libcpp_lib_name(target, lib_name)) { 8888 if (!comp.bin_file.options.link_libcpp) { 8889 return sema.fail( 8890 block, 8891 src_loc, 8892 "dependency on libc++ must be explicitly specified in the build command", 8893 .{}, 8894 ); 8895 } 8896 comp.bin_file.options.link_libcpp = true; 8897 break :blk; 8898 } 8899 if (mem.eql(u8, lib_name, "unwind")) { 8900 comp.bin_file.options.link_libunwind = true; 8901 break :blk; 8902 } 8903 if (!target.isWasm() and !comp.bin_file.options.pic) { 8904 return sema.fail( 8905 block, 8906 src_loc, 8907 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.", 8908 .{ lib_name, lib_name }, 8909 ); 8910 } 8911 comp.addLinkLib(lib_name) catch |err| { 8912 return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ 8913 lib_name, @errorName(err), 8914 }); 8915 }; 8916 } 8917 return sema.gpa.dupeZ(u8, lib_name); 8918 } 8919 8920 const FuncLinkSection = union(enum) { 8921 generic, 8922 default, 8923 explicit: InternPool.NullTerminatedString, 8924 }; 8925 8926 fn funcCommon( 8927 sema: *Sema, 8928 block: *Block, 8929 src_node_offset: i32, 8930 func_inst: Zir.Inst.Index, 8931 /// null means generic poison 8932 alignment: ?Alignment, 8933 /// null means generic poison 8934 address_space: ?std.builtin.AddressSpace, 8935 /// outer null means generic poison; inner null means default link section 8936 section: FuncLinkSection, 8937 /// null means generic poison 8938 cc: ?std.builtin.CallingConvention, 8939 /// this might be Type.generic_poison 8940 bare_return_type: Type, 8941 var_args: bool, 8942 inferred_error_set: bool, 8943 is_extern: bool, 8944 has_body: bool, 8945 src_locs: Zir.Inst.Func.SrcLocs, 8946 opt_lib_name: ?[]const u8, 8947 noalias_bits: u32, 8948 is_noinline: bool, 8949 ) CompileError!Air.Inst.Ref { 8950 const mod = sema.mod; 8951 const gpa = sema.gpa; 8952 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; 8953 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; 8954 const func_src = LazySrcLoc.nodeOffset(src_node_offset); 8955 8956 var is_generic = bare_return_type.isGenericPoison() or 8957 alignment == null or 8958 address_space == null or 8959 section == .generic or 8960 cc == null; 8961 8962 if (var_args) { 8963 if (is_generic) { 8964 return sema.fail(block, func_src, "generic function cannot be variadic", .{}); 8965 } 8966 if (cc.? != .C) { 8967 return sema.fail(block, cc_src, "variadic function must have 'C' calling convention", .{}); 8968 } 8969 } 8970 8971 var destroy_fn_on_error = false; 8972 const new_func_index = new_func: { 8973 if (!has_body) break :new_func undefined; 8974 if (sema.comptime_args_fn_inst == func_inst) { 8975 const new_func_index = sema.preallocated_new_func.unwrap().?; 8976 sema.preallocated_new_func = .none; // take ownership 8977 break :new_func new_func_index; 8978 } 8979 destroy_fn_on_error = true; 8980 var new_func: Module.Fn = undefined; 8981 // Set this here so that the inferred return type can be printed correctly if it appears in an error. 8982 new_func.owner_decl = sema.owner_decl_index; 8983 const new_func_index = try mod.createFunc(new_func); 8984 break :new_func new_func_index; 8985 }; 8986 errdefer if (destroy_fn_on_error) mod.destroyFunc(new_func_index); 8987 8988 const target = mod.getTarget(); 8989 const fn_ty: Type = fn_ty: { 8990 // In the case of generic calling convention, or generic alignment, we use 8991 // default values which are only meaningful for the generic function, *not* 8992 // the instantiation, which can depend on comptime parameters. 8993 // Related proposal: https://github.com/ziglang/zig/issues/11834 8994 const cc_resolved = cc orelse .Unspecified; 8995 const param_types = try sema.arena.alloc(InternPool.Index, block.params.items.len); 8996 var comptime_bits: u32 = 0; 8997 for (param_types, block.params.items, 0..) |*dest_param_ty, param, i| { 8998 const is_noalias = blk: { 8999 const index = std.math.cast(u5, i) orelse break :blk false; 9000 break :blk @as(u1, @truncate(noalias_bits >> index)) != 0; 9001 }; 9002 dest_param_ty.* = param.ty.toIntern(); 9003 sema.analyzeParameter( 9004 block, 9005 .unneeded, 9006 param, 9007 &comptime_bits, 9008 i, 9009 &is_generic, 9010 cc_resolved, 9011 has_body, 9012 is_noalias, 9013 ) catch |err| switch (err) { 9014 error.NeededSourceLocation => { 9015 const decl = mod.declPtr(block.src_decl); 9016 try sema.analyzeParameter( 9017 block, 9018 Module.paramSrc(src_node_offset, mod, decl, i), 9019 param, 9020 &comptime_bits, 9021 i, 9022 &is_generic, 9023 cc_resolved, 9024 has_body, 9025 is_noalias, 9026 ); 9027 unreachable; 9028 }, 9029 else => |e| return e, 9030 }; 9031 } 9032 9033 var ret_ty_requires_comptime = false; 9034 const ret_poison = if (sema.typeRequiresComptime(bare_return_type)) |ret_comptime| rp: { 9035 ret_ty_requires_comptime = ret_comptime; 9036 break :rp bare_return_type.isGenericPoison(); 9037 } else |err| switch (err) { 9038 error.GenericPoison => rp: { 9039 is_generic = true; 9040 break :rp true; 9041 }, 9042 else => |e| return e, 9043 }; 9044 9045 const return_type: Type = if (!inferred_error_set or ret_poison) 9046 bare_return_type 9047 else blk: { 9048 try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); 9049 const ies_index = try mod.intern_pool.createInferredErrorSet(gpa, .{ 9050 .func = new_func_index, 9051 }); 9052 const error_set_ty = try mod.intern(.{ .inferred_error_set_type = ies_index }); 9053 break :blk try mod.errorUnionType(error_set_ty.toType(), bare_return_type); 9054 }; 9055 9056 if (!return_type.isValidReturnType(mod)) { 9057 const opaque_str = if (return_type.zigTypeTag(mod) == .Opaque) "opaque " else ""; 9058 const msg = msg: { 9059 const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ 9060 opaque_str, return_type.fmt(mod), 9061 }); 9062 errdefer msg.destroy(gpa); 9063 9064 try sema.addDeclaredHereNote(msg, return_type); 9065 break :msg msg; 9066 }; 9067 return sema.failWithOwnedErrorMsg(msg); 9068 } 9069 if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(target, cc_resolved) and 9070 !try sema.validateExternType(return_type, .ret_ty)) 9071 { 9072 const msg = msg: { 9073 const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ 9074 return_type.fmt(mod), @tagName(cc_resolved), 9075 }); 9076 errdefer msg.destroy(gpa); 9077 9078 const src_decl = mod.declPtr(block.src_decl); 9079 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl, mod), return_type, .ret_ty); 9080 9081 try sema.addDeclaredHereNote(msg, return_type); 9082 break :msg msg; 9083 }; 9084 return sema.failWithOwnedErrorMsg(msg); 9085 } 9086 9087 // If the return type is comptime-only but not dependent on parameters then all parameter types also need to be comptime 9088 if (!sema.is_generic_instantiation and has_body and ret_ty_requires_comptime) comptime_check: { 9089 for (block.params.items) |param| { 9090 if (!param.is_comptime) break; 9091 } else break :comptime_check; 9092 9093 const msg = try sema.errMsg( 9094 block, 9095 ret_ty_src, 9096 "function with comptime-only return type '{}' requires all parameters to be comptime", 9097 .{return_type.fmt(mod)}, 9098 ); 9099 try sema.explainWhyTypeIsComptime(msg, ret_ty_src.toSrcLoc(sema.owner_decl, mod), return_type); 9100 9101 const tags = sema.code.instructions.items(.tag); 9102 const data = sema.code.instructions.items(.data); 9103 const param_body = sema.code.getParamBody(func_inst); 9104 for (block.params.items, 0..) |param, i| { 9105 if (!param.is_comptime) { 9106 const param_index = param_body[i]; 9107 const param_src = switch (tags[param_index]) { 9108 .param => data[param_index].pl_tok.src(), 9109 .param_anytype => data[param_index].str_tok.src(), 9110 else => unreachable, 9111 }; 9112 if (param.name.len != 0) { 9113 try sema.errNote(block, param_src, msg, "param '{s}' is required to be comptime", .{param.name}); 9114 } else { 9115 try sema.errNote(block, param_src, msg, "param is required to be comptime", .{}); 9116 } 9117 } 9118 } 9119 return sema.failWithOwnedErrorMsg(msg); 9120 } 9121 9122 const arch = mod.getTarget().cpu.arch; 9123 if (switch (cc_resolved) { 9124 .Unspecified, .C, .Naked, .Async, .Inline => null, 9125 .Interrupt => switch (arch) { 9126 .x86, .x86_64, .avr, .msp430 => null, 9127 else => @as([]const u8, "x86, x86_64, AVR, and MSP430"), 9128 }, 9129 .Signal => switch (arch) { 9130 .avr => null, 9131 else => @as([]const u8, "AVR"), 9132 }, 9133 .Stdcall, .Fastcall, .Thiscall => switch (arch) { 9134 .x86 => null, 9135 else => @as([]const u8, "x86"), 9136 }, 9137 .Vectorcall => switch (arch) { 9138 .x86, .aarch64, .aarch64_be, .aarch64_32 => null, 9139 else => @as([]const u8, "x86 and AArch64"), 9140 }, 9141 .APCS, .AAPCS, .AAPCSVFP => switch (arch) { 9142 .arm, .armeb, .aarch64, .aarch64_be, .aarch64_32, .thumb, .thumbeb => null, 9143 else => @as([]const u8, "ARM"), 9144 }, 9145 .SysV, .Win64 => switch (arch) { 9146 .x86_64 => null, 9147 else => @as([]const u8, "x86_64"), 9148 }, 9149 .Kernel => switch (arch) { 9150 .nvptx, .nvptx64, .amdgcn, .spirv32, .spirv64 => null, 9151 else => @as([]const u8, "nvptx, amdgcn and SPIR-V"), 9152 }, 9153 }) |allowed_platform| { 9154 return sema.fail(block, cc_src, "callconv '{s}' is only available on {s}, not {s}", .{ 9155 @tagName(cc_resolved), 9156 allowed_platform, 9157 @tagName(arch), 9158 }); 9159 } 9160 9161 if (cc_resolved == .Inline and is_noinline) { 9162 return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{}); 9163 } 9164 if (is_generic and sema.no_partial_func_ty) return error.GenericPoison; 9165 is_generic = is_generic or comptime_bits != 0 or ret_ty_requires_comptime; 9166 9167 if (!is_generic and sema.wantErrorReturnTracing(return_type)) { 9168 // Make sure that StackTrace's fields are resolved so that the backend can 9169 // lower this fn type. 9170 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 9171 _ = try sema.resolveTypeFields(unresolved_stack_trace_ty); 9172 } 9173 9174 break :fn_ty try mod.funcType(.{ 9175 .param_types = param_types, 9176 .noalias_bits = noalias_bits, 9177 .comptime_bits = comptime_bits, 9178 .return_type = return_type.toIntern(), 9179 .cc = cc_resolved, 9180 .cc_is_generic = cc == null, 9181 .alignment = alignment orelse .none, 9182 .align_is_generic = alignment == null, 9183 .section_is_generic = section == .generic, 9184 .addrspace_is_generic = address_space == null, 9185 .is_var_args = var_args, 9186 .is_generic = is_generic, 9187 .is_noinline = is_noinline, 9188 }); 9189 }; 9190 9191 sema.owner_decl.@"linksection" = switch (section) { 9192 .generic => .none, 9193 .default => .none, 9194 .explicit => |section_name| section_name.toOptional(), 9195 }; 9196 sema.owner_decl.alignment = alignment orelse .none; 9197 sema.owner_decl.@"addrspace" = address_space orelse .generic; 9198 9199 if (is_extern) { 9200 return sema.addConstant((try mod.intern(.{ .extern_func = .{ 9201 .ty = fn_ty.toIntern(), 9202 .decl = sema.owner_decl_index, 9203 .lib_name = if (opt_lib_name) |lib_name| (try mod.intern_pool.getOrPutString( 9204 gpa, 9205 try sema.handleExternLibName(block, .{ 9206 .node_offset_lib_name = src_node_offset, 9207 }, lib_name), 9208 )).toOptional() else .none, 9209 } })).toValue()); 9210 } 9211 9212 if (!has_body) { 9213 return sema.addType(fn_ty); 9214 } 9215 9216 const is_inline = fn_ty.fnCallingConvention(mod) == .Inline; 9217 const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .none; 9218 9219 const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == func_inst) blk: { 9220 break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr; 9221 } else null; 9222 9223 const new_func = mod.funcPtr(new_func_index); 9224 const hash = new_func.hash; 9225 const generic_owner_decl = if (comptime_args == null) .none else new_func.generic_owner_decl; 9226 new_func.* = .{ 9227 .state = anal_state, 9228 .zir_body_inst = func_inst, 9229 .owner_decl = sema.owner_decl_index, 9230 .generic_owner_decl = generic_owner_decl, 9231 .comptime_args = comptime_args, 9232 .hash = hash, 9233 .lbrace_line = src_locs.lbrace_line, 9234 .rbrace_line = src_locs.rbrace_line, 9235 .lbrace_column = @as(u16, @truncate(src_locs.columns)), 9236 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), 9237 .branch_quota = default_branch_quota, 9238 .is_noinline = is_noinline, 9239 }; 9240 return sema.addConstant((try mod.intern(.{ .func = .{ 9241 .ty = fn_ty.toIntern(), 9242 .index = new_func_index, 9243 } })).toValue()); 9244 } 9245 9246 fn analyzeParameter( 9247 sema: *Sema, 9248 block: *Block, 9249 param_src: LazySrcLoc, 9250 param: Block.Param, 9251 comptime_bits: *u32, 9252 i: usize, 9253 is_generic: *bool, 9254 cc: std.builtin.CallingConvention, 9255 has_body: bool, 9256 is_noalias: bool, 9257 ) !void { 9258 const mod = sema.mod; 9259 const requires_comptime = try sema.typeRequiresComptime(param.ty); 9260 if (param.is_comptime or requires_comptime) { 9261 comptime_bits.* |= @as(u32, 1) << @as(u5, @intCast(i)); // TODO: handle cast error 9262 } 9263 const this_generic = param.ty.isGenericPoison(); 9264 is_generic.* = is_generic.* or this_generic; 9265 const target = mod.getTarget(); 9266 if (param.is_comptime and !target_util.fnCallConvAllowsZigTypes(target, cc)) { 9267 return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); 9268 } 9269 if (this_generic and !sema.no_partial_func_ty and !target_util.fnCallConvAllowsZigTypes(target, cc)) { 9270 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); 9271 } 9272 if (!param.ty.isValidParamType(mod)) { 9273 const opaque_str = if (param.ty.zigTypeTag(mod) == .Opaque) "opaque " else ""; 9274 const msg = msg: { 9275 const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{ 9276 opaque_str, param.ty.fmt(mod), 9277 }); 9278 errdefer msg.destroy(sema.gpa); 9279 9280 try sema.addDeclaredHereNote(msg, param.ty); 9281 break :msg msg; 9282 }; 9283 return sema.failWithOwnedErrorMsg(msg); 9284 } 9285 if (!this_generic and !target_util.fnCallConvAllowsZigTypes(target, cc) and !try sema.validateExternType(param.ty, .param_ty)) { 9286 const msg = msg: { 9287 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ 9288 param.ty.fmt(mod), @tagName(cc), 9289 }); 9290 errdefer msg.destroy(sema.gpa); 9291 9292 const src_decl = mod.declPtr(block.src_decl); 9293 try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl, mod), param.ty, .param_ty); 9294 9295 try sema.addDeclaredHereNote(msg, param.ty); 9296 break :msg msg; 9297 }; 9298 return sema.failWithOwnedErrorMsg(msg); 9299 } 9300 if (!sema.is_generic_instantiation and requires_comptime and !param.is_comptime and has_body) { 9301 const msg = msg: { 9302 const msg = try sema.errMsg(block, param_src, "parameter of type '{}' must be declared comptime", .{ 9303 param.ty.fmt(mod), 9304 }); 9305 errdefer msg.destroy(sema.gpa); 9306 9307 const src_decl = mod.declPtr(block.src_decl); 9308 try sema.explainWhyTypeIsComptime(msg, param_src.toSrcLoc(src_decl, mod), param.ty); 9309 9310 try sema.addDeclaredHereNote(msg, param.ty); 9311 break :msg msg; 9312 }; 9313 return sema.failWithOwnedErrorMsg(msg); 9314 } 9315 if (!sema.is_generic_instantiation and !this_generic and is_noalias and 9316 !(param.ty.zigTypeTag(mod) == .Pointer or param.ty.isPtrLikeOptional(mod))) 9317 { 9318 return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); 9319 } 9320 } 9321 9322 fn zirParam( 9323 sema: *Sema, 9324 block: *Block, 9325 inst: Zir.Inst.Index, 9326 comptime_syntax: bool, 9327 ) CompileError!void { 9328 const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; 9329 const src = inst_data.src(); 9330 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); 9331 const param_name = sema.code.nullTerminatedString(extra.data.name); 9332 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 9333 9334 // We could be in a generic function instantiation, or we could be evaluating a generic 9335 // function without any comptime args provided. 9336 const param_ty = param_ty: { 9337 const err = err: { 9338 // Make sure any nested param instructions don't clobber our work. 9339 const prev_params = block.params; 9340 const prev_preallocated_new_func = sema.preallocated_new_func; 9341 const prev_no_partial_func_type = sema.no_partial_func_ty; 9342 block.params = .{}; 9343 sema.preallocated_new_func = .none; 9344 sema.no_partial_func_ty = true; 9345 defer { 9346 block.params.deinit(sema.gpa); 9347 block.params = prev_params; 9348 sema.preallocated_new_func = prev_preallocated_new_func; 9349 sema.no_partial_func_ty = prev_no_partial_func_type; 9350 } 9351 9352 if (sema.resolveBody(block, body, inst)) |param_ty_inst| { 9353 if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| { 9354 break :param_ty param_ty; 9355 } else |err| break :err err; 9356 } else |err| break :err err; 9357 }; 9358 switch (err) { 9359 error.GenericPoison => { 9360 if (sema.inst_map.get(inst)) |_| { 9361 // A generic function is about to evaluate to another generic function. 9362 // Return an error instead. 9363 return error.GenericPoison; 9364 } 9365 // The type is not available until the generic instantiation. 9366 // We result the param instruction with a poison value and 9367 // insert an anytype parameter. 9368 try block.params.append(sema.gpa, .{ 9369 .ty = Type.generic_poison, 9370 .is_comptime = comptime_syntax, 9371 .name = param_name, 9372 }); 9373 sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison); 9374 return; 9375 }, 9376 else => |e| return e, 9377 } 9378 }; 9379 const is_comptime = sema.typeRequiresComptime(param_ty) catch |err| switch (err) { 9380 error.GenericPoison => { 9381 if (sema.inst_map.get(inst)) |_| { 9382 // A generic function is about to evaluate to another generic function. 9383 // Return an error instead. 9384 return error.GenericPoison; 9385 } 9386 // The type is not available until the generic instantiation. 9387 // We result the param instruction with a poison value and 9388 // insert an anytype parameter. 9389 try block.params.append(sema.gpa, .{ 9390 .ty = Type.generic_poison, 9391 .is_comptime = comptime_syntax, 9392 .name = param_name, 9393 }); 9394 sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison); 9395 return; 9396 }, 9397 else => |e| return e, 9398 } or comptime_syntax; 9399 if (sema.inst_map.get(inst)) |arg| { 9400 if (is_comptime and sema.preallocated_new_func != .none) { 9401 // We have a comptime value for this parameter so it should be elided from the 9402 // function type of the function instruction in this block. 9403 const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) { 9404 error.NeededSourceLocation => { 9405 // We are instantiating a generic function and a comptime arg 9406 // cannot be coerced to the param type, but since we don't 9407 // have the callee source location return `GenericPoison` 9408 // so that the instantiation is failed and the coercion 9409 // is handled by comptime call logic instead. 9410 assert(sema.is_generic_instantiation); 9411 return error.GenericPoison; 9412 }, 9413 else => return err, 9414 }; 9415 sema.inst_map.putAssumeCapacity(inst, coerced_arg); 9416 return; 9417 } 9418 // Even though a comptime argument is provided, the generic function wants to treat 9419 // this as a runtime parameter. 9420 assert(sema.inst_map.remove(inst)); 9421 } 9422 9423 if (sema.preallocated_new_func != .none) { 9424 if (try sema.typeHasOnePossibleValue(param_ty)) |opv| { 9425 // In this case we are instantiating a generic function call with a non-comptime 9426 // non-anytype parameter that ended up being a one-possible-type. 9427 // We don't want the parameter to be part of the instantiated function type. 9428 const result = try sema.addConstant(opv); 9429 sema.inst_map.putAssumeCapacity(inst, result); 9430 return; 9431 } 9432 } 9433 9434 try block.params.append(sema.gpa, .{ 9435 .ty = param_ty, 9436 .is_comptime = comptime_syntax, 9437 .name = param_name, 9438 }); 9439 9440 if (is_comptime) { 9441 // If this is a comptime parameter we can add a constant generic_poison 9442 // since this is also a generic parameter. 9443 const result = try sema.addConstant(Value.generic_poison); 9444 sema.inst_map.putAssumeCapacityNoClobber(inst, result); 9445 } else { 9446 // Otherwise we need a dummy runtime instruction. 9447 const result_index = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 9448 try sema.air_instructions.append(sema.gpa, .{ 9449 .tag = .alloc, 9450 .data = .{ .ty = param_ty }, 9451 }); 9452 const result = Air.indexToRef(result_index); 9453 sema.inst_map.putAssumeCapacityNoClobber(inst, result); 9454 } 9455 } 9456 9457 fn zirParamAnytype( 9458 sema: *Sema, 9459 block: *Block, 9460 inst: Zir.Inst.Index, 9461 comptime_syntax: bool, 9462 ) CompileError!void { 9463 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 9464 const param_name = inst_data.get(sema.code); 9465 9466 if (sema.inst_map.get(inst)) |air_ref| { 9467 const param_ty = sema.typeOf(air_ref); 9468 if (comptime_syntax or try sema.typeRequiresComptime(param_ty)) { 9469 // We have a comptime value for this parameter so it should be elided from the 9470 // function type of the function instruction in this block. 9471 return; 9472 } 9473 if (null != try sema.typeHasOnePossibleValue(param_ty)) { 9474 return; 9475 } 9476 // The map is already populated but we do need to add a runtime parameter. 9477 try block.params.append(sema.gpa, .{ 9478 .ty = param_ty, 9479 .is_comptime = false, 9480 .name = param_name, 9481 }); 9482 return; 9483 } 9484 9485 // We are evaluating a generic function without any comptime args provided. 9486 9487 try block.params.append(sema.gpa, .{ 9488 .ty = Type.generic_poison, 9489 .is_comptime = comptime_syntax, 9490 .name = param_name, 9491 }); 9492 sema.inst_map.putAssumeCapacity(inst, .generic_poison); 9493 } 9494 9495 fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9496 const tracy = trace(@src()); 9497 defer tracy.end(); 9498 9499 const bin_inst = sema.code.instructions.items(.data)[inst].bin; 9500 return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false); 9501 } 9502 9503 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9504 const tracy = trace(@src()); 9505 defer tracy.end(); 9506 9507 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9508 const src = inst_data.src(); 9509 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; 9510 sema.src = src; 9511 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false); 9512 } 9513 9514 fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9515 const tracy = trace(@src()); 9516 defer tracy.end(); 9517 9518 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9519 const src = inst_data.src(); 9520 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; 9521 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true); 9522 } 9523 9524 fn analyzeAs( 9525 sema: *Sema, 9526 block: *Block, 9527 src: LazySrcLoc, 9528 zir_dest_type: Zir.Inst.Ref, 9529 zir_operand: Zir.Inst.Ref, 9530 no_cast_to_comptime_int: bool, 9531 ) CompileError!Air.Inst.Ref { 9532 const mod = sema.mod; 9533 const operand = try sema.resolveInst(zir_operand); 9534 if (zir_dest_type == .var_args_param_type) return operand; 9535 const dest_ty = sema.resolveType(block, src, zir_dest_type) catch |err| switch (err) { 9536 error.GenericPoison => return operand, 9537 else => |e| return e, 9538 }; 9539 if (dest_ty.zigTypeTag(mod) == .NoReturn) { 9540 return sema.fail(block, src, "cannot cast to noreturn", .{}); 9541 } 9542 const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index| 9543 sema.code.instructions.items(.tag)[ptr_index] == .ret_type 9544 else 9545 false; 9546 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) { 9547 error.NotCoercible => unreachable, 9548 else => |e| return e, 9549 }; 9550 } 9551 9552 fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9553 const tracy = trace(@src()); 9554 defer tracy.end(); 9555 9556 const mod = sema.mod; 9557 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 9558 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 9559 const ptr = try sema.resolveInst(inst_data.operand); 9560 const ptr_ty = sema.typeOf(ptr); 9561 if (!ptr_ty.isPtrAtRuntime(mod)) { 9562 return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(mod)}); 9563 } 9564 if (try sema.resolveMaybeUndefValIntable(ptr)) |ptr_val| { 9565 return sema.addConstant( 9566 try mod.intValue(Type.usize, (try ptr_val.getUnsignedIntAdvanced(mod, sema)).?), 9567 ); 9568 } 9569 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); 9570 return block.addUnOp(.int_from_ptr, ptr); 9571 } 9572 9573 fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9574 const tracy = trace(@src()); 9575 defer tracy.end(); 9576 9577 const mod = sema.mod; 9578 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9579 const src = inst_data.src(); 9580 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; 9581 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9582 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); 9583 const object = try sema.resolveInst(extra.lhs); 9584 return sema.fieldVal(block, src, object, field_name, field_name_src); 9585 } 9586 9587 fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index, initializing: bool) CompileError!Air.Inst.Ref { 9588 const tracy = trace(@src()); 9589 defer tracy.end(); 9590 9591 const mod = sema.mod; 9592 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9593 const src = inst_data.src(); 9594 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; 9595 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; 9596 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); 9597 const object_ptr = try sema.resolveInst(extra.lhs); 9598 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, initializing); 9599 } 9600 9601 fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9602 const tracy = trace(@src()); 9603 defer tracy.end(); 9604 9605 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9606 const src = inst_data.src(); 9607 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 9608 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; 9609 const object = try sema.resolveInst(extra.lhs); 9610 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, "field name must be comptime-known"); 9611 return sema.fieldVal(block, src, object, field_name, field_name_src); 9612 } 9613 9614 fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9615 const tracy = trace(@src()); 9616 defer tracy.end(); 9617 9618 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9619 const src = inst_data.src(); 9620 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 9621 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; 9622 const object_ptr = try sema.resolveInst(extra.lhs); 9623 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, "field name must be comptime-known"); 9624 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); 9625 } 9626 9627 fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9628 const tracy = trace(@src()); 9629 defer tracy.end(); 9630 9631 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9632 const src = inst_data.src(); 9633 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 9634 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9635 9636 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast"); 9637 const operand = try sema.resolveInst(extra.rhs); 9638 9639 return sema.intCast(block, inst_data.src(), dest_ty, src, operand, operand_src, true); 9640 } 9641 9642 fn intCast( 9643 sema: *Sema, 9644 block: *Block, 9645 src: LazySrcLoc, 9646 dest_ty: Type, 9647 dest_ty_src: LazySrcLoc, 9648 operand: Air.Inst.Ref, 9649 operand_src: LazySrcLoc, 9650 runtime_safety: bool, 9651 ) CompileError!Air.Inst.Ref { 9652 const mod = sema.mod; 9653 const operand_ty = sema.typeOf(operand); 9654 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src); 9655 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 9656 9657 if (try sema.isComptimeKnown(operand)) { 9658 return sema.coerce(block, dest_ty, operand, operand_src); 9659 } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 9660 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); 9661 } 9662 9663 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); 9664 const is_vector = dest_ty.zigTypeTag(mod) == .Vector; 9665 9666 if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| { 9667 // requirement: intCast(u0, input) iff input == 0 9668 if (runtime_safety and block.wantSafety()) { 9669 try sema.requireRuntimeBlock(block, src, operand_src); 9670 const wanted_info = dest_scalar_ty.intInfo(mod); 9671 const wanted_bits = wanted_info.bits; 9672 9673 if (wanted_bits == 0) { 9674 const ok = if (is_vector) ok: { 9675 const zeros = try sema.splat(operand_ty, try mod.intValue(operand_scalar_ty, 0)); 9676 const zero_inst = try sema.addConstant(zeros); 9677 const is_in_range = try block.addCmpVector(operand, zero_inst, .eq); 9678 const all_in_range = try block.addInst(.{ 9679 .tag = .reduce, 9680 .data = .{ .reduce = .{ .operand = is_in_range, .operation = .And } }, 9681 }); 9682 break :ok all_in_range; 9683 } else ok: { 9684 const zero_inst = try sema.addConstant(try mod.intValue(operand_ty, 0)); 9685 const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst); 9686 break :ok is_in_range; 9687 }; 9688 try sema.addSafetyCheck(block, ok, .cast_truncated_data); 9689 } 9690 } 9691 9692 return sema.addConstant(opv); 9693 } 9694 9695 try sema.requireRuntimeBlock(block, src, operand_src); 9696 if (runtime_safety and block.wantSafety()) { 9697 const actual_info = operand_scalar_ty.intInfo(mod); 9698 const wanted_info = dest_scalar_ty.intInfo(mod); 9699 const actual_bits = actual_info.bits; 9700 const wanted_bits = wanted_info.bits; 9701 const actual_value_bits = actual_bits - @intFromBool(actual_info.signedness == .signed); 9702 const wanted_value_bits = wanted_bits - @intFromBool(wanted_info.signedness == .signed); 9703 9704 // range shrinkage 9705 // requirement: int value fits into target type 9706 if (wanted_value_bits < actual_value_bits) { 9707 const dest_max_val_scalar = try dest_scalar_ty.maxIntScalar(mod, operand_scalar_ty); 9708 const dest_max_val = try sema.splat(operand_ty, dest_max_val_scalar); 9709 const dest_max = try sema.addConstant(dest_max_val); 9710 const diff = try block.addBinOp(.sub_wrap, dest_max, operand); 9711 9712 if (actual_info.signedness == .signed) { 9713 // Reinterpret the sign-bit as part of the value. This will make 9714 // negative differences (`operand` > `dest_max`) appear too big. 9715 const unsigned_operand_ty = try mod.intType(.unsigned, actual_bits); 9716 const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff); 9717 9718 // If the destination type is signed, then we need to double its 9719 // range to account for negative values. 9720 const dest_range_val = if (wanted_info.signedness == .signed) range_val: { 9721 const one = try mod.intValue(unsigned_operand_ty, 1); 9722 const range_minus_one = try dest_max_val.shl(one, unsigned_operand_ty, sema.arena, mod); 9723 break :range_val try sema.intAdd(range_minus_one, one, unsigned_operand_ty, undefined); 9724 } else try mod.getCoerced(dest_max_val, unsigned_operand_ty); 9725 const dest_range = try sema.addConstant(dest_range_val); 9726 9727 const ok = if (is_vector) ok: { 9728 const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte); 9729 const all_in_range = try block.addInst(.{ 9730 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 9731 .data = .{ .reduce = .{ 9732 .operand = is_in_range, 9733 .operation = .And, 9734 } }, 9735 }); 9736 break :ok all_in_range; 9737 } else ok: { 9738 const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range); 9739 break :ok is_in_range; 9740 }; 9741 // TODO negative_to_unsigned? 9742 try sema.addSafetyCheck(block, ok, .cast_truncated_data); 9743 } else { 9744 const ok = if (is_vector) ok: { 9745 const is_in_range = try block.addCmpVector(diff, dest_max, .lte); 9746 const all_in_range = try block.addInst(.{ 9747 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 9748 .data = .{ .reduce = .{ 9749 .operand = is_in_range, 9750 .operation = .And, 9751 } }, 9752 }); 9753 break :ok all_in_range; 9754 } else ok: { 9755 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max); 9756 break :ok is_in_range; 9757 }; 9758 try sema.addSafetyCheck(block, ok, .cast_truncated_data); 9759 } 9760 } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) { 9761 // no shrinkage, yes sign loss 9762 // requirement: signed to unsigned >= 0 9763 const ok = if (is_vector) ok: { 9764 const scalar_zero = try mod.intValue(operand_scalar_ty, 0); 9765 const zero_val = try sema.splat(operand_ty, scalar_zero); 9766 const zero_inst = try sema.addConstant(zero_val); 9767 const is_in_range = try block.addCmpVector(operand, zero_inst, .gte); 9768 const all_in_range = try block.addInst(.{ 9769 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 9770 .data = .{ .reduce = .{ 9771 .operand = is_in_range, 9772 .operation = .And, 9773 } }, 9774 }); 9775 break :ok all_in_range; 9776 } else ok: { 9777 const zero_inst = try sema.addConstant(try mod.intValue(operand_ty, 0)); 9778 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); 9779 break :ok is_in_range; 9780 }; 9781 try sema.addSafetyCheck(block, ok, .negative_to_unsigned); 9782 } 9783 } 9784 return block.addTyOp(.intcast, dest_ty, operand); 9785 } 9786 9787 fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9788 const tracy = trace(@src()); 9789 defer tracy.end(); 9790 9791 const mod = sema.mod; 9792 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9793 const src = inst_data.src(); 9794 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 9795 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9796 9797 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast"); 9798 const operand = try sema.resolveInst(extra.rhs); 9799 const operand_ty = sema.typeOf(operand); 9800 switch (dest_ty.zigTypeTag(mod)) { 9801 .AnyFrame, 9802 .ComptimeFloat, 9803 .ComptimeInt, 9804 .EnumLiteral, 9805 .ErrorSet, 9806 .ErrorUnion, 9807 .Fn, 9808 .Frame, 9809 .NoReturn, 9810 .Null, 9811 .Opaque, 9812 .Optional, 9813 .Type, 9814 .Undefined, 9815 .Void, 9816 => return sema.fail(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}), 9817 9818 .Enum => { 9819 const msg = msg: { 9820 const msg = try sema.errMsg(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}); 9821 errdefer msg.destroy(sema.gpa); 9822 switch (operand_ty.zigTypeTag(mod)) { 9823 .Int, .ComptimeInt => try sema.errNote(block, src, msg, "use @enumFromInt to cast from '{}'", .{operand_ty.fmt(mod)}), 9824 else => {}, 9825 } 9826 9827 break :msg msg; 9828 }; 9829 return sema.failWithOwnedErrorMsg(msg); 9830 }, 9831 9832 .Pointer => { 9833 const msg = msg: { 9834 const msg = try sema.errMsg(block, src, "cannot @bitCast to '{}'", .{dest_ty.fmt(mod)}); 9835 errdefer msg.destroy(sema.gpa); 9836 switch (operand_ty.zigTypeTag(mod)) { 9837 .Int, .ComptimeInt => try sema.errNote(block, src, msg, "use @ptrFromInt to cast from '{}'", .{operand_ty.fmt(mod)}), 9838 .Pointer => try sema.errNote(block, src, msg, "use @ptrCast to cast from '{}'", .{operand_ty.fmt(mod)}), 9839 else => {}, 9840 } 9841 9842 break :msg msg; 9843 }; 9844 return sema.failWithOwnedErrorMsg(msg); 9845 }, 9846 .Struct, .Union => if (dest_ty.containerLayout(mod) == .Auto) { 9847 const container = switch (dest_ty.zigTypeTag(mod)) { 9848 .Struct => "struct", 9849 .Union => "union", 9850 else => unreachable, 9851 }; 9852 return sema.fail(block, src, "cannot @bitCast to '{}'; {s} does not have a guaranteed in-memory layout", .{ 9853 dest_ty.fmt(mod), container, 9854 }); 9855 }, 9856 9857 .Array, 9858 .Bool, 9859 .Float, 9860 .Int, 9861 .Vector, 9862 => {}, 9863 } 9864 switch (operand_ty.zigTypeTag(mod)) { 9865 .AnyFrame, 9866 .ComptimeFloat, 9867 .ComptimeInt, 9868 .EnumLiteral, 9869 .ErrorSet, 9870 .ErrorUnion, 9871 .Fn, 9872 .Frame, 9873 .NoReturn, 9874 .Null, 9875 .Opaque, 9876 .Optional, 9877 .Type, 9878 .Undefined, 9879 .Void, 9880 => return sema.fail(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}), 9881 9882 .Enum => { 9883 const msg = msg: { 9884 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}); 9885 errdefer msg.destroy(sema.gpa); 9886 switch (dest_ty.zigTypeTag(mod)) { 9887 .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @intFromEnum to cast to '{}'", .{dest_ty.fmt(mod)}), 9888 else => {}, 9889 } 9890 9891 break :msg msg; 9892 }; 9893 return sema.failWithOwnedErrorMsg(msg); 9894 }, 9895 .Pointer => { 9896 const msg = msg: { 9897 const msg = try sema.errMsg(block, operand_src, "cannot @bitCast from '{}'", .{operand_ty.fmt(mod)}); 9898 errdefer msg.destroy(sema.gpa); 9899 switch (dest_ty.zigTypeTag(mod)) { 9900 .Int, .ComptimeInt => try sema.errNote(block, operand_src, msg, "use @intFromPtr to cast to '{}'", .{dest_ty.fmt(mod)}), 9901 .Pointer => try sema.errNote(block, operand_src, msg, "use @ptrCast to cast to '{}'", .{dest_ty.fmt(mod)}), 9902 else => {}, 9903 } 9904 9905 break :msg msg; 9906 }; 9907 return sema.failWithOwnedErrorMsg(msg); 9908 }, 9909 .Struct, .Union => if (operand_ty.containerLayout(mod) == .Auto) { 9910 const container = switch (operand_ty.zigTypeTag(mod)) { 9911 .Struct => "struct", 9912 .Union => "union", 9913 else => unreachable, 9914 }; 9915 return sema.fail(block, operand_src, "cannot @bitCast from '{}'; {s} does not have a guaranteed in-memory layout", .{ 9916 operand_ty.fmt(mod), container, 9917 }); 9918 }, 9919 9920 .Array, 9921 .Bool, 9922 .Float, 9923 .Int, 9924 .Vector, 9925 => {}, 9926 } 9927 return sema.bitCast(block, dest_ty, operand, inst_data.src(), operand_src); 9928 } 9929 9930 fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9931 const tracy = trace(@src()); 9932 defer tracy.end(); 9933 9934 const mod = sema.mod; 9935 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9936 const src = inst_data.src(); 9937 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 9938 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9939 9940 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast"); 9941 const operand = try sema.resolveInst(extra.rhs); 9942 9943 const target = mod.getTarget(); 9944 const dest_is_comptime_float = switch (dest_ty.zigTypeTag(mod)) { 9945 .ComptimeFloat => true, 9946 .Float => false, 9947 else => return sema.fail( 9948 block, 9949 src, 9950 "expected float type, found '{}'", 9951 .{dest_ty.fmt(mod)}, 9952 ), 9953 }; 9954 9955 const operand_ty = sema.typeOf(operand); 9956 switch (operand_ty.zigTypeTag(mod)) { 9957 .ComptimeFloat, .Float, .ComptimeInt => {}, 9958 else => return sema.fail( 9959 block, 9960 operand_src, 9961 "expected float type, found '{}'", 9962 .{operand_ty.fmt(mod)}, 9963 ), 9964 } 9965 9966 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { 9967 return sema.addConstant(try operand_val.floatCast(dest_ty, mod)); 9968 } 9969 if (dest_is_comptime_float) { 9970 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{}); 9971 } 9972 const src_bits = operand_ty.floatBits(target); 9973 const dst_bits = dest_ty.floatBits(target); 9974 if (dst_bits >= src_bits) { 9975 return sema.coerce(block, dest_ty, operand, operand_src); 9976 } 9977 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 9978 return block.addTyOp(.fptrunc, dest_ty, operand); 9979 } 9980 9981 fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9982 const tracy = trace(@src()); 9983 defer tracy.end(); 9984 9985 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9986 const src = inst_data.src(); 9987 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 9988 const array = try sema.resolveInst(extra.lhs); 9989 const elem_index = try sema.resolveInst(extra.rhs); 9990 return sema.elemVal(block, src, array, elem_index, src, false); 9991 } 9992 9993 fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 9994 const tracy = trace(@src()); 9995 defer tracy.end(); 9996 9997 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 9998 const src = inst_data.src(); 9999 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; 10000 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10001 const array = try sema.resolveInst(extra.lhs); 10002 const elem_index = try sema.resolveInst(extra.rhs); 10003 return sema.elemVal(block, src, array, elem_index, elem_index_src, true); 10004 } 10005 10006 fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10007 const tracy = trace(@src()); 10008 defer tracy.end(); 10009 10010 const mod = sema.mod; 10011 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 10012 const src = inst_data.src(); 10013 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10014 const array_ptr = try sema.resolveInst(extra.lhs); 10015 const elem_index = try sema.resolveInst(extra.rhs); 10016 const indexable_ty = sema.typeOf(array_ptr); 10017 if (indexable_ty.zigTypeTag(mod) != .Pointer) { 10018 const capture_src: LazySrcLoc = .{ .for_capture_from_input = inst_data.src_node }; 10019 const msg = msg: { 10020 const msg = try sema.errMsg(block, capture_src, "pointer capture of non pointer type '{}'", .{ 10021 indexable_ty.fmt(mod), 10022 }); 10023 errdefer msg.destroy(sema.gpa); 10024 if (indexable_ty.isIndexable(mod)) { 10025 try sema.errNote(block, src, msg, "consider using '&' here", .{}); 10026 } 10027 break :msg msg; 10028 }; 10029 return sema.failWithOwnedErrorMsg(msg); 10030 } 10031 return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false); 10032 } 10033 10034 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10035 const tracy = trace(@src()); 10036 defer tracy.end(); 10037 10038 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 10039 const src = inst_data.src(); 10040 const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; 10041 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 10042 const array_ptr = try sema.resolveInst(extra.lhs); 10043 const elem_index = try sema.resolveInst(extra.rhs); 10044 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true); 10045 } 10046 10047 fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10048 const tracy = trace(@src()); 10049 defer tracy.end(); 10050 10051 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 10052 const src = inst_data.src(); 10053 const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; 10054 const array_ptr = try sema.resolveInst(extra.ptr); 10055 const elem_index = try sema.addIntUnsigned(Type.usize, extra.index); 10056 return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true); 10057 } 10058 10059 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10060 const tracy = trace(@src()); 10061 defer tracy.end(); 10062 10063 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 10064 const src = inst_data.src(); 10065 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; 10066 const array_ptr = try sema.resolveInst(extra.lhs); 10067 const start = try sema.resolveInst(extra.start); 10068 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; 10069 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; 10070 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; 10071 10072 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false); 10073 } 10074 10075 fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10076 const tracy = trace(@src()); 10077 defer tracy.end(); 10078 10079 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 10080 const src = inst_data.src(); 10081 const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data; 10082 const array_ptr = try sema.resolveInst(extra.lhs); 10083 const start = try sema.resolveInst(extra.start); 10084 const end = try sema.resolveInst(extra.end); 10085 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; 10086 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; 10087 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; 10088 10089 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false); 10090 } 10091 10092 fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10093 const tracy = trace(@src()); 10094 defer tracy.end(); 10095 10096 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 10097 const src = inst_data.src(); 10098 const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node }; 10099 const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; 10100 const array_ptr = try sema.resolveInst(extra.lhs); 10101 const start = try sema.resolveInst(extra.start); 10102 const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end); 10103 const sentinel = try sema.resolveInst(extra.sentinel); 10104 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; 10105 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; 10106 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; 10107 10108 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false); 10109 } 10110 10111 fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 10112 const tracy = trace(@src()); 10113 defer tracy.end(); 10114 10115 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 10116 const src = inst_data.src(); 10117 const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; 10118 const array_ptr = try sema.resolveInst(extra.lhs); 10119 const start = try sema.resolveInst(extra.start); 10120 const len = try sema.resolveInst(extra.len); 10121 const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel); 10122 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; 10123 const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset }; 10124 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; 10125 const sentinel_src: LazySrcLoc = if (sentinel == .none) 10126 .unneeded 10127 else 10128 .{ .node_offset_slice_sentinel = inst_data.src_node }; 10129 10130 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); 10131 } 10132 10133 /// Holds common data used when analyzing or resolving switch prong bodies, 10134 /// including setting up captures. 10135 const SwitchProngAnalysis = struct { 10136 sema: *Sema, 10137 /// The block containing the `switch_block` itself. 10138 parent_block: *Block, 10139 /// The raw switch operand value (*not* the condition). Always defined. 10140 operand: Air.Inst.Ref, 10141 /// May be `undefined` if no prong has a by-ref capture. 10142 operand_ptr: Air.Inst.Ref, 10143 /// The switch condition value. For unions, `operand` is the union and `cond` is its tag. 10144 cond: Air.Inst.Ref, 10145 /// If this switch is on an error set, this is the type to assign to the 10146 /// `else` prong. If `null`, the prong should be unreachable. 10147 else_error_ty: ?Type, 10148 /// The index of the `switch_block` instruction itself. 10149 switch_block_inst: Zir.Inst.Index, 10150 /// The dummy index into which inline tag captures should be placed. May be 10151 /// undefined if no prong has a tag capture. 10152 tag_capture_inst: Zir.Inst.Index, 10153 10154 /// Resolve a switch prong which is determined at comptime to have no peers. 10155 /// Uses `resolveBlockBody`. Sets up captures as needed. 10156 fn resolveProngComptime( 10157 spa: SwitchProngAnalysis, 10158 child_block: *Block, 10159 prong_type: enum { normal, special }, 10160 prong_body: []const Zir.Inst.Index, 10161 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10162 /// Must use the `scalar_capture`, `special_capture`, or `multi_capture` union field. 10163 raw_capture_src: Module.SwitchProngSrc, 10164 /// The set of all values which can reach this prong. May be undefined 10165 /// if the prong is special or contains ranges. 10166 case_vals: []const Air.Inst.Ref, 10167 /// The inline capture of this prong. If this is not an inline prong, 10168 /// this is `.none`. 10169 inline_case_capture: Air.Inst.Ref, 10170 /// Whether this prong has an inline tag capture. If `true`, then 10171 /// `inline_case_capture` cannot be `.none`. 10172 has_tag_capture: bool, 10173 merges: *Block.Merges, 10174 ) CompileError!Air.Inst.Ref { 10175 const sema = spa.sema; 10176 const src = sema.code.instructions.items(.data)[spa.switch_block_inst].pl_node.src(); 10177 10178 if (has_tag_capture) { 10179 const tag_ref = try spa.analyzeTagCapture(child_block, raw_capture_src, inline_case_capture); 10180 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); 10181 } 10182 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); 10183 10184 switch (capture) { 10185 .none => { 10186 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); 10187 }, 10188 10189 .by_val, .by_ref => { 10190 const capture_ref = try spa.analyzeCapture( 10191 child_block, 10192 capture == .by_ref, 10193 prong_type == .special, 10194 raw_capture_src, 10195 case_vals, 10196 inline_case_capture, 10197 ); 10198 10199 if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) { 10200 // This prong should be unreachable! 10201 return Air.Inst.Ref.unreachable_value; 10202 } 10203 10204 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); 10205 defer assert(sema.inst_map.remove(spa.switch_block_inst)); 10206 10207 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges); 10208 }, 10209 } 10210 } 10211 10212 /// Analyze a switch prong which may have peers at runtime. 10213 /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed. 10214 fn analyzeProngRuntime( 10215 spa: SwitchProngAnalysis, 10216 case_block: *Block, 10217 prong_type: enum { normal, special }, 10218 prong_body: []const Zir.Inst.Index, 10219 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10220 /// Must use the `scalar`, `special`, or `multi_capture` union field. 10221 raw_capture_src: Module.SwitchProngSrc, 10222 /// The set of all values which can reach this prong. May be undefined 10223 /// if the prong is special or contains ranges. 10224 case_vals: []const Air.Inst.Ref, 10225 /// The inline capture of this prong. If this is not an inline prong, 10226 /// this is `.none`. 10227 inline_case_capture: Air.Inst.Ref, 10228 /// Whether this prong has an inline tag capture. If `true`, then 10229 /// `inline_case_capture` cannot be `.none`. 10230 has_tag_capture: bool, 10231 ) CompileError!void { 10232 const sema = spa.sema; 10233 10234 if (has_tag_capture) { 10235 const tag_ref = try spa.analyzeTagCapture(case_block, raw_capture_src, inline_case_capture); 10236 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref); 10237 } 10238 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst)); 10239 10240 switch (capture) { 10241 .none => { 10242 return sema.analyzeBodyRuntimeBreak(case_block, prong_body); 10243 }, 10244 10245 .by_val, .by_ref => { 10246 const capture_ref = try spa.analyzeCapture( 10247 case_block, 10248 capture == .by_ref, 10249 prong_type == .special, 10250 raw_capture_src, 10251 case_vals, 10252 inline_case_capture, 10253 ); 10254 10255 if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) { 10256 // No need to analyze any further, the prong is unreachable 10257 return; 10258 } 10259 10260 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref); 10261 defer assert(sema.inst_map.remove(spa.switch_block_inst)); 10262 10263 return sema.analyzeBodyRuntimeBreak(case_block, prong_body); 10264 }, 10265 } 10266 } 10267 10268 fn analyzeTagCapture( 10269 spa: SwitchProngAnalysis, 10270 block: *Block, 10271 raw_capture_src: Module.SwitchProngSrc, 10272 inline_case_capture: Air.Inst.Ref, 10273 ) CompileError!Air.Inst.Ref { 10274 const sema = spa.sema; 10275 const mod = sema.mod; 10276 const operand_ty = sema.typeOf(spa.operand); 10277 if (operand_ty.zigTypeTag(mod) != .Union) { 10278 const zir_datas = sema.code.instructions.items(.data); 10279 const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node; 10280 const raw_tag_capture_src: Module.SwitchProngSrc = switch (raw_capture_src) { 10281 .scalar_capture => |i| .{ .scalar_tag_capture = i }, 10282 .multi_capture => |i| .{ .multi_tag_capture = i }, 10283 .special_capture => .special_tag_capture, 10284 else => unreachable, 10285 }; 10286 const capture_src = raw_tag_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none); 10287 const msg = msg: { 10288 const msg = try sema.errMsg(block, capture_src, "cannot capture tag of non-union type '{}'", .{ 10289 operand_ty.fmt(mod), 10290 }); 10291 errdefer msg.destroy(sema.gpa); 10292 try sema.addDeclaredHereNote(msg, operand_ty); 10293 break :msg msg; 10294 }; 10295 return sema.failWithOwnedErrorMsg(msg); 10296 } 10297 assert(inline_case_capture != .none); 10298 return inline_case_capture; 10299 } 10300 10301 fn analyzeCapture( 10302 spa: SwitchProngAnalysis, 10303 block: *Block, 10304 capture_byref: bool, 10305 is_special_prong: bool, 10306 raw_capture_src: Module.SwitchProngSrc, 10307 case_vals: []const Air.Inst.Ref, 10308 inline_case_capture: Air.Inst.Ref, 10309 ) CompileError!Air.Inst.Ref { 10310 const sema = spa.sema; 10311 const mod = sema.mod; 10312 10313 const zir_datas = sema.code.instructions.items(.data); 10314 const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node; 10315 10316 const operand_ty = sema.typeOf(spa.operand); 10317 const operand_ptr_ty = if (capture_byref) sema.typeOf(spa.operand_ptr) else undefined; 10318 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_node_offset }; 10319 10320 if (inline_case_capture != .none) { 10321 const item_val = sema.resolveConstValue(block, .unneeded, inline_case_capture, "") catch unreachable; 10322 if (operand_ty.zigTypeTag(mod) == .Union) { 10323 const field_index = @as(u32, @intCast(operand_ty.unionTagFieldIndex(item_val, mod).?)); 10324 const union_obj = mod.typeToUnion(operand_ty).?; 10325 const field_ty = union_obj.fields.values()[field_index].ty; 10326 if (capture_byref) { 10327 const ptr_field_ty = try mod.ptrType(.{ 10328 .child = field_ty.toIntern(), 10329 .flags = .{ 10330 .is_const = !operand_ptr_ty.ptrIsMutable(mod), 10331 .is_volatile = operand_ptr_ty.isVolatilePtr(mod), 10332 .address_space = operand_ptr_ty.ptrAddressSpace(mod), 10333 }, 10334 }); 10335 if (try sema.resolveDefinedValue(block, sema.src, spa.operand_ptr)) |union_ptr| { 10336 return sema.addConstant( 10337 (try mod.intern(.{ .ptr = .{ 10338 .ty = ptr_field_ty.toIntern(), 10339 .addr = .{ .field = .{ 10340 .base = union_ptr.toIntern(), 10341 .index = field_index, 10342 } }, 10343 } })).toValue(), 10344 ); 10345 } 10346 return block.addStructFieldPtr(spa.operand_ptr, field_index, ptr_field_ty); 10347 } else { 10348 if (try sema.resolveDefinedValue(block, sema.src, spa.operand)) |union_val| { 10349 const tag_and_val = mod.intern_pool.indexToKey(union_val.toIntern()).un; 10350 return sema.addConstant(tag_and_val.val.toValue()); 10351 } 10352 return block.addStructFieldVal(spa.operand, field_index, field_ty); 10353 } 10354 } else if (capture_byref) { 10355 return sema.addConstantMaybeRef(block, operand_ty, item_val, true); 10356 } else { 10357 return inline_case_capture; 10358 } 10359 } 10360 10361 if (is_special_prong) { 10362 if (capture_byref) { 10363 return spa.operand_ptr; 10364 } 10365 10366 switch (operand_ty.zigTypeTag(mod)) { 10367 .ErrorSet => if (spa.else_error_ty) |ty| { 10368 return sema.bitCast(block, ty, spa.operand, operand_src, null); 10369 } else { 10370 try block.addUnreachable(false); 10371 return Air.Inst.Ref.unreachable_value; 10372 }, 10373 else => return spa.operand, 10374 } 10375 } 10376 10377 switch (operand_ty.zigTypeTag(mod)) { 10378 .Union => { 10379 const union_obj = mod.typeToUnion(operand_ty).?; 10380 const first_item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable; 10381 10382 const first_field_index = @as(u32, @intCast(operand_ty.unionTagFieldIndex(first_item_val, mod).?)); 10383 const first_field = union_obj.fields.values()[first_field_index]; 10384 10385 const field_tys = try sema.arena.alloc(Type, case_vals.len); 10386 for (case_vals, field_tys) |item, *field_ty| { 10387 const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; 10388 const field_idx = @as(u32, @intCast(operand_ty.unionTagFieldIndex(item_val, sema.mod).?)); 10389 field_ty.* = union_obj.fields.values()[field_idx].ty; 10390 } 10391 10392 // Fast path: if all the operands are the same type already, we don't need to hit 10393 // PTR! This will also allow us to emit simpler code. 10394 const same_types = for (field_tys[1..]) |field_ty| { 10395 if (!field_ty.eql(field_tys[0], sema.mod)) break false; 10396 } else true; 10397 10398 const capture_ty = if (same_types) field_tys[0] else capture_ty: { 10399 // We need values to run PTR on, so make a bunch of undef constants. 10400 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); 10401 for (dummy_captures, field_tys) |*dummy, field_ty| { 10402 dummy.* = try sema.addConstUndef(field_ty); 10403 } 10404 10405 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); 10406 @memset(case_srcs, .unneeded); 10407 10408 break :capture_ty sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { 10409 error.NeededSourceLocation => { 10410 // This must be a multi-prong so this must be a `multi_capture` src 10411 const multi_idx = raw_capture_src.multi_capture; 10412 const src_decl_ptr = sema.mod.declPtr(block.src_decl); 10413 for (case_srcs, 0..) |*case_src, i| { 10414 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @as(u32, @intCast(i)) } }; 10415 case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10416 } 10417 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10418 _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) { 10419 error.AnalysisFail => { 10420 const msg = sema.err orelse return error.AnalysisFail; 10421 try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{}); 10422 return error.AnalysisFail; 10423 }, 10424 else => |e| return e, 10425 }; 10426 unreachable; 10427 }, 10428 else => |e| return e, 10429 }; 10430 }; 10431 10432 // By-reference captures have some further restrictions which make them easier to emit 10433 if (capture_byref) { 10434 const operand_ptr_info = operand_ptr_ty.ptrInfo(mod); 10435 const capture_ptr_ty = try mod.ptrType(.{ 10436 .child = capture_ty.toIntern(), 10437 .flags = .{ 10438 // TODO: alignment! 10439 .is_const = operand_ptr_info.flags.is_const, 10440 .is_volatile = operand_ptr_info.flags.is_volatile, 10441 .address_space = operand_ptr_info.flags.address_space, 10442 }, 10443 }); 10444 10445 // By-ref captures of hetereogeneous types are only allowed if each field 10446 // pointer type is in-memory coercible to the capture pointer type. 10447 if (!same_types) { 10448 for (field_tys, 0..) |field_ty, i| { 10449 const field_ptr_ty = try mod.ptrType(.{ 10450 .child = field_ty.toIntern(), 10451 .flags = .{ 10452 // TODO: alignment! 10453 .is_const = operand_ptr_info.flags.is_const, 10454 .is_volatile = operand_ptr_info.flags.is_volatile, 10455 .address_space = operand_ptr_info.flags.address_space, 10456 }, 10457 }); 10458 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { 10459 const multi_idx = raw_capture_src.multi_capture; 10460 const src_decl_ptr = sema.mod.declPtr(block.src_decl); 10461 const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10462 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @as(u32, @intCast(i)) } }; 10463 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10464 const msg = msg: { 10465 const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{}); 10466 errdefer msg.destroy(sema.gpa); 10467 try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{ 10468 field_ty.fmt(sema.mod), 10469 capture_ty.fmt(sema.mod), 10470 }); 10471 try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{}); 10472 break :msg msg; 10473 }; 10474 return sema.failWithOwnedErrorMsg(msg); 10475 } 10476 } 10477 } 10478 10479 if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| { 10480 if (op_ptr_val.isUndef(mod)) return sema.addConstUndef(capture_ptr_ty); 10481 return sema.addConstant( 10482 (try mod.intern(.{ .ptr = .{ 10483 .ty = capture_ptr_ty.toIntern(), 10484 .addr = .{ .field = .{ 10485 .base = op_ptr_val.toIntern(), 10486 .index = first_field_index, 10487 } }, 10488 } })).toValue(), 10489 ); 10490 } 10491 10492 try sema.requireRuntimeBlock(block, operand_src, null); 10493 return block.addStructFieldPtr(spa.operand_ptr, first_field_index, capture_ptr_ty); 10494 } 10495 10496 if (try sema.resolveDefinedValue(block, operand_src, spa.operand)) |operand_val| { 10497 if (operand_val.isUndef(mod)) return sema.addConstUndef(capture_ty); 10498 const union_val = mod.intern_pool.indexToKey(operand_val.toIntern()).un; 10499 if (union_val.tag.toValue().isUndef(mod)) return sema.addConstUndef(capture_ty); 10500 const uncoerced = try sema.addConstant(union_val.val.toValue()); 10501 return sema.coerce(block, capture_ty, uncoerced, operand_src); 10502 } 10503 10504 try sema.requireRuntimeBlock(block, operand_src, null); 10505 10506 if (same_types) { 10507 return block.addStructFieldVal(spa.operand, first_field_index, capture_ty); 10508 } 10509 10510 // We may have to emit a switch block which coerces the operand to the capture type. 10511 // If we can, try to avoid that using in-memory coercions. 10512 const first_non_imc = in_mem: { 10513 for (field_tys, 0..) |field_ty, i| { 10514 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { 10515 break :in_mem i; 10516 } 10517 } 10518 // All fields are in-memory coercible to the resolved type! 10519 // Just take the first field and bitcast the result. 10520 const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field.ty); 10521 return block.addBitCast(capture_ty, uncoerced); 10522 }; 10523 10524 // By-val capture with heterogeneous types which are not all in-memory coercible to 10525 // the resolved capture type. We finally have to fall back to the ugly method. 10526 10527 // However, let's first track which operands are in-memory coercible. There may well 10528 // be several, and we can squash all of these cases into the same switch prong using 10529 // a simple bitcast. We'll make this the 'else' prong. 10530 10531 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_tys.len); 10532 in_mem_coercible.unset(first_non_imc); 10533 { 10534 const next = first_non_imc + 1; 10535 for (field_tys[next..], next..) |field_ty, i| { 10536 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { 10537 in_mem_coercible.unset(i); 10538 } 10539 } 10540 } 10541 10542 const capture_block_inst = try block.addInstAsIndex(.{ 10543 .tag = .block, 10544 .data = .{ 10545 .ty_pl = .{ 10546 .ty = try sema.addType(capture_ty), 10547 .payload = undefined, // updated below 10548 }, 10549 }, 10550 }); 10551 10552 const prong_count = field_tys.len - in_mem_coercible.count(); 10553 10554 const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts 10555 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra); 10556 defer cases_extra.deinit(); 10557 10558 { 10559 // Non-bitcast cases 10560 var it = in_mem_coercible.iterator(.{ .kind = .unset }); 10561 while (it.next()) |idx| { 10562 var coerce_block = block.makeSubBlock(); 10563 defer coerce_block.instructions.deinit(sema.gpa); 10564 10565 const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @as(u32, @intCast(idx)), field_tys[idx]); 10566 const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) { 10567 error.NeededSourceLocation => { 10568 const multi_idx = raw_capture_src.multi_capture; 10569 const src_decl_ptr = sema.mod.declPtr(block.src_decl); 10570 const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @as(u32, @intCast(idx)) } }; 10571 const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); 10572 _ = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); 10573 unreachable; 10574 }, 10575 else => |e| return e, 10576 }; 10577 _ = try coerce_block.addBr(capture_block_inst, coerced); 10578 10579 try cases_extra.ensureUnusedCapacity(3 + coerce_block.instructions.items.len); 10580 cases_extra.appendAssumeCapacity(1); // items_len 10581 cases_extra.appendAssumeCapacity(@as(u32, @intCast(coerce_block.instructions.items.len))); // body_len 10582 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item 10583 cases_extra.appendSliceAssumeCapacity(coerce_block.instructions.items); // body 10584 } 10585 } 10586 const else_body_len = len: { 10587 // 'else' prong uses a bitcast 10588 var coerce_block = block.makeSubBlock(); 10589 defer coerce_block.instructions.deinit(sema.gpa); 10590 10591 const first_imc = in_mem_coercible.findFirstSet().?; 10592 const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @as(u32, @intCast(first_imc)), field_tys[first_imc]); 10593 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); 10594 _ = try coerce_block.addBr(capture_block_inst, coerced); 10595 10596 try cases_extra.appendSlice(coerce_block.instructions.items); 10597 break :len coerce_block.instructions.items.len; 10598 }; 10599 10600 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + 10601 cases_extra.items.len + 10602 @typeInfo(Air.Block).Struct.fields.len + 10603 1); 10604 10605 const switch_br_inst = @as(u32, @intCast(sema.air_instructions.len)); 10606 try sema.air_instructions.append(sema.gpa, .{ 10607 .tag = .switch_br, 10608 .data = .{ .pl_op = .{ 10609 .operand = spa.cond, 10610 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ 10611 .cases_len = @as(u32, @intCast(prong_count)), 10612 .else_body_len = @as(u32, @intCast(else_body_len)), 10613 }), 10614 } }, 10615 }); 10616 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); 10617 10618 // Set up block body 10619 sema.air_instructions.items(.data)[capture_block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ 10620 .body_len = 1, 10621 }); 10622 sema.air_extra.appendAssumeCapacity(switch_br_inst); 10623 10624 return Air.indexToRef(capture_block_inst); 10625 }, 10626 .ErrorSet => { 10627 if (capture_byref) { 10628 const capture_src = raw_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none); 10629 return sema.fail( 10630 block, 10631 capture_src, 10632 "error set cannot be captured by reference", 10633 .{}, 10634 ); 10635 } 10636 10637 if (case_vals.len == 1) { 10638 const item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable; 10639 const item_ty = try mod.singleErrorSetType(item_val.getErrorName(mod).unwrap().?); 10640 return sema.bitCast(block, item_ty, spa.operand, operand_src, null); 10641 } 10642 10643 var names: Module.Fn.InferredErrorSet.NameMap = .{}; 10644 try names.ensureUnusedCapacity(sema.arena, case_vals.len); 10645 for (case_vals) |err| { 10646 const err_val = sema.resolveConstValue(block, .unneeded, err, "") catch unreachable; 10647 names.putAssumeCapacityNoClobber(err_val.getErrorName(mod).unwrap().?, {}); 10648 } 10649 const error_ty = try mod.errorSetFromUnsortedNames(names.keys()); 10650 return sema.bitCast(block, error_ty, spa.operand, operand_src, null); 10651 }, 10652 else => { 10653 // In this case the capture value is just the passed-through value 10654 // of the switch condition. 10655 if (capture_byref) { 10656 return spa.operand_ptr; 10657 } else { 10658 return spa.operand; 10659 } 10660 }, 10661 } 10662 } 10663 }; 10664 10665 fn switchCond( 10666 sema: *Sema, 10667 block: *Block, 10668 src: LazySrcLoc, 10669 operand: Air.Inst.Ref, 10670 ) CompileError!Air.Inst.Ref { 10671 const mod = sema.mod; 10672 const operand_ty = sema.typeOf(operand); 10673 switch (operand_ty.zigTypeTag(mod)) { 10674 .Type, 10675 .Void, 10676 .Bool, 10677 .Int, 10678 .Float, 10679 .ComptimeFloat, 10680 .ComptimeInt, 10681 .EnumLiteral, 10682 .Pointer, 10683 .Fn, 10684 .ErrorSet, 10685 .Enum, 10686 => { 10687 if (operand_ty.isSlice(mod)) { 10688 return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)}); 10689 } 10690 if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| { 10691 return sema.addConstant(opv); 10692 } 10693 return operand; 10694 }, 10695 10696 .Union => { 10697 const union_ty = try sema.resolveTypeFields(operand_ty); 10698 const enum_ty = union_ty.unionTagType(mod) orelse { 10699 const msg = msg: { 10700 const msg = try sema.errMsg(block, src, "switch on union with no attached enum", .{}); 10701 errdefer msg.destroy(sema.gpa); 10702 if (union_ty.declSrcLocOrNull(mod)) |union_src| { 10703 try mod.errNoteNonLazy(union_src, msg, "consider 'union(enum)' here", .{}); 10704 } 10705 break :msg msg; 10706 }; 10707 return sema.failWithOwnedErrorMsg(msg); 10708 }; 10709 return sema.unionToTag(block, enum_ty, operand, src); 10710 }, 10711 10712 .ErrorUnion, 10713 .NoReturn, 10714 .Array, 10715 .Struct, 10716 .Undefined, 10717 .Null, 10718 .Optional, 10719 .Opaque, 10720 .Vector, 10721 .Frame, 10722 .AnyFrame, 10723 => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(mod)}), 10724 } 10725 } 10726 10727 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc); 10728 10729 fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { 10730 const tracy = trace(@src()); 10731 defer tracy.end(); 10732 10733 const mod = sema.mod; 10734 const gpa = sema.gpa; 10735 const ip = &mod.intern_pool; 10736 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 10737 const src = inst_data.src(); 10738 const src_node_offset = inst_data.src_node; 10739 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; 10740 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset }; 10741 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); 10742 10743 const raw_operand: struct { val: Air.Inst.Ref, ptr: Air.Inst.Ref } = blk: { 10744 const maybe_ptr = try sema.resolveInst(extra.data.operand); 10745 if (operand_is_ref) { 10746 const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src); 10747 break :blk .{ .val = val, .ptr = maybe_ptr }; 10748 } else { 10749 break :blk .{ .val = maybe_ptr, .ptr = undefined }; 10750 } 10751 }; 10752 10753 const operand = try sema.switchCond(block, operand_src, raw_operand.val); 10754 10755 // AstGen guarantees that the instruction immediately preceding 10756 // switch_block(_ref) is a dbg_stmt 10757 const cond_dbg_node_index = inst - 1; 10758 10759 var header_extra_index: usize = extra.end; 10760 10761 const scalar_cases_len = extra.data.bits.scalar_cases_len; 10762 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: { 10763 const multi_cases_len = sema.code.extra[header_extra_index]; 10764 header_extra_index += 1; 10765 break :blk multi_cases_len; 10766 } else 0; 10767 10768 const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: { 10769 const tag_capture_inst = sema.code.extra[header_extra_index]; 10770 header_extra_index += 1; 10771 // SwitchProngAnalysis wants inst_map to have space for the tag capture. 10772 // Note that the normal capture is referred to via the switch block 10773 // index, which there is already necessarily space for. 10774 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst}); 10775 break :blk tag_capture_inst; 10776 } else undefined; 10777 10778 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); 10779 defer case_vals.deinit(gpa); 10780 10781 const Special = struct { 10782 body: []const Zir.Inst.Index, 10783 end: usize, 10784 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, 10785 is_inline: bool, 10786 has_tag_capture: bool, 10787 }; 10788 10789 const special_prong = extra.data.bits.specialProng(); 10790 const special: Special = switch (special_prong) { 10791 .none => .{ 10792 .body = &.{}, 10793 .end = header_extra_index, 10794 .capture = .none, 10795 .is_inline = false, 10796 .has_tag_capture = false, 10797 }, 10798 .under, .@"else" => blk: { 10799 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[header_extra_index])); 10800 const extra_body_start = header_extra_index + 1; 10801 break :blk .{ 10802 .body = sema.code.extra[extra_body_start..][0..info.body_len], 10803 .end = extra_body_start + info.body_len, 10804 .capture = info.capture, 10805 .is_inline = info.is_inline, 10806 .has_tag_capture = info.has_tag_capture, 10807 }; 10808 }, 10809 }; 10810 10811 const maybe_union_ty = sema.typeOf(raw_operand.val); 10812 const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union; 10813 10814 // Duplicate checking variables later also used for `inline else`. 10815 var seen_enum_fields: []?Module.SwitchProngSrc = &.{}; 10816 var seen_errors = SwitchErrorSet.init(gpa); 10817 var range_set = RangeSet.init(gpa, mod); 10818 var true_count: u8 = 0; 10819 var false_count: u8 = 0; 10820 10821 defer { 10822 range_set.deinit(); 10823 gpa.free(seen_enum_fields); 10824 seen_errors.deinit(); 10825 } 10826 10827 var empty_enum = false; 10828 10829 const operand_ty = sema.typeOf(operand); 10830 const err_set = operand_ty.zigTypeTag(mod) == .ErrorSet; 10831 10832 var else_error_ty: ?Type = null; 10833 10834 // Validate usage of '_' prongs. 10835 if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) { 10836 const msg = msg: { 10837 const msg = try sema.errMsg( 10838 block, 10839 src, 10840 "'_' prong only allowed when switching on non-exhaustive enums", 10841 .{}, 10842 ); 10843 errdefer msg.destroy(gpa); 10844 try sema.errNote( 10845 block, 10846 special_prong_src, 10847 msg, 10848 "'_' prong here", 10849 .{}, 10850 ); 10851 break :msg msg; 10852 }; 10853 return sema.failWithOwnedErrorMsg(msg); 10854 } 10855 10856 // Validate for duplicate items, missing else prong, and invalid range. 10857 switch (operand_ty.zigTypeTag(mod)) { 10858 .Union => unreachable, // handled in zirSwitchCond 10859 .Enum => { 10860 seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount(mod)); 10861 empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(mod); 10862 @memset(seen_enum_fields, null); 10863 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. 10864 10865 var extra_index: usize = special.end; 10866 { 10867 var scalar_i: u32 = 0; 10868 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 10869 const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 10870 extra_index += 1; 10871 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 10872 extra_index += 1 + info.body_len; 10873 10874 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( 10875 block, 10876 seen_enum_fields, 10877 &range_set, 10878 item_ref, 10879 operand_ty, 10880 src_node_offset, 10881 .{ .scalar = scalar_i }, 10882 )); 10883 } 10884 } 10885 { 10886 var multi_i: u32 = 0; 10887 while (multi_i < multi_cases_len) : (multi_i += 1) { 10888 const items_len = sema.code.extra[extra_index]; 10889 extra_index += 1; 10890 const ranges_len = sema.code.extra[extra_index]; 10891 extra_index += 1; 10892 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 10893 extra_index += 1; 10894 const items = sema.code.refSlice(extra_index, items_len); 10895 extra_index += items_len + info.body_len; 10896 10897 try case_vals.ensureUnusedCapacity(gpa, items.len); 10898 for (items, 0..) |item_ref, item_i| { 10899 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum( 10900 block, 10901 seen_enum_fields, 10902 &range_set, 10903 item_ref, 10904 operand_ty, 10905 src_node_offset, 10906 .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } }, 10907 )); 10908 } 10909 10910 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 10911 } 10912 } 10913 const all_tags_handled = for (seen_enum_fields) |seen_src| { 10914 if (seen_src == null) break false; 10915 } else true; 10916 10917 if (special_prong == .@"else") { 10918 if (all_tags_handled and !operand_ty.isNonexhaustiveEnum(mod)) return sema.fail( 10919 block, 10920 special_prong_src, 10921 "unreachable else prong; all cases already handled", 10922 .{}, 10923 ); 10924 } else if (!all_tags_handled) { 10925 const msg = msg: { 10926 const msg = try sema.errMsg( 10927 block, 10928 src, 10929 "switch must handle all possibilities", 10930 .{}, 10931 ); 10932 errdefer msg.destroy(sema.gpa); 10933 for (seen_enum_fields, 0..) |seen_src, i| { 10934 if (seen_src != null) continue; 10935 10936 const field_name = operand_ty.enumFieldName(i, mod); 10937 try sema.addFieldErrNote( 10938 operand_ty, 10939 i, 10940 msg, 10941 "unhandled enumeration value: '{}'", 10942 .{field_name.fmt(&mod.intern_pool)}, 10943 ); 10944 } 10945 try mod.errNoteNonLazy( 10946 operand_ty.declSrcLoc(mod), 10947 msg, 10948 "enum '{}' declared here", 10949 .{operand_ty.fmt(mod)}, 10950 ); 10951 break :msg msg; 10952 }; 10953 return sema.failWithOwnedErrorMsg(msg); 10954 } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum(mod) and !union_originally) { 10955 return sema.fail( 10956 block, 10957 src, 10958 "switch on non-exhaustive enum must include 'else' or '_' prong", 10959 .{}, 10960 ); 10961 } 10962 }, 10963 .ErrorSet => { 10964 var extra_index: usize = special.end; 10965 { 10966 var scalar_i: u32 = 0; 10967 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 10968 const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 10969 extra_index += 1; 10970 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 10971 extra_index += 1 + info.body_len; 10972 10973 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( 10974 block, 10975 &seen_errors, 10976 item_ref, 10977 operand_ty, 10978 src_node_offset, 10979 .{ .scalar = scalar_i }, 10980 )); 10981 } 10982 } 10983 { 10984 var multi_i: u32 = 0; 10985 while (multi_i < multi_cases_len) : (multi_i += 1) { 10986 const items_len = sema.code.extra[extra_index]; 10987 extra_index += 1; 10988 const ranges_len = sema.code.extra[extra_index]; 10989 extra_index += 1; 10990 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 10991 extra_index += 1; 10992 const items = sema.code.refSlice(extra_index, items_len); 10993 extra_index += items_len + info.body_len; 10994 10995 try case_vals.ensureUnusedCapacity(gpa, items.len); 10996 for (items, 0..) |item_ref, item_i| { 10997 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( 10998 block, 10999 &seen_errors, 11000 item_ref, 11001 operand_ty, 11002 src_node_offset, 11003 .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } }, 11004 )); 11005 } 11006 11007 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 11008 } 11009 } 11010 11011 try sema.resolveInferredErrorSetTy(block, src, operand_ty); 11012 11013 if (operand_ty.isAnyError(mod)) { 11014 if (special_prong != .@"else") { 11015 return sema.fail( 11016 block, 11017 src, 11018 "else prong required when switching on type 'anyerror'", 11019 .{}, 11020 ); 11021 } 11022 else_error_ty = Type.anyerror; 11023 } else else_validation: { 11024 var maybe_msg: ?*Module.ErrorMsg = null; 11025 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); 11026 11027 for (operand_ty.errorSetNames(mod)) |error_name| { 11028 if (!seen_errors.contains(error_name) and special_prong != .@"else") { 11029 const msg = maybe_msg orelse blk: { 11030 maybe_msg = try sema.errMsg( 11031 block, 11032 src, 11033 "switch must handle all possibilities", 11034 .{}, 11035 ); 11036 break :blk maybe_msg.?; 11037 }; 11038 11039 try sema.errNote( 11040 block, 11041 src, 11042 msg, 11043 "unhandled error value: 'error.{}'", 11044 .{error_name.fmt(ip)}, 11045 ); 11046 } 11047 } 11048 11049 if (maybe_msg) |msg| { 11050 maybe_msg = null; 11051 try sema.addDeclaredHereNote(msg, operand_ty); 11052 return sema.failWithOwnedErrorMsg(msg); 11053 } 11054 11055 if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames(mod).len) { 11056 // In order to enable common patterns for generic code allow simple else bodies 11057 // else => unreachable, 11058 // else => return, 11059 // else => |e| return e, 11060 // even if all the possible errors were already handled. 11061 const tags = sema.code.instructions.items(.tag); 11062 for (special.body) |else_inst| switch (tags[else_inst]) { 11063 .dbg_block_begin, 11064 .dbg_block_end, 11065 .dbg_stmt, 11066 .dbg_var_val, 11067 .ret_type, 11068 .as_node, 11069 .ret_node, 11070 .@"unreachable", 11071 .@"defer", 11072 .defer_err_code, 11073 .err_union_code, 11074 .ret_err_value_code, 11075 .restore_err_ret_index, 11076 .is_non_err, 11077 .ret_is_non_err, 11078 .condbr, 11079 => {}, 11080 else => break, 11081 } else break :else_validation; 11082 11083 return sema.fail( 11084 block, 11085 special_prong_src, 11086 "unreachable else prong; all cases already handled", 11087 .{}, 11088 ); 11089 } 11090 11091 const error_names = operand_ty.errorSetNames(mod); 11092 var names: Module.Fn.InferredErrorSet.NameMap = .{}; 11093 try names.ensureUnusedCapacity(sema.arena, error_names.len); 11094 for (error_names) |error_name| { 11095 if (seen_errors.contains(error_name)) continue; 11096 11097 names.putAssumeCapacityNoClobber(error_name, {}); 11098 } 11099 // No need to keep the hash map metadata correct; here we 11100 // extract the (sorted) keys only. 11101 else_error_ty = try mod.errorSetFromUnsortedNames(names.keys()); 11102 } 11103 }, 11104 .Int, .ComptimeInt => { 11105 var extra_index: usize = special.end; 11106 { 11107 var scalar_i: u32 = 0; 11108 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11109 const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 11110 extra_index += 1; 11111 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11112 extra_index += 1 + info.body_len; 11113 11114 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( 11115 block, 11116 &range_set, 11117 item_ref, 11118 operand_ty, 11119 src_node_offset, 11120 .{ .scalar = scalar_i }, 11121 )); 11122 } 11123 } 11124 { 11125 var multi_i: u32 = 0; 11126 while (multi_i < multi_cases_len) : (multi_i += 1) { 11127 const items_len = sema.code.extra[extra_index]; 11128 extra_index += 1; 11129 const ranges_len = sema.code.extra[extra_index]; 11130 extra_index += 1; 11131 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11132 extra_index += 1; 11133 const items = sema.code.refSlice(extra_index, items_len); 11134 extra_index += items_len; 11135 11136 try case_vals.ensureUnusedCapacity(gpa, items.len); 11137 for (items, 0..) |item_ref, item_i| { 11138 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt( 11139 block, 11140 &range_set, 11141 item_ref, 11142 operand_ty, 11143 src_node_offset, 11144 .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } }, 11145 )); 11146 } 11147 11148 try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len); 11149 var range_i: u32 = 0; 11150 while (range_i < ranges_len) : (range_i += 1) { 11151 const item_first = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 11152 extra_index += 1; 11153 const item_last = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 11154 extra_index += 1; 11155 11156 const vals = try sema.validateSwitchRange( 11157 block, 11158 &range_set, 11159 item_first, 11160 item_last, 11161 operand_ty, 11162 src_node_offset, 11163 .{ .range = .{ .prong = multi_i, .item = range_i } }, 11164 ); 11165 case_vals.appendAssumeCapacity(vals[0]); 11166 case_vals.appendAssumeCapacity(vals[1]); 11167 } 11168 11169 extra_index += info.body_len; 11170 } 11171 } 11172 11173 check_range: { 11174 if (operand_ty.zigTypeTag(mod) == .Int) { 11175 const min_int = try operand_ty.minInt(mod, operand_ty); 11176 const max_int = try operand_ty.maxInt(mod, operand_ty); 11177 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) { 11178 if (special_prong == .@"else") { 11179 return sema.fail( 11180 block, 11181 special_prong_src, 11182 "unreachable else prong; all cases already handled", 11183 .{}, 11184 ); 11185 } 11186 break :check_range; 11187 } 11188 } 11189 if (special_prong != .@"else") { 11190 return sema.fail( 11191 block, 11192 src, 11193 "switch must handle all possibilities", 11194 .{}, 11195 ); 11196 } 11197 } 11198 }, 11199 .Bool => { 11200 var extra_index: usize = special.end; 11201 { 11202 var scalar_i: u32 = 0; 11203 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11204 const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 11205 extra_index += 1; 11206 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11207 extra_index += 1 + info.body_len; 11208 11209 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( 11210 block, 11211 &true_count, 11212 &false_count, 11213 item_ref, 11214 src_node_offset, 11215 .{ .scalar = scalar_i }, 11216 )); 11217 } 11218 } 11219 { 11220 var multi_i: u32 = 0; 11221 while (multi_i < multi_cases_len) : (multi_i += 1) { 11222 const items_len = sema.code.extra[extra_index]; 11223 extra_index += 1; 11224 const ranges_len = sema.code.extra[extra_index]; 11225 extra_index += 1; 11226 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11227 extra_index += 1; 11228 const items = sema.code.refSlice(extra_index, items_len); 11229 extra_index += items_len + info.body_len; 11230 11231 try case_vals.ensureUnusedCapacity(gpa, items.len); 11232 for (items, 0..) |item_ref, item_i| { 11233 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool( 11234 block, 11235 &true_count, 11236 &false_count, 11237 item_ref, 11238 src_node_offset, 11239 .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } }, 11240 )); 11241 } 11242 11243 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 11244 } 11245 } 11246 switch (special_prong) { 11247 .@"else" => { 11248 if (true_count + false_count == 2) { 11249 return sema.fail( 11250 block, 11251 special_prong_src, 11252 "unreachable else prong; all cases already handled", 11253 .{}, 11254 ); 11255 } 11256 }, 11257 .under, .none => { 11258 if (true_count + false_count < 2) { 11259 return sema.fail( 11260 block, 11261 src, 11262 "switch must handle all possibilities", 11263 .{}, 11264 ); 11265 } 11266 }, 11267 } 11268 }, 11269 .EnumLiteral, .Void, .Fn, .Pointer, .Type => { 11270 if (special_prong != .@"else") { 11271 return sema.fail( 11272 block, 11273 src, 11274 "else prong required when switching on type '{}'", 11275 .{operand_ty.fmt(mod)}, 11276 ); 11277 } 11278 11279 var seen_values = ValueSrcMap{}; 11280 defer seen_values.deinit(gpa); 11281 11282 var extra_index: usize = special.end; 11283 { 11284 var scalar_i: u32 = 0; 11285 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11286 const item_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 11287 extra_index += 1; 11288 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11289 extra_index += 1; 11290 extra_index += info.body_len; 11291 11292 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( 11293 block, 11294 &seen_values, 11295 item_ref, 11296 operand_ty, 11297 src_node_offset, 11298 .{ .scalar = scalar_i }, 11299 )); 11300 } 11301 } 11302 { 11303 var multi_i: u32 = 0; 11304 while (multi_i < multi_cases_len) : (multi_i += 1) { 11305 const items_len = sema.code.extra[extra_index]; 11306 extra_index += 1; 11307 const ranges_len = sema.code.extra[extra_index]; 11308 extra_index += 1; 11309 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11310 extra_index += 1; 11311 const items = sema.code.refSlice(extra_index, items_len); 11312 extra_index += items_len + info.body_len; 11313 11314 try case_vals.ensureUnusedCapacity(gpa, items.len); 11315 for (items, 0..) |item_ref, item_i| { 11316 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse( 11317 block, 11318 &seen_values, 11319 item_ref, 11320 operand_ty, 11321 src_node_offset, 11322 .{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } }, 11323 )); 11324 } 11325 11326 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); 11327 } 11328 } 11329 }, 11330 11331 .ErrorUnion, 11332 .NoReturn, 11333 .Array, 11334 .Struct, 11335 .Undefined, 11336 .Null, 11337 .Optional, 11338 .Opaque, 11339 .Vector, 11340 .Frame, 11341 .AnyFrame, 11342 .ComptimeFloat, 11343 .Float, 11344 => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{ 11345 operand_ty.fmt(mod), 11346 }), 11347 } 11348 11349 const spa: SwitchProngAnalysis = .{ 11350 .sema = sema, 11351 .parent_block = block, 11352 .operand = raw_operand.val, 11353 .operand_ptr = raw_operand.ptr, 11354 .cond = operand, 11355 .else_error_ty = else_error_ty, 11356 .switch_block_inst = inst, 11357 .tag_capture_inst = tag_capture_inst, 11358 }; 11359 11360 const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 11361 try sema.air_instructions.append(gpa, .{ 11362 .tag = .block, 11363 .data = undefined, 11364 }); 11365 var label: Block.Label = .{ 11366 .zir_block = inst, 11367 .merges = .{ 11368 .src_locs = .{}, 11369 .results = .{}, 11370 .br_list = .{}, 11371 .block_inst = block_inst, 11372 }, 11373 }; 11374 11375 var child_block: Block = .{ 11376 .parent = block, 11377 .sema = sema, 11378 .src_decl = block.src_decl, 11379 .namespace = block.namespace, 11380 .wip_capture_scope = block.wip_capture_scope, 11381 .instructions = .{}, 11382 .label = &label, 11383 .inlining = block.inlining, 11384 .is_comptime = block.is_comptime, 11385 .comptime_reason = block.comptime_reason, 11386 .is_typeof = block.is_typeof, 11387 .c_import_buf = block.c_import_buf, 11388 .runtime_cond = block.runtime_cond, 11389 .runtime_loop = block.runtime_loop, 11390 .runtime_index = block.runtime_index, 11391 .error_return_trace_index = block.error_return_trace_index, 11392 }; 11393 const merges = &child_block.label.?.merges; 11394 defer child_block.instructions.deinit(gpa); 11395 defer merges.deinit(gpa); 11396 11397 if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| { 11398 const resolved_operand_val = try sema.resolveLazyValue(operand_val); 11399 var extra_index: usize = special.end; 11400 { 11401 var scalar_i: usize = 0; 11402 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11403 extra_index += 1; 11404 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11405 extra_index += 1; 11406 const body = sema.code.extra[extra_index..][0..info.body_len]; 11407 extra_index += info.body_len; 11408 11409 const item = case_vals.items[scalar_i]; 11410 const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable; 11411 if (operand_val.eql(item_val, operand_ty, sema.mod)) { 11412 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); 11413 return spa.resolveProngComptime( 11414 &child_block, 11415 .normal, 11416 body, 11417 info.capture, 11418 .{ .scalar_capture = @as(u32, @intCast(scalar_i)) }, 11419 &.{item}, 11420 if (info.is_inline) operand else .none, 11421 info.has_tag_capture, 11422 merges, 11423 ); 11424 } 11425 } 11426 } 11427 { 11428 var multi_i: usize = 0; 11429 var case_val_idx: usize = scalar_cases_len; 11430 while (multi_i < multi_cases_len) : (multi_i += 1) { 11431 const items_len = sema.code.extra[extra_index]; 11432 extra_index += 1; 11433 const ranges_len = sema.code.extra[extra_index]; 11434 extra_index += 1; 11435 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11436 extra_index += 1 + items_len; 11437 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..info.body_len]; 11438 11439 const items = case_vals.items[case_val_idx..][0..items_len]; 11440 case_val_idx += items_len; 11441 11442 for (items) |item| { 11443 // Validation above ensured these will succeed. 11444 const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable; 11445 if (operand_val.eql(item_val, operand_ty, sema.mod)) { 11446 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); 11447 return spa.resolveProngComptime( 11448 &child_block, 11449 .normal, 11450 body, 11451 info.capture, 11452 .{ .multi_capture = @as(u32, @intCast(multi_i)) }, 11453 items, 11454 if (info.is_inline) operand else .none, 11455 info.has_tag_capture, 11456 merges, 11457 ); 11458 } 11459 } 11460 11461 var range_i: usize = 0; 11462 while (range_i < ranges_len) : (range_i += 1) { 11463 const range_items = case_vals.items[case_val_idx..][0..2]; 11464 extra_index += 2; 11465 case_val_idx += 2; 11466 11467 // Validation above ensured these will succeed. 11468 const first_val = sema.resolveConstValue(&child_block, .unneeded, range_items[0], "") catch unreachable; 11469 const last_val = sema.resolveConstValue(&child_block, .unneeded, range_items[1], "") catch unreachable; 11470 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and 11471 (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty))) 11472 { 11473 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); 11474 return spa.resolveProngComptime( 11475 &child_block, 11476 .normal, 11477 body, 11478 info.capture, 11479 .{ .multi_capture = @as(u32, @intCast(multi_i)) }, 11480 undefined, // case_vals may be undefined for ranges 11481 if (info.is_inline) operand else .none, 11482 info.has_tag_capture, 11483 merges, 11484 ); 11485 } 11486 } 11487 11488 extra_index += info.body_len; 11489 } 11490 } 11491 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand); 11492 if (empty_enum) { 11493 return Air.Inst.Ref.void_value; 11494 } 11495 11496 return spa.resolveProngComptime( 11497 &child_block, 11498 .special, 11499 special.body, 11500 special.capture, 11501 .special_capture, 11502 undefined, // case_vals may be undefined for special prongs 11503 if (special.is_inline) operand else .none, 11504 special.has_tag_capture, 11505 merges, 11506 ); 11507 } 11508 11509 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) { 11510 if (empty_enum) { 11511 return Air.Inst.Ref.void_value; 11512 } 11513 if (special_prong == .none) { 11514 return sema.fail(block, src, "switch must handle all possibilities", .{}); 11515 } 11516 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) { 11517 return Air.Inst.Ref.unreachable_value; 11518 } 11519 if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and 11520 (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) 11521 { 11522 try sema.zirDbgStmt(block, cond_dbg_node_index); 11523 const ok = try block.addUnOp(.is_named_enum_value, operand); 11524 try sema.addSafetyCheck(block, ok, .corrupt_switch); 11525 } 11526 11527 return spa.resolveProngComptime( 11528 &child_block, 11529 .special, 11530 special.body, 11531 special.capture, 11532 .special_capture, 11533 undefined, // case_vals may be undefined for special prongs 11534 .none, 11535 false, 11536 merges, 11537 ); 11538 } 11539 11540 if (child_block.is_comptime) { 11541 _ = sema.resolveConstValue(&child_block, operand_src, operand, "condition in comptime switch must be comptime-known") catch |err| { 11542 if (err == error.AnalysisFail and child_block.comptime_reason != null) try child_block.comptime_reason.?.explain(sema, sema.err); 11543 return err; 11544 }; 11545 unreachable; 11546 } 11547 11548 const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * 11549 @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2; 11550 var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra); 11551 defer cases_extra.deinit(gpa); 11552 11553 var case_block = child_block.makeSubBlock(); 11554 case_block.runtime_loop = null; 11555 case_block.runtime_cond = operand_src; 11556 case_block.runtime_index.increment(); 11557 defer case_block.instructions.deinit(gpa); 11558 11559 var extra_index: usize = special.end; 11560 11561 var scalar_i: usize = 0; 11562 while (scalar_i < scalar_cases_len) : (scalar_i += 1) { 11563 extra_index += 1; 11564 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11565 extra_index += 1; 11566 const body = sema.code.extra[extra_index..][0..info.body_len]; 11567 extra_index += info.body_len; 11568 11569 var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope); 11570 defer wip_captures.deinit(); 11571 11572 case_block.instructions.shrinkRetainingCapacity(0); 11573 case_block.wip_capture_scope = wip_captures.scope; 11574 11575 const item = case_vals.items[scalar_i]; 11576 // `item` is already guaranteed to be constant known. 11577 11578 const analyze_body = if (union_originally) blk: { 11579 const item_val = sema.resolveConstLazyValue(block, .unneeded, item, "") catch unreachable; 11580 const field_ty = maybe_union_ty.unionFieldType(item_val, mod); 11581 break :blk field_ty.zigTypeTag(mod) != .NoReturn; 11582 } else true; 11583 11584 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) { 11585 // nothing to do here 11586 } else if (analyze_body) { 11587 try spa.analyzeProngRuntime( 11588 &case_block, 11589 .normal, 11590 body, 11591 info.capture, 11592 .{ .scalar_capture = @as(u32, @intCast(scalar_i)) }, 11593 &.{item}, 11594 if (info.is_inline) item else .none, 11595 info.has_tag_capture, 11596 ); 11597 } else { 11598 _ = try case_block.addNoOp(.unreach); 11599 } 11600 11601 try wip_captures.finalize(); 11602 11603 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 11604 cases_extra.appendAssumeCapacity(1); // items_len 11605 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 11606 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 11607 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 11608 } 11609 11610 var is_first = true; 11611 var prev_cond_br: Air.Inst.Index = undefined; 11612 var first_else_body: []const Air.Inst.Index = &.{}; 11613 defer gpa.free(first_else_body); 11614 var prev_then_body: []const Air.Inst.Index = &.{}; 11615 defer gpa.free(prev_then_body); 11616 11617 var cases_len = scalar_cases_len; 11618 var case_val_idx: usize = scalar_cases_len; 11619 var multi_i: u32 = 0; 11620 while (multi_i < multi_cases_len) : (multi_i += 1) { 11621 const items_len = sema.code.extra[extra_index]; 11622 extra_index += 1; 11623 const ranges_len = sema.code.extra[extra_index]; 11624 extra_index += 1; 11625 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(sema.code.extra[extra_index])); 11626 extra_index += 1 + items_len; 11627 11628 const items = case_vals.items[case_val_idx..][0..items_len]; 11629 case_val_idx += items_len; 11630 11631 case_block.instructions.shrinkRetainingCapacity(0); 11632 case_block.wip_capture_scope = child_block.wip_capture_scope; 11633 11634 // Generate all possible cases as scalar prongs. 11635 if (info.is_inline) { 11636 const body_start = extra_index + 2 * ranges_len; 11637 const body = sema.code.extra[body_start..][0..info.body_len]; 11638 var emit_bb = false; 11639 11640 var range_i: u32 = 0; 11641 while (range_i < ranges_len) : (range_i += 1) { 11642 const range_items = case_vals.items[case_val_idx..][0..2]; 11643 extra_index += 2; 11644 case_val_idx += 2; 11645 11646 const item_first_ref = range_items[0]; 11647 const item_last_ref = range_items[1]; 11648 11649 var item = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable; 11650 const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable; 11651 11652 while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({ 11653 // Previous validation has resolved any possible lazy values. 11654 item = sema.intAddScalar(item, try mod.intValue(operand_ty, 1), operand_ty) catch |err| switch (err) { 11655 error.Overflow => unreachable, 11656 else => |e| return e, 11657 }; 11658 }) { 11659 cases_len += 1; 11660 11661 const item_ref = try sema.addConstant(item); 11662 11663 case_block.instructions.shrinkRetainingCapacity(0); 11664 case_block.wip_capture_scope = child_block.wip_capture_scope; 11665 11666 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { 11667 error.NeededSourceLocation => { 11668 const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } }; 11669 const decl = mod.declPtr(case_block.src_decl); 11670 try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none)); 11671 unreachable; 11672 }, 11673 else => return err, 11674 }; 11675 emit_bb = true; 11676 11677 try spa.analyzeProngRuntime( 11678 &case_block, 11679 .normal, 11680 body, 11681 info.capture, 11682 .{ .multi_capture = multi_i }, 11683 undefined, // case_vals may be undefined for ranges 11684 item_ref, 11685 info.has_tag_capture, 11686 ); 11687 11688 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 11689 cases_extra.appendAssumeCapacity(1); // items_len 11690 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 11691 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 11692 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 11693 11694 if (item.compareScalar(.eq, item_last, operand_ty, mod)) break; 11695 } 11696 } 11697 11698 for (items, 0..) |item, item_i| { 11699 cases_len += 1; 11700 11701 case_block.instructions.shrinkRetainingCapacity(0); 11702 case_block.wip_capture_scope = child_block.wip_capture_scope; 11703 11704 const analyze_body = if (union_originally) blk: { 11705 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; 11706 const field_ty = maybe_union_ty.unionFieldType(item_val, mod); 11707 break :blk field_ty.zigTypeTag(mod) != .NoReturn; 11708 } else true; 11709 11710 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { 11711 error.NeededSourceLocation => { 11712 const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } }; 11713 const decl = mod.declPtr(case_block.src_decl); 11714 try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none)); 11715 unreachable; 11716 }, 11717 else => return err, 11718 }; 11719 emit_bb = true; 11720 11721 if (analyze_body) { 11722 try spa.analyzeProngRuntime( 11723 &case_block, 11724 .normal, 11725 body, 11726 info.capture, 11727 .{ .multi_capture = multi_i }, 11728 &.{item}, 11729 item, 11730 info.has_tag_capture, 11731 ); 11732 } else { 11733 _ = try case_block.addNoOp(.unreach); 11734 } 11735 11736 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 11737 cases_extra.appendAssumeCapacity(1); // items_len 11738 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 11739 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 11740 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 11741 } 11742 11743 extra_index += info.body_len; 11744 continue; 11745 } 11746 11747 var any_ok: Air.Inst.Ref = .none; 11748 11749 // If there are any ranges, we have to put all the items into the 11750 // else prong. Otherwise, we can take advantage of multiple items 11751 // mapping to the same body. 11752 if (ranges_len == 0) { 11753 cases_len += 1; 11754 11755 const analyze_body = if (union_originally) 11756 for (items) |item| { 11757 const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; 11758 const field_ty = maybe_union_ty.unionFieldType(item_val, mod); 11759 if (field_ty.zigTypeTag(mod) != .NoReturn) break true; 11760 } else false 11761 else 11762 true; 11763 11764 const body = sema.code.extra[extra_index..][0..info.body_len]; 11765 extra_index += info.body_len; 11766 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) { 11767 // nothing to do here 11768 } else if (analyze_body) { 11769 try spa.analyzeProngRuntime( 11770 &case_block, 11771 .normal, 11772 body, 11773 info.capture, 11774 .{ .multi_capture = multi_i }, 11775 items, 11776 .none, 11777 false, 11778 ); 11779 } else { 11780 _ = try case_block.addNoOp(.unreach); 11781 } 11782 11783 try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len + 11784 case_block.instructions.items.len); 11785 11786 cases_extra.appendAssumeCapacity(@as(u32, @intCast(items.len))); 11787 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 11788 11789 for (items) |item| { 11790 cases_extra.appendAssumeCapacity(@intFromEnum(item)); 11791 } 11792 11793 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 11794 } else { 11795 for (items) |item| { 11796 const cmp_ok = try case_block.addBinOp(if (case_block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, item); 11797 if (any_ok != .none) { 11798 any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok); 11799 } else { 11800 any_ok = cmp_ok; 11801 } 11802 } 11803 11804 var range_i: usize = 0; 11805 while (range_i < ranges_len) : (range_i += 1) { 11806 const range_items = case_vals.items[case_val_idx..][0..2]; 11807 extra_index += 2; 11808 case_val_idx += 2; 11809 11810 const item_first = range_items[0]; 11811 const item_last = range_items[1]; 11812 11813 // operand >= first and operand <= last 11814 const range_first_ok = try case_block.addBinOp( 11815 if (case_block.float_mode == .Optimized) .cmp_gte_optimized else .cmp_gte, 11816 operand, 11817 item_first, 11818 ); 11819 const range_last_ok = try case_block.addBinOp( 11820 if (case_block.float_mode == .Optimized) .cmp_lte_optimized else .cmp_lte, 11821 operand, 11822 item_last, 11823 ); 11824 const range_ok = try case_block.addBinOp( 11825 .bool_and, 11826 range_first_ok, 11827 range_last_ok, 11828 ); 11829 if (any_ok != .none) { 11830 any_ok = try case_block.addBinOp(.bool_or, any_ok, range_ok); 11831 } else { 11832 any_ok = range_ok; 11833 } 11834 } 11835 11836 const new_cond_br = try case_block.addInstAsIndex(.{ .tag = .cond_br, .data = .{ 11837 .pl_op = .{ 11838 .operand = any_ok, 11839 .payload = undefined, 11840 }, 11841 } }); 11842 var cond_body = try case_block.instructions.toOwnedSlice(gpa); 11843 defer gpa.free(cond_body); 11844 11845 var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope); 11846 defer wip_captures.deinit(); 11847 11848 case_block.instructions.shrinkRetainingCapacity(0); 11849 case_block.wip_capture_scope = wip_captures.scope; 11850 11851 const body = sema.code.extra[extra_index..][0..info.body_len]; 11852 extra_index += info.body_len; 11853 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) { 11854 // nothing to do here 11855 } else { 11856 try spa.analyzeProngRuntime( 11857 &case_block, 11858 .normal, 11859 body, 11860 info.capture, 11861 .{ .multi_capture = multi_i }, 11862 items, 11863 .none, 11864 false, 11865 ); 11866 } 11867 11868 try wip_captures.finalize(); 11869 11870 if (is_first) { 11871 is_first = false; 11872 first_else_body = cond_body; 11873 cond_body = &.{}; 11874 } else { 11875 try sema.air_extra.ensureUnusedCapacity( 11876 gpa, 11877 @typeInfo(Air.CondBr).Struct.fields.len + prev_then_body.len + cond_body.len, 11878 ); 11879 11880 sema.air_instructions.items(.data)[prev_cond_br].pl_op.payload = 11881 sema.addExtraAssumeCapacity(Air.CondBr{ 11882 .then_body_len = @as(u32, @intCast(prev_then_body.len)), 11883 .else_body_len = @as(u32, @intCast(cond_body.len)), 11884 }); 11885 sema.air_extra.appendSliceAssumeCapacity(prev_then_body); 11886 sema.air_extra.appendSliceAssumeCapacity(cond_body); 11887 } 11888 gpa.free(prev_then_body); 11889 prev_then_body = try case_block.instructions.toOwnedSlice(gpa); 11890 prev_cond_br = new_cond_br; 11891 } 11892 } 11893 11894 var final_else_body: []const Air.Inst.Index = &.{}; 11895 if (special.body.len != 0 or !is_first or case_block.wantSafety()) { 11896 var emit_bb = false; 11897 if (special.is_inline) switch (operand_ty.zigTypeTag(mod)) { 11898 .Enum => { 11899 if (operand_ty.isNonexhaustiveEnum(mod) and !union_originally) { 11900 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ 11901 operand_ty.fmt(mod), 11902 }); 11903 } 11904 for (seen_enum_fields, 0..) |f, i| { 11905 if (f != null) continue; 11906 cases_len += 1; 11907 11908 const item_val = try mod.enumValueFieldIndex(operand_ty, @as(u32, @intCast(i))); 11909 const item_ref = try sema.addConstant(item_val); 11910 11911 case_block.instructions.shrinkRetainingCapacity(0); 11912 case_block.wip_capture_scope = child_block.wip_capture_scope; 11913 11914 const analyze_body = if (union_originally) blk: { 11915 const field_ty = maybe_union_ty.unionFieldType(item_val, mod); 11916 break :blk field_ty.zigTypeTag(mod) != .NoReturn; 11917 } else true; 11918 11919 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 11920 emit_bb = true; 11921 11922 if (analyze_body) { 11923 try spa.analyzeProngRuntime( 11924 &case_block, 11925 .special, 11926 special.body, 11927 special.capture, 11928 .special_capture, 11929 &.{item_ref}, 11930 item_ref, 11931 special.has_tag_capture, 11932 ); 11933 } else { 11934 _ = try case_block.addNoOp(.unreach); 11935 } 11936 11937 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 11938 cases_extra.appendAssumeCapacity(1); // items_len 11939 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 11940 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 11941 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 11942 } 11943 }, 11944 .ErrorSet => { 11945 if (operand_ty.isAnyError(mod)) { 11946 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ 11947 operand_ty.fmt(mod), 11948 }); 11949 } 11950 for (0..operand_ty.errorSetNames(mod).len) |i| { 11951 const error_name = operand_ty.errorSetNames(mod)[i]; 11952 if (seen_errors.contains(error_name)) continue; 11953 cases_len += 1; 11954 11955 const item_val = try mod.intern(.{ .err = .{ 11956 .ty = operand_ty.toIntern(), 11957 .name = error_name, 11958 } }); 11959 const item_ref = try sema.addConstant(item_val.toValue()); 11960 11961 case_block.instructions.shrinkRetainingCapacity(0); 11962 case_block.wip_capture_scope = child_block.wip_capture_scope; 11963 11964 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 11965 emit_bb = true; 11966 11967 try spa.analyzeProngRuntime( 11968 &case_block, 11969 .special, 11970 special.body, 11971 special.capture, 11972 .special_capture, 11973 &.{item_ref}, 11974 item_ref, 11975 special.has_tag_capture, 11976 ); 11977 11978 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 11979 cases_extra.appendAssumeCapacity(1); // items_len 11980 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 11981 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 11982 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 11983 } 11984 }, 11985 .Int => { 11986 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set); 11987 while (try it.next()) |cur| { 11988 cases_len += 1; 11989 11990 const item_ref = try sema.addConstant(cur.toValue()); 11991 11992 case_block.instructions.shrinkRetainingCapacity(0); 11993 case_block.wip_capture_scope = child_block.wip_capture_scope; 11994 11995 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 11996 emit_bb = true; 11997 11998 try spa.analyzeProngRuntime( 11999 &case_block, 12000 .special, 12001 special.body, 12002 special.capture, 12003 .special_capture, 12004 &.{item_ref}, 12005 item_ref, 12006 special.has_tag_capture, 12007 ); 12008 12009 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12010 cases_extra.appendAssumeCapacity(1); // items_len 12011 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 12012 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref)); 12013 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 12014 } 12015 }, 12016 .Bool => { 12017 if (true_count == 0) { 12018 cases_len += 1; 12019 12020 case_block.instructions.shrinkRetainingCapacity(0); 12021 case_block.wip_capture_scope = child_block.wip_capture_scope; 12022 12023 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12024 emit_bb = true; 12025 12026 try spa.analyzeProngRuntime( 12027 &case_block, 12028 .special, 12029 special.body, 12030 special.capture, 12031 .special_capture, 12032 &.{Air.Inst.Ref.bool_true}, 12033 Air.Inst.Ref.bool_true, 12034 special.has_tag_capture, 12035 ); 12036 12037 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12038 cases_extra.appendAssumeCapacity(1); // items_len 12039 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 12040 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true)); 12041 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 12042 } 12043 if (false_count == 0) { 12044 cases_len += 1; 12045 12046 case_block.instructions.shrinkRetainingCapacity(0); 12047 case_block.wip_capture_scope = child_block.wip_capture_scope; 12048 12049 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); 12050 emit_bb = true; 12051 12052 try spa.analyzeProngRuntime( 12053 &case_block, 12054 .special, 12055 special.body, 12056 special.capture, 12057 .special_capture, 12058 &.{Air.Inst.Ref.bool_false}, 12059 Air.Inst.Ref.bool_false, 12060 special.has_tag_capture, 12061 ); 12062 12063 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); 12064 cases_extra.appendAssumeCapacity(1); // items_len 12065 cases_extra.appendAssumeCapacity(@as(u32, @intCast(case_block.instructions.items.len))); 12066 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false)); 12067 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); 12068 } 12069 }, 12070 else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ 12071 operand_ty.fmt(mod), 12072 }), 12073 }; 12074 12075 var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope); 12076 defer wip_captures.deinit(); 12077 12078 case_block.instructions.shrinkRetainingCapacity(0); 12079 case_block.wip_capture_scope = wip_captures.scope; 12080 12081 if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and 12082 operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) 12083 { 12084 try sema.zirDbgStmt(&case_block, cond_dbg_node_index); 12085 const ok = try case_block.addUnOp(.is_named_enum_value, operand); 12086 try sema.addSafetyCheck(&case_block, ok, .corrupt_switch); 12087 } 12088 12089 const analyze_body = if (union_originally and !special.is_inline) 12090 for (seen_enum_fields, 0..) |seen_field, index| { 12091 if (seen_field != null) continue; 12092 const union_obj = mod.typeToUnion(maybe_union_ty).?; 12093 const field_ty = union_obj.fields.values()[index].ty; 12094 if (field_ty.zigTypeTag(mod) != .NoReturn) break true; 12095 } else false 12096 else 12097 true; 12098 if (special.body.len != 0 and err_set and 12099 try sema.maybeErrorUnwrap(&case_block, special.body, operand)) 12100 { 12101 // nothing to do here 12102 } else if (special.body.len != 0 and analyze_body and !special.is_inline) { 12103 try spa.analyzeProngRuntime( 12104 &case_block, 12105 .special, 12106 special.body, 12107 special.capture, 12108 .special_capture, 12109 undefined, // case_vals may be undefined for special prongs 12110 .none, 12111 false, 12112 ); 12113 } else { 12114 // We still need a terminator in this block, but we have proven 12115 // that it is unreachable. 12116 if (case_block.wantSafety()) { 12117 try sema.zirDbgStmt(&case_block, cond_dbg_node_index); 12118 try sema.safetyPanic(&case_block, .corrupt_switch); 12119 } else { 12120 _ = try case_block.addNoOp(.unreach); 12121 } 12122 } 12123 12124 try wip_captures.finalize(); 12125 12126 if (is_first) { 12127 final_else_body = case_block.instructions.items; 12128 } else { 12129 try sema.air_extra.ensureUnusedCapacity(gpa, prev_then_body.len + 12130 @typeInfo(Air.CondBr).Struct.fields.len + case_block.instructions.items.len); 12131 12132 sema.air_instructions.items(.data)[prev_cond_br].pl_op.payload = 12133 sema.addExtraAssumeCapacity(Air.CondBr{ 12134 .then_body_len = @as(u32, @intCast(prev_then_body.len)), 12135 .else_body_len = @as(u32, @intCast(case_block.instructions.items.len)), 12136 }); 12137 sema.air_extra.appendSliceAssumeCapacity(prev_then_body); 12138 sema.air_extra.appendSliceAssumeCapacity(case_block.instructions.items); 12139 final_else_body = first_else_body; 12140 } 12141 } 12142 12143 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + 12144 cases_extra.items.len + final_else_body.len); 12145 12146 _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{ 12147 .operand = operand, 12148 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ 12149 .cases_len = @as(u32, @intCast(cases_len)), 12150 .else_body_len = @as(u32, @intCast(final_else_body.len)), 12151 }), 12152 } } }); 12153 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); 12154 sema.air_extra.appendSliceAssumeCapacity(final_else_body); 12155 12156 return sema.analyzeBlockBody(block, src, &child_block, merges); 12157 } 12158 12159 const RangeSetUnhandledIterator = struct { 12160 mod: *Module, 12161 cur: ?InternPool.Index, 12162 max: InternPool.Index, 12163 range_i: usize, 12164 ranges: []const RangeSet.Range, 12165 limbs: []math.big.Limb, 12166 12167 const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128); 12168 12169 fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator { 12170 const mod = sema.mod; 12171 const int_type = mod.intern_pool.indexToKey(ty.toIntern()).int_type; 12172 const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits); 12173 return .{ 12174 .mod = mod, 12175 .cur = (try ty.minInt(mod, ty)).toIntern(), 12176 .max = (try ty.maxInt(mod, ty)).toIntern(), 12177 .range_i = 0, 12178 .ranges = range_set.ranges.items, 12179 .limbs = if (needed_limbs > preallocated_limbs) 12180 try sema.arena.alloc(math.big.Limb, needed_limbs) 12181 else 12182 &.{}, 12183 }; 12184 } 12185 12186 fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index { 12187 if (val == it.max) return null; 12188 const int = it.mod.intern_pool.indexToKey(val).int; 12189 12190 switch (int.storage) { 12191 inline .u64, .i64 => |val_int| { 12192 const next_int = @addWithOverflow(val_int, 1); 12193 if (next_int[1] == 0) 12194 return (try it.mod.intValue(int.ty.toType(), next_int[0])).toIntern(); 12195 }, 12196 .big_int => {}, 12197 .lazy_align, .lazy_size => unreachable, 12198 } 12199 12200 var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined; 12201 const val_bigint = int.storage.toBigInt(&val_space); 12202 12203 var result_limbs: [preallocated_limbs]math.big.Limb = undefined; 12204 var result_bigint = math.big.int.Mutable.init( 12205 if (it.limbs.len > 0) it.limbs else &result_limbs, 12206 0, 12207 ); 12208 12209 result_bigint.addScalar(val_bigint, 1); 12210 return (try it.mod.intValue_big(int.ty.toType(), result_bigint.toConst())).toIntern(); 12211 } 12212 12213 fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index { 12214 var cur = it.cur orelse return null; 12215 while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) { 12216 defer it.range_i += 1; 12217 cur = (try it.addOne(it.ranges[it.range_i].last)) orelse { 12218 it.cur = null; 12219 return null; 12220 }; 12221 } 12222 it.cur = try it.addOne(cur); 12223 return cur; 12224 } 12225 }; 12226 12227 const ResolvedSwitchItem = struct { 12228 ref: Air.Inst.Ref, 12229 val: InternPool.Index, 12230 }; 12231 fn resolveSwitchItemVal( 12232 sema: *Sema, 12233 block: *Block, 12234 item_ref: Zir.Inst.Ref, 12235 /// Coerce `item_ref` to this type. 12236 coerce_ty: Type, 12237 switch_node_offset: i32, 12238 switch_prong_src: Module.SwitchProngSrc, 12239 range_expand: Module.SwitchProngSrc.RangeExpand, 12240 ) CompileError!ResolvedSwitchItem { 12241 const mod = sema.mod; 12242 const uncoerced_item = try sema.resolveInst(item_ref); 12243 12244 // Constructing a LazySrcLoc is costly because we only have the switch AST node. 12245 // Only if we know for sure we need to report a compile error do we resolve the 12246 // full source locations. 12247 12248 const item = sema.coerce(block, coerce_ty, uncoerced_item, .unneeded) catch |err| switch (err) { 12249 error.NeededSourceLocation => { 12250 const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand); 12251 _ = try sema.coerce(block, coerce_ty, uncoerced_item, src); 12252 unreachable; 12253 }, 12254 else => |e| return e, 12255 }; 12256 12257 const maybe_lazy = sema.resolveConstValue(block, .unneeded, item, "") catch |err| switch (err) { 12258 error.NeededSourceLocation => { 12259 const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand); 12260 _ = try sema.resolveConstValue(block, src, item, "switch prong values must be comptime-known"); 12261 unreachable; 12262 }, 12263 else => |e| return e, 12264 }; 12265 12266 const val = try sema.resolveLazyValue(maybe_lazy); 12267 const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: { 12268 break :blk try sema.addConstant(val); 12269 } else item; 12270 12271 return .{ .ref = new_item, .val = val.toIntern() }; 12272 } 12273 12274 fn validateSwitchRange( 12275 sema: *Sema, 12276 block: *Block, 12277 range_set: *RangeSet, 12278 first_ref: Zir.Inst.Ref, 12279 last_ref: Zir.Inst.Ref, 12280 operand_ty: Type, 12281 src_node_offset: i32, 12282 switch_prong_src: Module.SwitchProngSrc, 12283 ) CompileError![2]Air.Inst.Ref { 12284 const mod = sema.mod; 12285 const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, src_node_offset, switch_prong_src, .first); 12286 const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, src_node_offset, switch_prong_src, .last); 12287 if (try first.val.toValue().compareAll(.gt, last.val.toValue(), operand_ty, mod)) { 12288 const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), src_node_offset, .first); 12289 return sema.fail(block, src, "range start value is greater than the end value", .{}); 12290 } 12291 const maybe_prev_src = try range_set.add(first.val, last.val, switch_prong_src); 12292 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12293 return .{ first.ref, last.ref }; 12294 } 12295 12296 fn validateSwitchItemInt( 12297 sema: *Sema, 12298 block: *Block, 12299 range_set: *RangeSet, 12300 item_ref: Zir.Inst.Ref, 12301 operand_ty: Type, 12302 src_node_offset: i32, 12303 switch_prong_src: Module.SwitchProngSrc, 12304 ) CompileError!Air.Inst.Ref { 12305 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); 12306 const maybe_prev_src = try range_set.add(item.val, item.val, switch_prong_src); 12307 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12308 return item.ref; 12309 } 12310 12311 fn validateSwitchItemEnum( 12312 sema: *Sema, 12313 block: *Block, 12314 seen_fields: []?Module.SwitchProngSrc, 12315 range_set: *RangeSet, 12316 item_ref: Zir.Inst.Ref, 12317 operand_ty: Type, 12318 src_node_offset: i32, 12319 switch_prong_src: Module.SwitchProngSrc, 12320 ) CompileError!Air.Inst.Ref { 12321 const ip = &sema.mod.intern_pool; 12322 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); 12323 const int = ip.indexToKey(item.val).enum_tag.int; 12324 const field_index = ip.indexToKey(ip.typeOf(item.val)).enum_type.tagValueIndex(ip, int) orelse { 12325 const maybe_prev_src = try range_set.add(int, int, switch_prong_src); 12326 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12327 return item.ref; 12328 }; 12329 const maybe_prev_src = seen_fields[field_index]; 12330 seen_fields[field_index] = switch_prong_src; 12331 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12332 return item.ref; 12333 } 12334 12335 fn validateSwitchItemError( 12336 sema: *Sema, 12337 block: *Block, 12338 seen_errors: *SwitchErrorSet, 12339 item_ref: Zir.Inst.Ref, 12340 operand_ty: Type, 12341 src_node_offset: i32, 12342 switch_prong_src: Module.SwitchProngSrc, 12343 ) CompileError!Air.Inst.Ref { 12344 const ip = &sema.mod.intern_pool; 12345 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); 12346 const error_name = ip.indexToKey(item.val).err.name; 12347 const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev| 12348 prev.value 12349 else 12350 null; 12351 try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); 12352 return item.ref; 12353 } 12354 12355 fn validateSwitchDupe( 12356 sema: *Sema, 12357 block: *Block, 12358 maybe_prev_src: ?Module.SwitchProngSrc, 12359 switch_prong_src: Module.SwitchProngSrc, 12360 src_node_offset: i32, 12361 ) CompileError!void { 12362 const prev_prong_src = maybe_prev_src orelse return; 12363 const mod = sema.mod; 12364 const block_src_decl = mod.declPtr(block.src_decl); 12365 const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); 12366 const prev_src = prev_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); 12367 const msg = msg: { 12368 const msg = try sema.errMsg( 12369 block, 12370 src, 12371 "duplicate switch value", 12372 .{}, 12373 ); 12374 errdefer msg.destroy(sema.gpa); 12375 try sema.errNote( 12376 block, 12377 prev_src, 12378 msg, 12379 "previous value here", 12380 .{}, 12381 ); 12382 break :msg msg; 12383 }; 12384 return sema.failWithOwnedErrorMsg(msg); 12385 } 12386 12387 fn validateSwitchItemBool( 12388 sema: *Sema, 12389 block: *Block, 12390 true_count: *u8, 12391 false_count: *u8, 12392 item_ref: Zir.Inst.Ref, 12393 src_node_offset: i32, 12394 switch_prong_src: Module.SwitchProngSrc, 12395 ) CompileError!Air.Inst.Ref { 12396 const mod = sema.mod; 12397 const item = try sema.resolveSwitchItemVal(block, item_ref, Type.bool, src_node_offset, switch_prong_src, .none); 12398 if (item.val.toValue().toBool()) { 12399 true_count.* += 1; 12400 } else { 12401 false_count.* += 1; 12402 } 12403 if (true_count.* > 1 or false_count.* > 1) { 12404 const block_src_decl = sema.mod.declPtr(block.src_decl); 12405 const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none); 12406 return sema.fail(block, src, "duplicate switch value", .{}); 12407 } 12408 return item.ref; 12409 } 12410 12411 const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, Module.SwitchProngSrc); 12412 12413 fn validateSwitchItemSparse( 12414 sema: *Sema, 12415 block: *Block, 12416 seen_values: *ValueSrcMap, 12417 item_ref: Zir.Inst.Ref, 12418 operand_ty: Type, 12419 src_node_offset: i32, 12420 switch_prong_src: Module.SwitchProngSrc, 12421 ) CompileError!Air.Inst.Ref { 12422 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); 12423 const kv = (try seen_values.fetchPut(sema.gpa, item.val, switch_prong_src)) orelse return item.ref; 12424 try sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset); 12425 unreachable; 12426 } 12427 12428 fn validateSwitchNoRange( 12429 sema: *Sema, 12430 block: *Block, 12431 ranges_len: u32, 12432 operand_ty: Type, 12433 src_node_offset: i32, 12434 ) CompileError!void { 12435 if (ranges_len == 0) 12436 return; 12437 12438 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; 12439 const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset }; 12440 12441 const msg = msg: { 12442 const msg = try sema.errMsg( 12443 block, 12444 operand_src, 12445 "ranges not allowed when switching on type '{}'", 12446 .{operand_ty.fmt(sema.mod)}, 12447 ); 12448 errdefer msg.destroy(sema.gpa); 12449 try sema.errNote( 12450 block, 12451 range_src, 12452 msg, 12453 "range here", 12454 .{}, 12455 ); 12456 break :msg msg; 12457 }; 12458 return sema.failWithOwnedErrorMsg(msg); 12459 } 12460 12461 fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !bool { 12462 const mod = sema.mod; 12463 if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false; 12464 12465 const tags = sema.code.instructions.items(.tag); 12466 for (body) |inst| { 12467 switch (tags[inst]) { 12468 .@"unreachable" => if (!block.wantSafety()) return false, 12469 .save_err_ret_index, 12470 .dbg_block_begin, 12471 .dbg_block_end, 12472 .dbg_stmt, 12473 .str, 12474 .as_node, 12475 .panic, 12476 .field_val, 12477 => {}, 12478 else => return false, 12479 } 12480 } 12481 12482 for (body) |inst| { 12483 const air_inst = switch (tags[inst]) { 12484 .dbg_block_begin, 12485 .dbg_block_end, 12486 => continue, 12487 .dbg_stmt => { 12488 try sema.zirDbgStmt(block, inst); 12489 continue; 12490 }, 12491 .save_err_ret_index => { 12492 try sema.zirSaveErrRetIndex(block, inst); 12493 continue; 12494 }, 12495 .str => try sema.zirStr(block, inst), 12496 .as_node => try sema.zirAsNode(block, inst), 12497 .field_val => try sema.zirFieldVal(block, inst), 12498 .@"unreachable" => { 12499 if (!mod.comp.formatted_panics) { 12500 try sema.safetyPanic(block, .unwrap_error); 12501 return true; 12502 } 12503 12504 const panic_fn = try sema.getBuiltin("panicUnwrapError"); 12505 const err_return_trace = try sema.getErrorReturnTrace(block); 12506 const args: [2]Air.Inst.Ref = .{ err_return_trace, operand }; 12507 try sema.callBuiltin(block, panic_fn, .auto, &args); 12508 return true; 12509 }, 12510 .panic => { 12511 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 12512 const msg_inst = try sema.resolveInst(inst_data.operand); 12513 12514 const panic_fn = try sema.getBuiltin("panic"); 12515 const err_return_trace = try sema.getErrorReturnTrace(block); 12516 const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value }; 12517 try sema.callBuiltin(block, panic_fn, .auto, &args); 12518 return true; 12519 }, 12520 else => unreachable, 12521 }; 12522 if (sema.typeOf(air_inst).isNoReturn(mod)) 12523 return true; 12524 sema.inst_map.putAssumeCapacity(inst, air_inst); 12525 } 12526 unreachable; 12527 } 12528 12529 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void { 12530 const mod = sema.mod; 12531 const index = Zir.refToIndex(cond) orelse return; 12532 if (sema.code.instructions.items(.tag)[index] != .is_non_err) return; 12533 12534 const err_inst_data = sema.code.instructions.items(.data)[index].un_node; 12535 const err_operand = try sema.resolveInst(err_inst_data.operand); 12536 const operand_ty = sema.typeOf(err_operand); 12537 if (operand_ty.zigTypeTag(mod) == .ErrorSet) { 12538 try sema.maybeErrorUnwrapComptime(block, body, err_operand); 12539 return; 12540 } 12541 if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| { 12542 if (!operand_ty.isError(mod)) return; 12543 if (val.getErrorName(mod) == .none) return; 12544 try sema.maybeErrorUnwrapComptime(block, body, err_operand); 12545 } 12546 } 12547 12548 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void { 12549 const tags = sema.code.instructions.items(.tag); 12550 const inst = for (body) |inst| { 12551 switch (tags[inst]) { 12552 .dbg_block_begin, 12553 .dbg_block_end, 12554 .dbg_stmt, 12555 .save_err_ret_index, 12556 => {}, 12557 .@"unreachable" => break inst, 12558 else => return, 12559 } 12560 } else return; 12561 const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable"; 12562 const src = inst_data.src(); 12563 12564 if (try sema.resolveDefinedValue(block, src, operand)) |val| { 12565 if (val.getErrorName(sema.mod).unwrap()) |name| { 12566 return sema.fail(block, src, "caught unexpected error '{}'", .{name.fmt(&sema.mod.intern_pool)}); 12567 } 12568 } 12569 } 12570 12571 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 12572 const mod = sema.mod; 12573 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 12574 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 12575 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 12576 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 12577 const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs); 12578 const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, "field name must be comptime-known"); 12579 const ty = try sema.resolveTypeFields(unresolved_ty); 12580 const ip = &mod.intern_pool; 12581 12582 const has_field = hf: { 12583 switch (ip.indexToKey(ty.toIntern())) { 12584 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 12585 .Slice => { 12586 if (ip.stringEqlSlice(field_name, "ptr")) break :hf true; 12587 if (ip.stringEqlSlice(field_name, "len")) break :hf true; 12588 break :hf false; 12589 }, 12590 else => {}, 12591 }, 12592 .anon_struct_type => |anon_struct| { 12593 if (anon_struct.names.len != 0) { 12594 break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names, field_name) != null; 12595 } else { 12596 const field_index = field_name.toUnsigned(ip) orelse break :hf false; 12597 break :hf field_index < ty.structFieldCount(mod); 12598 } 12599 }, 12600 .struct_type => |struct_type| { 12601 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :hf false; 12602 assert(struct_obj.haveFieldTypes()); 12603 break :hf struct_obj.fields.contains(field_name); 12604 }, 12605 .union_type => |union_type| { 12606 const union_obj = mod.unionPtr(union_type.index); 12607 assert(union_obj.haveFieldTypes()); 12608 break :hf union_obj.fields.contains(field_name); 12609 }, 12610 .enum_type => |enum_type| { 12611 break :hf enum_type.nameIndex(ip, field_name) != null; 12612 }, 12613 .array_type => break :hf ip.stringEqlSlice(field_name, "len"), 12614 else => {}, 12615 } 12616 return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ 12617 ty.fmt(mod), 12618 }); 12619 }; 12620 if (has_field) { 12621 return Air.Inst.Ref.bool_true; 12622 } else { 12623 return Air.Inst.Ref.bool_false; 12624 } 12625 } 12626 12627 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 12628 const mod = sema.mod; 12629 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 12630 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 12631 const src = inst_data.src(); 12632 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 12633 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 12634 const container_type = try sema.resolveType(block, lhs_src, extra.lhs); 12635 const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, "decl name must be comptime-known"); 12636 12637 try sema.checkNamespaceType(block, lhs_src, container_type); 12638 12639 const namespace = container_type.getNamespaceIndex(mod).unwrap() orelse 12640 return Air.Inst.Ref.bool_false; 12641 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { 12642 const decl = mod.declPtr(decl_index); 12643 if (decl.is_pub or decl.getFileScope(mod) == block.getFileScope(mod)) { 12644 return Air.Inst.Ref.bool_true; 12645 } 12646 } 12647 return Air.Inst.Ref.bool_false; 12648 } 12649 12650 fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 12651 const tracy = trace(@src()); 12652 defer tracy.end(); 12653 12654 const mod = sema.mod; 12655 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 12656 const operand_src = inst_data.src(); 12657 const operand = inst_data.get(sema.code); 12658 12659 const result = mod.importFile(block.getFileScope(mod), operand) catch |err| switch (err) { 12660 error.ImportOutsidePkgPath => { 12661 return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand}); 12662 }, 12663 error.PackageNotFound => { 12664 const name = try block.getFileScope(mod).pkg.getName(sema.gpa, mod.*); 12665 defer sema.gpa.free(name); 12666 return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, name }); 12667 }, 12668 else => { 12669 // TODO: these errors are file system errors; make sure an update() will 12670 // retry this and not cache the file system error, which may be transient. 12671 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); 12672 }, 12673 }; 12674 try mod.semaFile(result.file); 12675 const file_root_decl_index = result.file.root_decl.unwrap().?; 12676 const file_root_decl = mod.declPtr(file_root_decl_index); 12677 try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index); 12678 return sema.addConstant(file_root_decl.val); 12679 } 12680 12681 fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 12682 const tracy = trace(@src()); 12683 defer tracy.end(); 12684 12685 const mod = sema.mod; 12686 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 12687 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 12688 const name = try sema.resolveConstString(block, operand_src, inst_data.operand, "file path name must be comptime-known"); 12689 12690 const embed_file = mod.embedFile(block.getFileScope(mod), name) catch |err| switch (err) { 12691 error.ImportOutsidePkgPath => { 12692 return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name}); 12693 }, 12694 else => { 12695 // TODO: these errors are file system errors; make sure an update() will 12696 // retry this and not cache the file system error, which may be transient. 12697 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(err) }); 12698 }, 12699 }; 12700 12701 var anon_decl = try block.startAnonDecl(); 12702 defer anon_decl.deinit(); 12703 12704 // TODO instead of using `.bytes`, create a new value tag for pointing at 12705 // a `*Module.EmbedFile`. The purpose of this would be: 12706 // - If only the length is read and the bytes are not inspected by comptime code, 12707 // there can be an optimization where the codegen backend does a copy_file_range 12708 // into the final binary, and never loads the data into memory. 12709 // - When a Decl is destroyed, it can free the `*Module.EmbedFile`. 12710 const ty = try mod.arrayType(.{ 12711 .len = embed_file.bytes.len, 12712 .sentinel = .zero_u8, 12713 .child = .u8_type, 12714 }); 12715 embed_file.owner_decl = try anon_decl.finish( 12716 ty, 12717 (try mod.intern(.{ .aggregate = .{ 12718 .ty = ty.toIntern(), 12719 .storage = .{ .bytes = embed_file.bytes }, 12720 } })).toValue(), 12721 .none, // default alignment 12722 ); 12723 12724 return sema.analyzeDeclRef(embed_file.owner_decl); 12725 } 12726 12727 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 12728 const mod = sema.mod; 12729 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 12730 const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 12731 _ = try mod.getErrorValue(name); 12732 const error_set_type = try mod.singleErrorSetType(name); 12733 return sema.addConstant((try mod.intern(.{ .err = .{ 12734 .ty = error_set_type.toIntern(), 12735 .name = name, 12736 } })).toValue()); 12737 } 12738 12739 fn zirShl( 12740 sema: *Sema, 12741 block: *Block, 12742 inst: Zir.Inst.Index, 12743 air_tag: Air.Inst.Tag, 12744 ) CompileError!Air.Inst.Ref { 12745 const tracy = trace(@src()); 12746 defer tracy.end(); 12747 12748 const mod = sema.mod; 12749 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 12750 const src = inst_data.src(); 12751 sema.src = src; 12752 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 12753 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 12754 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 12755 const lhs = try sema.resolveInst(extra.lhs); 12756 const rhs = try sema.resolveInst(extra.rhs); 12757 const lhs_ty = sema.typeOf(lhs); 12758 const rhs_ty = sema.typeOf(rhs); 12759 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 12760 12761 const scalar_ty = lhs_ty.scalarType(mod); 12762 const scalar_rhs_ty = rhs_ty.scalarType(mod); 12763 12764 // TODO coerce rhs if air_tag is not shl_sat 12765 const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); 12766 12767 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(lhs); 12768 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(rhs); 12769 12770 if (maybe_rhs_val) |rhs_val| { 12771 if (rhs_val.isUndef(mod)) { 12772 return sema.addConstUndef(sema.typeOf(lhs)); 12773 } 12774 // If rhs is 0, return lhs without doing any calculations. 12775 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 12776 return lhs; 12777 } 12778 if (scalar_ty.zigTypeTag(mod) != .ComptimeInt and air_tag != .shl_sat) { 12779 const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits); 12780 if (rhs_ty.zigTypeTag(mod) == .Vector) { 12781 var i: usize = 0; 12782 while (i < rhs_ty.vectorLen(mod)) : (i += 1) { 12783 const rhs_elem = try rhs_val.elemValue(mod, i); 12784 if (rhs_elem.compareHetero(.gte, bit_value, mod)) { 12785 return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{ 12786 rhs_elem.fmtValue(scalar_ty, mod), 12787 i, 12788 scalar_ty.fmt(mod), 12789 }); 12790 } 12791 } 12792 } else if (rhs_val.compareHetero(.gte, bit_value, mod)) { 12793 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{ 12794 rhs_val.fmtValue(scalar_ty, mod), 12795 scalar_ty.fmt(mod), 12796 }); 12797 } 12798 } 12799 if (rhs_ty.zigTypeTag(mod) == .Vector) { 12800 var i: usize = 0; 12801 while (i < rhs_ty.vectorLen(mod)) : (i += 1) { 12802 const rhs_elem = try rhs_val.elemValue(mod, i); 12803 if (rhs_elem.compareHetero(.lt, try mod.intValue(scalar_rhs_ty, 0), mod)) { 12804 return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{ 12805 rhs_elem.fmtValue(scalar_ty, mod), 12806 i, 12807 }); 12808 } 12809 } 12810 } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) { 12811 return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{ 12812 rhs_val.fmtValue(scalar_ty, mod), 12813 }); 12814 } 12815 } 12816 12817 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { 12818 if (lhs_val.isUndef(mod)) return sema.addConstUndef(lhs_ty); 12819 const rhs_val = maybe_rhs_val orelse { 12820 if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 12821 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 12822 } 12823 break :rs rhs_src; 12824 }; 12825 12826 const val = switch (air_tag) { 12827 .shl_exact => val: { 12828 const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, mod); 12829 if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 12830 break :val shifted.wrapped_result; 12831 } 12832 if (shifted.overflow_bit.compareAllWithZero(.eq, mod)) { 12833 break :val shifted.wrapped_result; 12834 } 12835 return sema.fail(block, src, "operation caused overflow", .{}); 12836 }, 12837 12838 .shl_sat => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) 12839 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod) 12840 else 12841 try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, mod), 12842 12843 .shl => if (scalar_ty.zigTypeTag(mod) == .ComptimeInt) 12844 try lhs_val.shl(rhs_val, lhs_ty, sema.arena, mod) 12845 else 12846 try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, mod), 12847 12848 else => unreachable, 12849 }; 12850 12851 return sema.addConstant(val); 12852 } else lhs_src; 12853 12854 const new_rhs = if (air_tag == .shl_sat) rhs: { 12855 // Limit the RHS type for saturating shl to be an integer as small as the LHS. 12856 if (rhs_is_comptime_int or 12857 scalar_rhs_ty.intInfo(mod).bits > scalar_ty.intInfo(mod).bits) 12858 { 12859 const max_int = try sema.addConstant( 12860 try lhs_ty.maxInt(mod, lhs_ty), 12861 ); 12862 const rhs_limited = try sema.analyzeMinMax(block, rhs_src, .min, &.{ rhs, max_int }, &.{ rhs_src, rhs_src }); 12863 break :rhs try sema.intCast(block, src, lhs_ty, rhs_src, rhs_limited, rhs_src, false); 12864 } else { 12865 break :rhs rhs; 12866 } 12867 } else rhs; 12868 12869 try sema.requireRuntimeBlock(block, src, runtime_src); 12870 if (block.wantSafety()) { 12871 const bit_count = scalar_ty.intInfo(mod).bits; 12872 if (!std.math.isPowerOfTwo(bit_count)) { 12873 const bit_count_val = try mod.intValue(scalar_rhs_ty, bit_count); 12874 const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { 12875 const bit_count_inst = try sema.addConstant(try sema.splat(rhs_ty, bit_count_val)); 12876 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); 12877 break :ok try block.addInst(.{ 12878 .tag = .reduce, 12879 .data = .{ .reduce = .{ 12880 .operand = lt, 12881 .operation = .And, 12882 } }, 12883 }); 12884 } else ok: { 12885 const bit_count_inst = try sema.addConstant(bit_count_val); 12886 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); 12887 }; 12888 try sema.addSafetyCheck(block, ok, .shift_rhs_too_big); 12889 } 12890 12891 if (air_tag == .shl_exact) { 12892 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty); 12893 const op_ov = try block.addInst(.{ 12894 .tag = .shl_with_overflow, 12895 .data = .{ .ty_pl = .{ 12896 .ty = try sema.addType(op_ov_tuple_ty), 12897 .payload = try sema.addExtra(Air.Bin{ 12898 .lhs = lhs, 12899 .rhs = rhs, 12900 }), 12901 } }, 12902 }); 12903 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); 12904 const any_ov_bit = if (lhs_ty.zigTypeTag(mod) == .Vector) 12905 try block.addInst(.{ 12906 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 12907 .data = .{ .reduce = .{ 12908 .operand = ov_bit, 12909 .operation = .Or, 12910 } }, 12911 }) 12912 else 12913 ov_bit; 12914 const zero_ov = try sema.addConstant(try mod.intValue(Type.u1, 0)); 12915 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); 12916 12917 try sema.addSafetyCheck(block, no_ov, .shl_overflow); 12918 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); 12919 } 12920 } 12921 return block.addBinOp(air_tag, lhs, new_rhs); 12922 } 12923 12924 fn zirShr( 12925 sema: *Sema, 12926 block: *Block, 12927 inst: Zir.Inst.Index, 12928 air_tag: Air.Inst.Tag, 12929 ) CompileError!Air.Inst.Ref { 12930 const tracy = trace(@src()); 12931 defer tracy.end(); 12932 12933 const mod = sema.mod; 12934 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 12935 const src = inst_data.src(); 12936 sema.src = src; 12937 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 12938 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 12939 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 12940 const lhs = try sema.resolveInst(extra.lhs); 12941 const rhs = try sema.resolveInst(extra.rhs); 12942 const lhs_ty = sema.typeOf(lhs); 12943 const rhs_ty = sema.typeOf(rhs); 12944 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 12945 const scalar_ty = lhs_ty.scalarType(mod); 12946 12947 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(lhs); 12948 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(rhs); 12949 12950 const runtime_src = if (maybe_rhs_val) |rhs_val| rs: { 12951 if (rhs_val.isUndef(mod)) { 12952 return sema.addConstUndef(lhs_ty); 12953 } 12954 // If rhs is 0, return lhs without doing any calculations. 12955 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 12956 return lhs; 12957 } 12958 if (scalar_ty.zigTypeTag(mod) != .ComptimeInt) { 12959 const bit_value = try mod.intValue(Type.comptime_int, scalar_ty.intInfo(mod).bits); 12960 if (rhs_ty.zigTypeTag(mod) == .Vector) { 12961 var i: usize = 0; 12962 while (i < rhs_ty.vectorLen(mod)) : (i += 1) { 12963 const rhs_elem = try rhs_val.elemValue(mod, i); 12964 if (rhs_elem.compareHetero(.gte, bit_value, mod)) { 12965 return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{ 12966 rhs_elem.fmtValue(scalar_ty, mod), 12967 i, 12968 scalar_ty.fmt(mod), 12969 }); 12970 } 12971 } 12972 } else if (rhs_val.compareHetero(.gte, bit_value, mod)) { 12973 return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{ 12974 rhs_val.fmtValue(scalar_ty, mod), 12975 scalar_ty.fmt(mod), 12976 }); 12977 } 12978 } 12979 if (rhs_ty.zigTypeTag(mod) == .Vector) { 12980 var i: usize = 0; 12981 while (i < rhs_ty.vectorLen(mod)) : (i += 1) { 12982 const rhs_elem = try rhs_val.elemValue(mod, i); 12983 if (rhs_elem.compareHetero(.lt, try mod.intValue(rhs_ty.childType(mod), 0), mod)) { 12984 return sema.fail(block, rhs_src, "shift by negative amount '{}' at index '{d}'", .{ 12985 rhs_elem.fmtValue(scalar_ty, mod), 12986 i, 12987 }); 12988 } 12989 } 12990 } else if (rhs_val.compareHetero(.lt, try mod.intValue(rhs_ty, 0), mod)) { 12991 return sema.fail(block, rhs_src, "shift by negative amount '{}'", .{ 12992 rhs_val.fmtValue(scalar_ty, mod), 12993 }); 12994 } 12995 if (maybe_lhs_val) |lhs_val| { 12996 if (lhs_val.isUndef(mod)) { 12997 return sema.addConstUndef(lhs_ty); 12998 } 12999 if (air_tag == .shr_exact) { 13000 // Detect if any ones would be shifted out. 13001 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, mod); 13002 if (!(try truncated.compareAllWithZeroAdvanced(.eq, sema))) { 13003 return sema.fail(block, src, "exact shift shifted out 1 bits", .{}); 13004 } 13005 } 13006 const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, mod); 13007 return sema.addConstant(val); 13008 } else { 13009 break :rs lhs_src; 13010 } 13011 } else rhs_src; 13012 13013 if (maybe_rhs_val == null and scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 13014 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); 13015 } 13016 13017 try sema.requireRuntimeBlock(block, src, runtime_src); 13018 const result = try block.addBinOp(air_tag, lhs, rhs); 13019 if (block.wantSafety()) { 13020 const bit_count = scalar_ty.intInfo(mod).bits; 13021 if (!std.math.isPowerOfTwo(bit_count)) { 13022 const bit_count_val = try mod.intValue(rhs_ty.scalarType(mod), bit_count); 13023 13024 const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { 13025 const bit_count_inst = try sema.addConstant(try sema.splat(rhs_ty, bit_count_val)); 13026 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); 13027 break :ok try block.addInst(.{ 13028 .tag = .reduce, 13029 .data = .{ .reduce = .{ 13030 .operand = lt, 13031 .operation = .And, 13032 } }, 13033 }); 13034 } else ok: { 13035 const bit_count_inst = try sema.addConstant(bit_count_val); 13036 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); 13037 }; 13038 try sema.addSafetyCheck(block, ok, .shift_rhs_too_big); 13039 } 13040 13041 if (air_tag == .shr_exact) { 13042 const back = try block.addBinOp(.shl, result, rhs); 13043 13044 const ok = if (rhs_ty.zigTypeTag(mod) == .Vector) ok: { 13045 const eql = try block.addCmpVector(lhs, back, .eq); 13046 break :ok try block.addInst(.{ 13047 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 13048 .data = .{ .reduce = .{ 13049 .operand = eql, 13050 .operation = .And, 13051 } }, 13052 }); 13053 } else try block.addBinOp(.cmp_eq, lhs, back); 13054 try sema.addSafetyCheck(block, ok, .shr_overflow); 13055 } 13056 } 13057 return result; 13058 } 13059 13060 fn zirBitwise( 13061 sema: *Sema, 13062 block: *Block, 13063 inst: Zir.Inst.Index, 13064 air_tag: Air.Inst.Tag, 13065 ) CompileError!Air.Inst.Ref { 13066 const tracy = trace(@src()); 13067 defer tracy.end(); 13068 13069 const mod = sema.mod; 13070 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 13071 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 13072 sema.src = src; 13073 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13074 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13075 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13076 const lhs = try sema.resolveInst(extra.lhs); 13077 const rhs = try sema.resolveInst(extra.rhs); 13078 const lhs_ty = sema.typeOf(lhs); 13079 const rhs_ty = sema.typeOf(rhs); 13080 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13081 13082 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 13083 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); 13084 const scalar_type = resolved_type.scalarType(mod); 13085 const scalar_tag = scalar_type.zigTypeTag(mod); 13086 13087 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 13088 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 13089 13090 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 13091 13092 if (!is_int) { 13093 return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(mod)), @tagName(rhs_ty.zigTypeTag(mod)) }); 13094 } 13095 13096 const runtime_src = runtime: { 13097 // TODO: ask the linker what kind of relocations are available, and 13098 // in some cases emit a Value that means "this decl's address AND'd with this operand". 13099 if (try sema.resolveMaybeUndefValIntable(casted_lhs)) |lhs_val| { 13100 if (try sema.resolveMaybeUndefValIntable(casted_rhs)) |rhs_val| { 13101 const result_val = switch (air_tag) { 13102 .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, mod), 13103 .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, mod), 13104 .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, mod), 13105 else => unreachable, 13106 }; 13107 return sema.addConstant(result_val); 13108 } else { 13109 break :runtime rhs_src; 13110 } 13111 } else { 13112 break :runtime lhs_src; 13113 } 13114 }; 13115 13116 try sema.requireRuntimeBlock(block, src, runtime_src); 13117 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 13118 } 13119 13120 fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13121 const tracy = trace(@src()); 13122 defer tracy.end(); 13123 13124 const mod = sema.mod; 13125 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 13126 const src = inst_data.src(); 13127 const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 13128 13129 const operand = try sema.resolveInst(inst_data.operand); 13130 const operand_type = sema.typeOf(operand); 13131 const scalar_type = operand_type.scalarType(mod); 13132 13133 if (scalar_type.zigTypeTag(mod) != .Int) { 13134 return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{ 13135 operand_type.fmt(mod), 13136 }); 13137 } 13138 13139 if (try sema.resolveMaybeUndefVal(operand)) |val| { 13140 if (val.isUndef(mod)) { 13141 return sema.addConstUndef(operand_type); 13142 } else if (operand_type.zigTypeTag(mod) == .Vector) { 13143 const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen(mod)); 13144 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 13145 for (elems, 0..) |*elem, i| { 13146 const elem_val = try val.elemValue(mod, i); 13147 elem.* = try (try elem_val.bitwiseNot(scalar_type, sema.arena, mod)).intern(scalar_type, mod); 13148 } 13149 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 13150 .ty = operand_type.toIntern(), 13151 .storage = .{ .elems = elems }, 13152 } })).toValue()); 13153 } else { 13154 const result_val = try val.bitwiseNot(operand_type, sema.arena, mod); 13155 return sema.addConstant(result_val); 13156 } 13157 } 13158 13159 try sema.requireRuntimeBlock(block, src, null); 13160 return block.addTyOp(.not, operand_type, operand); 13161 } 13162 13163 fn analyzeTupleCat( 13164 sema: *Sema, 13165 block: *Block, 13166 src_node: i32, 13167 lhs: Air.Inst.Ref, 13168 rhs: Air.Inst.Ref, 13169 ) CompileError!Air.Inst.Ref { 13170 const mod = sema.mod; 13171 const lhs_ty = sema.typeOf(lhs); 13172 const rhs_ty = sema.typeOf(rhs); 13173 const src = LazySrcLoc.nodeOffset(src_node); 13174 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; 13175 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; 13176 13177 const lhs_len = lhs_ty.structFieldCount(mod); 13178 const rhs_len = rhs_ty.structFieldCount(mod); 13179 const dest_fields = lhs_len + rhs_len; 13180 13181 if (dest_fields == 0) { 13182 return sema.addConstant(Value.empty_struct); 13183 } 13184 if (lhs_len == 0) { 13185 return rhs; 13186 } 13187 if (rhs_len == 0) { 13188 return lhs; 13189 } 13190 const final_len = try sema.usizeCast(block, rhs_src, dest_fields); 13191 13192 const types = try sema.arena.alloc(InternPool.Index, final_len); 13193 const values = try sema.arena.alloc(InternPool.Index, final_len); 13194 13195 const opt_runtime_src = rs: { 13196 var runtime_src: ?LazySrcLoc = null; 13197 var i: u32 = 0; 13198 while (i < lhs_len) : (i += 1) { 13199 types[i] = lhs_ty.structFieldType(i, mod).toIntern(); 13200 const default_val = lhs_ty.structFieldDefaultValue(i, mod); 13201 values[i] = default_val.toIntern(); 13202 const operand_src = lhs_src; // TODO better source location 13203 if (default_val.toIntern() == .unreachable_value) { 13204 runtime_src = operand_src; 13205 values[i] = .none; 13206 } 13207 } 13208 i = 0; 13209 while (i < rhs_len) : (i += 1) { 13210 types[i + lhs_len] = rhs_ty.structFieldType(i, mod).toIntern(); 13211 const default_val = rhs_ty.structFieldDefaultValue(i, mod); 13212 values[i + lhs_len] = default_val.toIntern(); 13213 const operand_src = rhs_src; // TODO better source location 13214 if (default_val.toIntern() == .unreachable_value) { 13215 runtime_src = operand_src; 13216 values[i + lhs_len] = .none; 13217 } 13218 } 13219 break :rs runtime_src; 13220 }; 13221 13222 const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ 13223 .types = types, 13224 .values = values, 13225 .names = &.{}, 13226 } }); 13227 13228 const runtime_src = opt_runtime_src orelse { 13229 const tuple_val = try mod.intern(.{ .aggregate = .{ 13230 .ty = tuple_ty, 13231 .storage = .{ .elems = values }, 13232 } }); 13233 return sema.addConstant(tuple_val.toValue()); 13234 }; 13235 13236 try sema.requireRuntimeBlock(block, src, runtime_src); 13237 13238 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); 13239 var i: u32 = 0; 13240 while (i < lhs_len) : (i += 1) { 13241 const operand_src = lhs_src; // TODO better source location 13242 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, i, lhs_ty); 13243 } 13244 i = 0; 13245 while (i < rhs_len) : (i += 1) { 13246 const operand_src = rhs_src; // TODO better source location 13247 element_refs[i + lhs_len] = 13248 try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty); 13249 } 13250 13251 return block.addAggregateInit(tuple_ty.toType(), element_refs); 13252 } 13253 13254 fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13255 const tracy = trace(@src()); 13256 defer tracy.end(); 13257 13258 const mod = sema.mod; 13259 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 13260 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13261 const lhs = try sema.resolveInst(extra.lhs); 13262 const rhs = try sema.resolveInst(extra.rhs); 13263 const lhs_ty = sema.typeOf(lhs); 13264 const rhs_ty = sema.typeOf(rhs); 13265 const src = inst_data.src(); 13266 13267 const lhs_is_tuple = lhs_ty.isTuple(mod); 13268 const rhs_is_tuple = rhs_ty.isTuple(mod); 13269 if (lhs_is_tuple and rhs_is_tuple) { 13270 return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); 13271 } 13272 13273 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13274 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13275 13276 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: { 13277 if (lhs_is_tuple) break :lhs_info @as(Type.ArrayInfo, undefined); 13278 return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)}); 13279 }; 13280 const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse { 13281 assert(!rhs_is_tuple); 13282 return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(mod)}); 13283 }; 13284 13285 const resolved_elem_ty = t: { 13286 var trash_block = block.makeSubBlock(); 13287 trash_block.is_comptime = false; 13288 defer trash_block.instructions.deinit(sema.gpa); 13289 13290 const instructions = [_]Air.Inst.Ref{ 13291 try trash_block.addBitCast(lhs_info.elem_type, .void_value), 13292 try trash_block.addBitCast(rhs_info.elem_type, .void_value), 13293 }; 13294 break :t try sema.resolvePeerTypes(block, src, &instructions, .{ 13295 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 13296 }); 13297 }; 13298 13299 // When there is a sentinel mismatch, no sentinel on the result. 13300 // Otherwise, use the sentinel value provided by either operand, 13301 // coercing it to the peer-resolved element type. 13302 const res_sent_val: ?Value = s: { 13303 if (lhs_info.sentinel) |lhs_sent_val| { 13304 const lhs_sent = try sema.addConstant(lhs_sent_val); 13305 if (rhs_info.sentinel) |rhs_sent_val| { 13306 const rhs_sent = try sema.addConstant(rhs_sent_val); 13307 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); 13308 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); 13309 const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted, "array sentinel value must be comptime-known"); 13310 const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted, "array sentinel value must be comptime-known"); 13311 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { 13312 break :s lhs_sent_casted_val; 13313 } else { 13314 break :s null; 13315 } 13316 } else { 13317 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); 13318 const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted, "array sentinel value must be comptime-known"); 13319 break :s lhs_sent_casted_val; 13320 } 13321 } else { 13322 if (rhs_info.sentinel) |rhs_sent_val| { 13323 const rhs_sent = try sema.addConstant(rhs_sent_val); 13324 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); 13325 const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted, "array sentinel value must be comptime-known"); 13326 break :s rhs_sent_casted_val; 13327 } else { 13328 break :s null; 13329 } 13330 } 13331 }; 13332 13333 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); 13334 const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); 13335 const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) { 13336 error.Overflow => return sema.fail( 13337 block, 13338 src, 13339 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle", 13340 .{ lhs_len, rhs_len }, 13341 ), 13342 }; 13343 13344 const result_ty = try mod.arrayType(.{ 13345 .len = result_len, 13346 .sentinel = if (res_sent_val) |v| v.toIntern() else .none, 13347 .child = resolved_elem_ty.toIntern(), 13348 }); 13349 const ptr_addrspace = p: { 13350 if (lhs_ty.zigTypeTag(mod) == .Pointer) break :p lhs_ty.ptrAddressSpace(mod); 13351 if (rhs_ty.zigTypeTag(mod) == .Pointer) break :p rhs_ty.ptrAddressSpace(mod); 13352 break :p null; 13353 }; 13354 13355 const runtime_src = if (switch (lhs_ty.zigTypeTag(mod)) { 13356 .Array, .Struct => try sema.resolveMaybeUndefVal(lhs), 13357 .Pointer => try sema.resolveDefinedValue(block, lhs_src, lhs), 13358 else => unreachable, 13359 }) |lhs_val| rs: { 13360 if (switch (rhs_ty.zigTypeTag(mod)) { 13361 .Array, .Struct => try sema.resolveMaybeUndefVal(rhs), 13362 .Pointer => try sema.resolveDefinedValue(block, rhs_src, rhs), 13363 else => unreachable, 13364 }) |rhs_val| { 13365 const lhs_sub_val = if (lhs_ty.isSinglePointer(mod)) 13366 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? 13367 else 13368 lhs_val; 13369 13370 const rhs_sub_val = if (rhs_ty.isSinglePointer(mod)) 13371 (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? 13372 else 13373 rhs_val; 13374 13375 const element_vals = try sema.arena.alloc(InternPool.Index, result_len); 13376 var elem_i: usize = 0; 13377 while (elem_i < lhs_len) : (elem_i += 1) { 13378 const lhs_elem_i = elem_i; 13379 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, mod) else Value.@"unreachable"; 13380 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(mod, lhs_elem_i) else elem_default_val; 13381 const elem_val_inst = try sema.addConstant(elem_val); 13382 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); 13383 const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); 13384 element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod); 13385 } 13386 while (elem_i < result_len) : (elem_i += 1) { 13387 const rhs_elem_i = elem_i - lhs_len; 13388 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, mod) else Value.@"unreachable"; 13389 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(mod, rhs_elem_i) else elem_default_val; 13390 const elem_val_inst = try sema.addConstant(elem_val); 13391 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); 13392 const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); 13393 element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod); 13394 } 13395 return sema.addConstantMaybeRef(block, result_ty, (try mod.intern(.{ .aggregate = .{ 13396 .ty = result_ty.toIntern(), 13397 .storage = .{ .elems = element_vals }, 13398 } })).toValue(), ptr_addrspace != null); 13399 } else break :rs rhs_src; 13400 } else lhs_src; 13401 13402 try sema.requireRuntimeBlock(block, src, runtime_src); 13403 13404 if (ptr_addrspace) |ptr_as| { 13405 const alloc_ty = try mod.ptrType(.{ 13406 .child = result_ty.toIntern(), 13407 .flags = .{ .address_space = ptr_as }, 13408 }); 13409 const alloc = try block.addTy(.alloc, alloc_ty); 13410 const elem_ptr_ty = try mod.ptrType(.{ 13411 .child = resolved_elem_ty.toIntern(), 13412 .flags = .{ .address_space = ptr_as }, 13413 }); 13414 13415 var elem_i: usize = 0; 13416 while (elem_i < lhs_len) : (elem_i += 1) { 13417 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i); 13418 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 13419 const init = try sema.elemVal(block, lhs_src, lhs, elem_index, src, true); 13420 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 13421 } 13422 while (elem_i < result_len) : (elem_i += 1) { 13423 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i); 13424 const rhs_index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len); 13425 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 13426 const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src, true); 13427 try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store); 13428 } 13429 if (res_sent_val) |sent_val| { 13430 const elem_index = try sema.addIntUnsigned(Type.usize, result_len); 13431 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 13432 const init = try sema.addConstant(try mod.getCoerced(sent_val, lhs_info.elem_type)); 13433 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 13434 } 13435 13436 return alloc; 13437 } 13438 13439 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); 13440 { 13441 var elem_i: usize = 0; 13442 while (elem_i < lhs_len) : (elem_i += 1) { 13443 const index = try sema.addIntUnsigned(Type.usize, elem_i); 13444 const init = try sema.elemVal(block, lhs_src, lhs, index, src, true); 13445 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, lhs_src); 13446 } 13447 while (elem_i < result_len) : (elem_i += 1) { 13448 const index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len); 13449 const init = try sema.elemVal(block, rhs_src, rhs, index, src, true); 13450 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, rhs_src); 13451 } 13452 } 13453 13454 return block.addAggregateInit(result_ty, element_refs); 13455 } 13456 13457 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo { 13458 const mod = sema.mod; 13459 const operand_ty = sema.typeOf(operand); 13460 switch (operand_ty.zigTypeTag(mod)) { 13461 .Array => return operand_ty.arrayInfo(mod), 13462 .Pointer => { 13463 const ptr_info = operand_ty.ptrInfo(mod); 13464 switch (ptr_info.flags.size) { 13465 // TODO: in the Many case here this should only work if the type 13466 // has a sentinel, and this code should compute the length based 13467 // on the sentinel value. 13468 .Slice, .Many => { 13469 const val = try sema.resolveConstValue(block, src, operand, "slice value being concatenated must be comptime-known"); 13470 return Type.ArrayInfo{ 13471 .elem_type = ptr_info.child.toType(), 13472 .sentinel = switch (ptr_info.sentinel) { 13473 .none => null, 13474 else => ptr_info.sentinel.toValue(), 13475 }, 13476 .len = val.sliceLen(mod), 13477 }; 13478 }, 13479 .One => { 13480 if (ptr_info.child.toType().zigTypeTag(mod) == .Array) { 13481 return ptr_info.child.toType().arrayInfo(mod); 13482 } 13483 }, 13484 .C => {}, 13485 } 13486 }, 13487 .Struct => { 13488 if (operand_ty.isTuple(mod) and peer_ty.isIndexable(mod)) { 13489 assert(!peer_ty.isTuple(mod)); 13490 return .{ 13491 .elem_type = peer_ty.elemType2(mod), 13492 .sentinel = null, 13493 .len = operand_ty.arrayLen(mod), 13494 }; 13495 } 13496 }, 13497 else => {}, 13498 } 13499 return null; 13500 } 13501 13502 fn analyzeTupleMul( 13503 sema: *Sema, 13504 block: *Block, 13505 src_node: i32, 13506 operand: Air.Inst.Ref, 13507 factor: usize, 13508 ) CompileError!Air.Inst.Ref { 13509 const mod = sema.mod; 13510 const operand_ty = sema.typeOf(operand); 13511 const src = LazySrcLoc.nodeOffset(src_node); 13512 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; 13513 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; 13514 13515 const tuple_len = operand_ty.structFieldCount(mod); 13516 const final_len = std.math.mul(usize, tuple_len, factor) catch 13517 return sema.fail(block, rhs_src, "operation results in overflow", .{}); 13518 13519 if (final_len == 0) { 13520 return sema.addConstant(Value.empty_struct); 13521 } 13522 const types = try sema.arena.alloc(InternPool.Index, final_len); 13523 const values = try sema.arena.alloc(InternPool.Index, final_len); 13524 13525 const opt_runtime_src = rs: { 13526 var runtime_src: ?LazySrcLoc = null; 13527 for (0..tuple_len) |i| { 13528 types[i] = operand_ty.structFieldType(i, mod).toIntern(); 13529 values[i] = operand_ty.structFieldDefaultValue(i, mod).toIntern(); 13530 const operand_src = lhs_src; // TODO better source location 13531 if (values[i] == .unreachable_value) { 13532 runtime_src = operand_src; 13533 values[i] = .none; // TODO don't treat unreachable_value as special 13534 } 13535 } 13536 for (0..factor) |i| { 13537 mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]); 13538 mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]); 13539 } 13540 break :rs runtime_src; 13541 }; 13542 13543 const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ 13544 .types = types, 13545 .values = values, 13546 .names = &.{}, 13547 } }); 13548 13549 const runtime_src = opt_runtime_src orelse { 13550 const tuple_val = try mod.intern(.{ .aggregate = .{ 13551 .ty = tuple_ty, 13552 .storage = .{ .elems = values }, 13553 } }); 13554 return sema.addConstant(tuple_val.toValue()); 13555 }; 13556 13557 try sema.requireRuntimeBlock(block, src, runtime_src); 13558 13559 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); 13560 var i: u32 = 0; 13561 while (i < tuple_len) : (i += 1) { 13562 const operand_src = lhs_src; // TODO better source location 13563 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @as(u32, @intCast(i)), operand_ty); 13564 } 13565 i = 1; 13566 while (i < factor) : (i += 1) { 13567 @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]); 13568 } 13569 13570 return block.addAggregateInit(tuple_ty.toType(), element_refs); 13571 } 13572 13573 fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13574 const tracy = trace(@src()); 13575 defer tracy.end(); 13576 13577 const mod = sema.mod; 13578 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 13579 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13580 const lhs = try sema.resolveInst(extra.lhs); 13581 const lhs_ty = sema.typeOf(lhs); 13582 const src: LazySrcLoc = inst_data.src(); 13583 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13584 const operator_src: LazySrcLoc = .{ .node_offset_main_token = inst_data.src_node }; 13585 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13586 13587 if (lhs_ty.isTuple(mod)) { 13588 // In `**` rhs must be comptime-known, but lhs can be runtime-known 13589 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, "array multiplication factor must be comptime-known"); 13590 const factor_casted = try sema.usizeCast(block, rhs_src, factor); 13591 return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted); 13592 } 13593 13594 // Analyze the lhs first, to catch the case that someone tried to do exponentiation 13595 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse { 13596 const msg = msg: { 13597 const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(mod)}); 13598 errdefer msg.destroy(sema.gpa); 13599 switch (lhs_ty.zigTypeTag(mod)) { 13600 .Int, .Float, .ComptimeFloat, .ComptimeInt, .Vector => { 13601 try sema.errNote(block, operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{}); 13602 }, 13603 else => {}, 13604 } 13605 break :msg msg; 13606 }; 13607 return sema.failWithOwnedErrorMsg(msg); 13608 }; 13609 13610 // In `**` rhs must be comptime-known, but lhs can be runtime-known 13611 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, "array multiplication factor must be comptime-known"); 13612 13613 const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch 13614 return sema.fail(block, rhs_src, "operation results in overflow", .{}); 13615 const result_len = try sema.usizeCast(block, src, result_len_u64); 13616 13617 const result_ty = try mod.arrayType(.{ 13618 .len = result_len, 13619 .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none, 13620 .child = lhs_info.elem_type.toIntern(), 13621 }); 13622 13623 const ptr_addrspace = if (lhs_ty.zigTypeTag(mod) == .Pointer) lhs_ty.ptrAddressSpace(mod) else null; 13624 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); 13625 13626 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { 13627 const lhs_sub_val = if (lhs_ty.isSinglePointer(mod)) 13628 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? 13629 else 13630 lhs_val; 13631 13632 const val = v: { 13633 // Optimization for the common pattern of a single element repeated N times, such 13634 // as zero-filling a byte array. 13635 if (lhs_len == 1 and lhs_info.sentinel == null) { 13636 const elem_val = try lhs_sub_val.elemValue(mod, 0); 13637 break :v try mod.intern(.{ .aggregate = .{ 13638 .ty = result_ty.toIntern(), 13639 .storage = .{ .repeated_elem = elem_val.toIntern() }, 13640 } }); 13641 } 13642 13643 const element_vals = try sema.arena.alloc(InternPool.Index, result_len); 13644 var elem_i: usize = 0; 13645 while (elem_i < result_len) { 13646 var lhs_i: usize = 0; 13647 while (lhs_i < lhs_len) : (lhs_i += 1) { 13648 const elem_val = try lhs_sub_val.elemValue(mod, lhs_i); 13649 element_vals[elem_i] = elem_val.toIntern(); 13650 elem_i += 1; 13651 } 13652 } 13653 break :v try mod.intern(.{ .aggregate = .{ 13654 .ty = result_ty.toIntern(), 13655 .storage = .{ .elems = element_vals }, 13656 } }); 13657 }; 13658 return sema.addConstantMaybeRef(block, result_ty, val.toValue(), ptr_addrspace != null); 13659 } 13660 13661 try sema.requireRuntimeBlock(block, src, lhs_src); 13662 13663 if (ptr_addrspace) |ptr_as| { 13664 const alloc_ty = try mod.ptrType(.{ 13665 .child = result_ty.toIntern(), 13666 .flags = .{ .address_space = ptr_as }, 13667 }); 13668 const alloc = try block.addTy(.alloc, alloc_ty); 13669 const elem_ptr_ty = try mod.ptrType(.{ 13670 .child = lhs_info.elem_type.toIntern(), 13671 .flags = .{ .address_space = ptr_as }, 13672 }); 13673 13674 var elem_i: usize = 0; 13675 while (elem_i < result_len) { 13676 var lhs_i: usize = 0; 13677 while (lhs_i < lhs_len) : (lhs_i += 1) { 13678 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i); 13679 elem_i += 1; 13680 const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i); 13681 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 13682 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src, true); 13683 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 13684 } 13685 } 13686 if (lhs_info.sentinel) |sent_val| { 13687 const elem_index = try sema.addIntUnsigned(Type.usize, result_len); 13688 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); 13689 const init = try sema.addConstant(sent_val); 13690 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); 13691 } 13692 13693 return alloc; 13694 } 13695 13696 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); 13697 var elem_i: usize = 0; 13698 while (elem_i < result_len) { 13699 var lhs_i: usize = 0; 13700 while (lhs_i < lhs_len) : (lhs_i += 1) { 13701 const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i); 13702 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src, true); 13703 element_refs[elem_i] = init; 13704 elem_i += 1; 13705 } 13706 } 13707 13708 return block.addAggregateInit(result_ty, element_refs); 13709 } 13710 13711 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13712 const mod = sema.mod; 13713 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 13714 const src = inst_data.src(); 13715 const lhs_src = src; 13716 const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 13717 13718 const rhs = try sema.resolveInst(inst_data.operand); 13719 const rhs_ty = sema.typeOf(rhs); 13720 const rhs_scalar_ty = rhs_ty.scalarType(mod); 13721 13722 if (rhs_scalar_ty.isUnsignedInt(mod) or switch (rhs_scalar_ty.zigTypeTag(mod)) { 13723 .Int, .ComptimeInt, .Float, .ComptimeFloat => false, 13724 else => true, 13725 }) { 13726 return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)}); 13727 } 13728 13729 if (rhs_scalar_ty.isAnyFloat()) { 13730 // We handle float negation here to ensure negative zero is represented in the bits. 13731 if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { 13732 if (rhs_val.isUndef(mod)) return sema.addConstUndef(rhs_ty); 13733 return sema.addConstant(try rhs_val.floatNeg(rhs_ty, sema.arena, mod)); 13734 } 13735 try sema.requireRuntimeBlock(block, src, null); 13736 return block.addUnOp(if (block.float_mode == .Optimized) .neg_optimized else .neg, rhs); 13737 } 13738 13739 const lhs = try sema.addConstant(try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0))); 13740 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true); 13741 } 13742 13743 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13744 const mod = sema.mod; 13745 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 13746 const src = inst_data.src(); 13747 const lhs_src = src; 13748 const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 13749 13750 const rhs = try sema.resolveInst(inst_data.operand); 13751 const rhs_ty = sema.typeOf(rhs); 13752 const rhs_scalar_ty = rhs_ty.scalarType(mod); 13753 13754 switch (rhs_scalar_ty.zigTypeTag(mod)) { 13755 .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, 13756 else => return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(mod)}), 13757 } 13758 13759 const lhs = try sema.addConstant(try sema.splat(rhs_ty, try mod.intValue(rhs_scalar_ty, 0))); 13760 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true); 13761 } 13762 13763 fn zirArithmetic( 13764 sema: *Sema, 13765 block: *Block, 13766 inst: Zir.Inst.Index, 13767 zir_tag: Zir.Inst.Tag, 13768 safety: bool, 13769 ) CompileError!Air.Inst.Ref { 13770 const tracy = trace(@src()); 13771 defer tracy.end(); 13772 13773 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 13774 sema.src = .{ .node_offset_bin_op = inst_data.src_node }; 13775 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13776 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13777 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13778 const lhs = try sema.resolveInst(extra.lhs); 13779 const rhs = try sema.resolveInst(extra.rhs); 13780 13781 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, safety); 13782 } 13783 13784 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13785 const mod = sema.mod; 13786 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 13787 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 13788 sema.src = src; 13789 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13790 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13791 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13792 const lhs = try sema.resolveInst(extra.lhs); 13793 const rhs = try sema.resolveInst(extra.rhs); 13794 const lhs_ty = sema.typeOf(lhs); 13795 const rhs_ty = sema.typeOf(rhs); 13796 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 13797 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 13798 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13799 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 13800 13801 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 13802 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 13803 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 13804 }); 13805 13806 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 13807 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 13808 13809 const lhs_scalar_ty = lhs_ty.scalarType(mod); 13810 const rhs_scalar_ty = rhs_ty.scalarType(mod); 13811 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 13812 13813 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 13814 13815 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div); 13816 13817 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); 13818 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); 13819 13820 if ((lhs_ty.zigTypeTag(mod) == .ComptimeFloat and rhs_ty.zigTypeTag(mod) == .ComptimeInt) or 13821 (lhs_ty.zigTypeTag(mod) == .ComptimeInt and rhs_ty.zigTypeTag(mod) == .ComptimeFloat)) 13822 { 13823 // If it makes a difference whether we coerce to ints or floats before doing the division, error. 13824 // If lhs % rhs is 0, it doesn't matter. 13825 const lhs_val = maybe_lhs_val orelse unreachable; 13826 const rhs_val = maybe_rhs_val orelse unreachable; 13827 const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod) catch unreachable; 13828 if (!rem.compareAllWithZero(.eq, mod)) { 13829 return sema.fail( 13830 block, 13831 src, 13832 "ambiguous coercion of division operands '{}' and '{}'; non-zero remainder '{}'", 13833 .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod), rem.fmtValue(resolved_type, mod) }, 13834 ); 13835 } 13836 } 13837 13838 // TODO: emit compile error when .div is used on integers and there would be an 13839 // ambiguous result between div_floor and div_trunc. 13840 13841 // For integers: 13842 // If the lhs is zero, then zero is returned regardless of rhs. 13843 // If the rhs is zero, compile error for division by zero. 13844 // If the rhs is undefined, compile error because there is a possible 13845 // value (zero) for which the division would be illegal behavior. 13846 // If the lhs is undefined: 13847 // * if lhs type is signed: 13848 // * if rhs is comptime-known and not -1, result is undefined 13849 // * if rhs is -1 or runtime-known, compile error because there is a 13850 // possible value (-min_int / -1) for which division would be 13851 // illegal behavior. 13852 // * if lhs type is unsigned, undef is returned regardless of rhs. 13853 // 13854 // For floats: 13855 // If the rhs is zero: 13856 // * comptime_float: compile error for division by zero. 13857 // * other float type: 13858 // * if the lhs is zero: QNaN 13859 // * otherwise: +Inf or -Inf depending on lhs sign 13860 // If the rhs is undefined: 13861 // * comptime_float: compile error because there is a possible 13862 // value (zero) for which the division would be illegal behavior. 13863 // * other float type: result is undefined 13864 // If the lhs is undefined, result is undefined. 13865 switch (scalar_tag) { 13866 .Int, .ComptimeInt, .ComptimeFloat => { 13867 if (maybe_lhs_val) |lhs_val| { 13868 if (!lhs_val.isUndef(mod)) { 13869 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 13870 const scalar_zero = switch (scalar_tag) { 13871 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 13872 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 13873 else => unreachable, 13874 }; 13875 const zero_val = try sema.splat(resolved_type, scalar_zero); 13876 return sema.addConstant(zero_val); 13877 } 13878 } 13879 } 13880 if (maybe_rhs_val) |rhs_val| { 13881 if (rhs_val.isUndef(mod)) { 13882 return sema.failWithUseOfUndef(block, rhs_src); 13883 } 13884 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 13885 return sema.failWithDivideByZero(block, rhs_src); 13886 } 13887 // TODO: if the RHS is one, return the LHS directly 13888 } 13889 }, 13890 else => {}, 13891 } 13892 13893 const runtime_src = rs: { 13894 if (maybe_lhs_val) |lhs_val| { 13895 if (lhs_val.isUndef(mod)) { 13896 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { 13897 if (maybe_rhs_val) |rhs_val| { 13898 if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { 13899 return sema.addConstUndef(resolved_type); 13900 } 13901 } 13902 return sema.failWithUseOfUndef(block, rhs_src); 13903 } 13904 return sema.addConstUndef(resolved_type); 13905 } 13906 13907 if (maybe_rhs_val) |rhs_val| { 13908 if (is_int) { 13909 var overflow_idx: ?usize = null; 13910 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); 13911 if (overflow_idx) |vec_idx| { 13912 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); 13913 } 13914 return sema.addConstant(res); 13915 } else { 13916 return sema.addConstant( 13917 try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, mod), 13918 ); 13919 } 13920 } else { 13921 break :rs rhs_src; 13922 } 13923 } else { 13924 break :rs lhs_src; 13925 } 13926 }; 13927 13928 try sema.requireRuntimeBlock(block, src, runtime_src); 13929 13930 if (block.wantSafety()) { 13931 try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 13932 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); 13933 } 13934 13935 const air_tag = if (is_int) blk: { 13936 if (lhs_ty.isSignedInt(mod) or rhs_ty.isSignedInt(mod)) { 13937 return sema.fail( 13938 block, 13939 src, 13940 "division with '{}' and '{}': signed integers must use @divTrunc, @divFloor, or @divExact", 13941 .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod) }, 13942 ); 13943 } 13944 break :blk Air.Inst.Tag.div_trunc; 13945 } else switch (block.float_mode) { 13946 .Optimized => Air.Inst.Tag.div_float_optimized, 13947 .Strict => Air.Inst.Tag.div_float, 13948 }; 13949 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 13950 } 13951 13952 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 13953 const mod = sema.mod; 13954 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 13955 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 13956 sema.src = src; 13957 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 13958 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 13959 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 13960 const lhs = try sema.resolveInst(extra.lhs); 13961 const rhs = try sema.resolveInst(extra.rhs); 13962 const lhs_ty = sema.typeOf(lhs); 13963 const rhs_ty = sema.typeOf(rhs); 13964 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 13965 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 13966 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 13967 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 13968 13969 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 13970 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 13971 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 13972 }); 13973 13974 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 13975 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 13976 13977 const lhs_scalar_ty = lhs_ty.scalarType(mod); 13978 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 13979 13980 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 13981 13982 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); 13983 13984 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); 13985 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); 13986 13987 const runtime_src = rs: { 13988 // For integers: 13989 // If the lhs is zero, then zero is returned regardless of rhs. 13990 // If the rhs is zero, compile error for division by zero. 13991 // If the rhs is undefined, compile error because there is a possible 13992 // value (zero) for which the division would be illegal behavior. 13993 // If the lhs is undefined, compile error because there is a possible 13994 // value for which the division would result in a remainder. 13995 // TODO: emit runtime safety for if there is a remainder 13996 // TODO: emit runtime safety for division by zero 13997 // 13998 // For floats: 13999 // If the rhs is zero, compile error for division by zero. 14000 // If the rhs is undefined, compile error because there is a possible 14001 // value (zero) for which the division would be illegal behavior. 14002 // If the lhs is undefined, compile error because there is a possible 14003 // value for which the division would result in a remainder. 14004 if (maybe_lhs_val) |lhs_val| { 14005 if (lhs_val.isUndef(mod)) { 14006 return sema.failWithUseOfUndef(block, rhs_src); 14007 } else { 14008 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14009 const scalar_zero = switch (scalar_tag) { 14010 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14011 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14012 else => unreachable, 14013 }; 14014 const zero_val = try sema.splat(resolved_type, scalar_zero); 14015 return sema.addConstant(zero_val); 14016 } 14017 } 14018 } 14019 if (maybe_rhs_val) |rhs_val| { 14020 if (rhs_val.isUndef(mod)) { 14021 return sema.failWithUseOfUndef(block, rhs_src); 14022 } 14023 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14024 return sema.failWithDivideByZero(block, rhs_src); 14025 } 14026 // TODO: if the RHS is one, return the LHS directly 14027 } 14028 if (maybe_lhs_val) |lhs_val| { 14029 if (maybe_rhs_val) |rhs_val| { 14030 if (is_int) { 14031 const modulus_val = try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod); 14032 if (!(modulus_val.compareAllWithZero(.eq, mod))) { 14033 return sema.fail(block, src, "exact division produced remainder", .{}); 14034 } 14035 var overflow_idx: ?usize = null; 14036 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); 14037 if (overflow_idx) |vec_idx| { 14038 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); 14039 } 14040 return sema.addConstant(res); 14041 } else { 14042 const modulus_val = try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod); 14043 if (!(modulus_val.compareAllWithZero(.eq, mod))) { 14044 return sema.fail(block, src, "exact division produced remainder", .{}); 14045 } 14046 return sema.addConstant( 14047 try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, mod), 14048 ); 14049 } 14050 } else break :rs rhs_src; 14051 } else break :rs lhs_src; 14052 }; 14053 14054 try sema.requireRuntimeBlock(block, src, runtime_src); 14055 14056 // Depending on whether safety is enabled, we will have a slightly different strategy 14057 // here. The `div_exact` AIR instruction causes undefined behavior if a remainder 14058 // is produced, so in the safety check case, it cannot be used. Instead we do a 14059 // div_trunc and check for remainder. 14060 14061 if (block.wantSafety()) { 14062 try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14063 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14064 14065 const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs); 14066 const ok = if (!is_int) ok: { 14067 const floored = try block.addUnOp(.floor, result); 14068 14069 if (resolved_type.zigTypeTag(mod) == .Vector) { 14070 const eql = try block.addCmpVector(result, floored, .eq); 14071 break :ok try block.addInst(.{ 14072 .tag = switch (block.float_mode) { 14073 .Strict => .reduce, 14074 .Optimized => .reduce_optimized, 14075 }, 14076 .data = .{ .reduce = .{ 14077 .operand = eql, 14078 .operation = .And, 14079 } }, 14080 }); 14081 } else { 14082 const is_in_range = try block.addBinOp(switch (block.float_mode) { 14083 .Strict => .cmp_eq, 14084 .Optimized => .cmp_eq_optimized, 14085 }, result, floored); 14086 break :ok is_in_range; 14087 } 14088 } else ok: { 14089 const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); 14090 14091 const scalar_zero = switch (scalar_tag) { 14092 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14093 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14094 else => unreachable, 14095 }; 14096 if (resolved_type.zigTypeTag(mod) == .Vector) { 14097 const zero_val = try sema.splat(resolved_type, scalar_zero); 14098 const zero = try sema.addConstant(zero_val); 14099 const eql = try block.addCmpVector(remainder, zero, .eq); 14100 break :ok try block.addInst(.{ 14101 .tag = .reduce, 14102 .data = .{ .reduce = .{ 14103 .operand = eql, 14104 .operation = .And, 14105 } }, 14106 }); 14107 } else { 14108 const zero = try sema.addConstant(scalar_zero); 14109 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero); 14110 break :ok is_in_range; 14111 } 14112 }; 14113 try sema.addSafetyCheck(block, ok, .exact_division_remainder); 14114 return result; 14115 } 14116 14117 return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs); 14118 } 14119 14120 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14121 const mod = sema.mod; 14122 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 14123 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14124 sema.src = src; 14125 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14126 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14127 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14128 const lhs = try sema.resolveInst(extra.lhs); 14129 const rhs = try sema.resolveInst(extra.rhs); 14130 const lhs_ty = sema.typeOf(lhs); 14131 const rhs_ty = sema.typeOf(rhs); 14132 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14133 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14134 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14135 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14136 14137 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14138 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14139 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14140 }); 14141 14142 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14143 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14144 14145 const lhs_scalar_ty = lhs_ty.scalarType(mod); 14146 const rhs_scalar_ty = rhs_ty.scalarType(mod); 14147 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14148 14149 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14150 14151 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor); 14152 14153 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); 14154 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); 14155 14156 const runtime_src = rs: { 14157 // For integers: 14158 // If the lhs is zero, then zero is returned regardless of rhs. 14159 // If the rhs is zero, compile error for division by zero. 14160 // If the rhs is undefined, compile error because there is a possible 14161 // value (zero) for which the division would be illegal behavior. 14162 // If the lhs is undefined: 14163 // * if lhs type is signed: 14164 // * if rhs is comptime-known and not -1, result is undefined 14165 // * if rhs is -1 or runtime-known, compile error because there is a 14166 // possible value (-min_int / -1) for which division would be 14167 // illegal behavior. 14168 // * if lhs type is unsigned, undef is returned regardless of rhs. 14169 // TODO: emit runtime safety for division by zero 14170 // 14171 // For floats: 14172 // If the rhs is zero, compile error for division by zero. 14173 // If the rhs is undefined, compile error because there is a possible 14174 // value (zero) for which the division would be illegal behavior. 14175 // If the lhs is undefined, result is undefined. 14176 if (maybe_lhs_val) |lhs_val| { 14177 if (!lhs_val.isUndef(mod)) { 14178 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14179 const scalar_zero = switch (scalar_tag) { 14180 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14181 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14182 else => unreachable, 14183 }; 14184 const zero_val = try sema.splat(resolved_type, scalar_zero); 14185 return sema.addConstant(zero_val); 14186 } 14187 } 14188 } 14189 if (maybe_rhs_val) |rhs_val| { 14190 if (rhs_val.isUndef(mod)) { 14191 return sema.failWithUseOfUndef(block, rhs_src); 14192 } 14193 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14194 return sema.failWithDivideByZero(block, rhs_src); 14195 } 14196 // TODO: if the RHS is one, return the LHS directly 14197 } 14198 if (maybe_lhs_val) |lhs_val| { 14199 if (lhs_val.isUndef(mod)) { 14200 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { 14201 if (maybe_rhs_val) |rhs_val| { 14202 if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { 14203 return sema.addConstUndef(resolved_type); 14204 } 14205 } 14206 return sema.failWithUseOfUndef(block, rhs_src); 14207 } 14208 return sema.addConstUndef(resolved_type); 14209 } 14210 14211 if (maybe_rhs_val) |rhs_val| { 14212 if (is_int) { 14213 return sema.addConstant( 14214 try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, mod), 14215 ); 14216 } else { 14217 return sema.addConstant( 14218 try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, mod), 14219 ); 14220 } 14221 } else break :rs rhs_src; 14222 } else break :rs lhs_src; 14223 }; 14224 14225 try sema.requireRuntimeBlock(block, src, runtime_src); 14226 14227 if (block.wantSafety()) { 14228 try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14229 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14230 } 14231 14232 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); 14233 } 14234 14235 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14236 const mod = sema.mod; 14237 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 14238 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14239 sema.src = src; 14240 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14241 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14242 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14243 const lhs = try sema.resolveInst(extra.lhs); 14244 const rhs = try sema.resolveInst(extra.rhs); 14245 const lhs_ty = sema.typeOf(lhs); 14246 const rhs_ty = sema.typeOf(rhs); 14247 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14248 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14249 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14250 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14251 14252 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14253 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14254 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14255 }); 14256 14257 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14258 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14259 14260 const lhs_scalar_ty = lhs_ty.scalarType(mod); 14261 const rhs_scalar_ty = rhs_ty.scalarType(mod); 14262 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14263 14264 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14265 14266 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc); 14267 14268 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); 14269 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); 14270 14271 const runtime_src = rs: { 14272 // For integers: 14273 // If the lhs is zero, then zero is returned regardless of rhs. 14274 // If the rhs is zero, compile error for division by zero. 14275 // If the rhs is undefined, compile error because there is a possible 14276 // value (zero) for which the division would be illegal behavior. 14277 // If the lhs is undefined: 14278 // * if lhs type is signed: 14279 // * if rhs is comptime-known and not -1, result is undefined 14280 // * if rhs is -1 or runtime-known, compile error because there is a 14281 // possible value (-min_int / -1) for which division would be 14282 // illegal behavior. 14283 // * if lhs type is unsigned, undef is returned regardless of rhs. 14284 // TODO: emit runtime safety for division by zero 14285 // 14286 // For floats: 14287 // If the rhs is zero, compile error for division by zero. 14288 // If the rhs is undefined, compile error because there is a possible 14289 // value (zero) for which the division would be illegal behavior. 14290 // If the lhs is undefined, result is undefined. 14291 if (maybe_lhs_val) |lhs_val| { 14292 if (!lhs_val.isUndef(mod)) { 14293 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14294 const scalar_zero = switch (scalar_tag) { 14295 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14296 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14297 else => unreachable, 14298 }; 14299 const zero_val = try sema.splat(resolved_type, scalar_zero); 14300 return sema.addConstant(zero_val); 14301 } 14302 } 14303 } 14304 if (maybe_rhs_val) |rhs_val| { 14305 if (rhs_val.isUndef(mod)) { 14306 return sema.failWithUseOfUndef(block, rhs_src); 14307 } 14308 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14309 return sema.failWithDivideByZero(block, rhs_src); 14310 } 14311 } 14312 if (maybe_lhs_val) |lhs_val| { 14313 if (lhs_val.isUndef(mod)) { 14314 if (lhs_scalar_ty.isSignedInt(mod) and rhs_scalar_ty.isSignedInt(mod)) { 14315 if (maybe_rhs_val) |rhs_val| { 14316 if (try sema.compareAll(rhs_val, .neq, try mod.intValue(resolved_type, -1), resolved_type)) { 14317 return sema.addConstUndef(resolved_type); 14318 } 14319 } 14320 return sema.failWithUseOfUndef(block, rhs_src); 14321 } 14322 return sema.addConstUndef(resolved_type); 14323 } 14324 14325 if (maybe_rhs_val) |rhs_val| { 14326 if (is_int) { 14327 var overflow_idx: ?usize = null; 14328 const res = try lhs_val.intDiv(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); 14329 if (overflow_idx) |vec_idx| { 14330 return sema.failWithIntegerOverflow(block, src, resolved_type, res, vec_idx); 14331 } 14332 return sema.addConstant(res); 14333 } else { 14334 return sema.addConstant( 14335 try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, mod), 14336 ); 14337 } 14338 } else break :rs rhs_src; 14339 } else break :rs lhs_src; 14340 }; 14341 14342 try sema.requireRuntimeBlock(block, src, runtime_src); 14343 14344 if (block.wantSafety()) { 14345 try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); 14346 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14347 } 14348 14349 return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs); 14350 } 14351 14352 fn addDivIntOverflowSafety( 14353 sema: *Sema, 14354 block: *Block, 14355 resolved_type: Type, 14356 lhs_scalar_ty: Type, 14357 maybe_lhs_val: ?Value, 14358 maybe_rhs_val: ?Value, 14359 casted_lhs: Air.Inst.Ref, 14360 casted_rhs: Air.Inst.Ref, 14361 is_int: bool, 14362 ) CompileError!void { 14363 const mod = sema.mod; 14364 if (!is_int) return; 14365 14366 // If the LHS is unsigned, it cannot cause overflow. 14367 if (!lhs_scalar_ty.isSignedInt(mod)) return; 14368 14369 // If the LHS is widened to a larger integer type, no overflow is possible. 14370 if (lhs_scalar_ty.intInfo(mod).bits < resolved_type.intInfo(mod).bits) { 14371 return; 14372 } 14373 14374 const min_int = try resolved_type.minInt(mod, resolved_type); 14375 const neg_one_scalar = try mod.intValue(lhs_scalar_ty, -1); 14376 const neg_one = try sema.splat(resolved_type, neg_one_scalar); 14377 14378 // If the LHS is comptime-known to be not equal to the min int, 14379 // no overflow is possible. 14380 if (maybe_lhs_val) |lhs_val| { 14381 if (try lhs_val.compareAll(.neq, min_int, resolved_type, mod)) return; 14382 } 14383 14384 // If the RHS is comptime-known to not be equal to -1, no overflow is possible. 14385 if (maybe_rhs_val) |rhs_val| { 14386 if (try rhs_val.compareAll(.neq, neg_one, resolved_type, mod)) return; 14387 } 14388 14389 var ok: Air.Inst.Ref = .none; 14390 if (resolved_type.zigTypeTag(mod) == .Vector) { 14391 if (maybe_lhs_val == null) { 14392 const min_int_ref = try sema.addConstant(min_int); 14393 ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq); 14394 } 14395 if (maybe_rhs_val == null) { 14396 const neg_one_ref = try sema.addConstant(neg_one); 14397 const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq); 14398 if (ok == .none) { 14399 ok = rhs_ok; 14400 } else { 14401 ok = try block.addBinOp(.bool_or, ok, rhs_ok); 14402 } 14403 } 14404 assert(ok != .none); 14405 ok = try block.addInst(.{ 14406 .tag = .reduce, 14407 .data = .{ .reduce = .{ 14408 .operand = ok, 14409 .operation = .And, 14410 } }, 14411 }); 14412 } else { 14413 if (maybe_lhs_val == null) { 14414 const min_int_ref = try sema.addConstant(min_int); 14415 ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); 14416 } 14417 if (maybe_rhs_val == null) { 14418 const neg_one_ref = try sema.addConstant(neg_one); 14419 const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref); 14420 if (ok == .none) { 14421 ok = rhs_ok; 14422 } else { 14423 ok = try block.addBinOp(.bool_or, ok, rhs_ok); 14424 } 14425 } 14426 assert(ok != .none); 14427 } 14428 try sema.addSafetyCheck(block, ok, .integer_overflow); 14429 } 14430 14431 fn addDivByZeroSafety( 14432 sema: *Sema, 14433 block: *Block, 14434 resolved_type: Type, 14435 maybe_rhs_val: ?Value, 14436 casted_rhs: Air.Inst.Ref, 14437 is_int: bool, 14438 ) CompileError!void { 14439 // Strict IEEE floats have well-defined division by zero. 14440 if (!is_int and block.float_mode == .Strict) return; 14441 14442 // If rhs was comptime-known to be zero a compile error would have been 14443 // emitted above. 14444 if (maybe_rhs_val != null) return; 14445 14446 const mod = sema.mod; 14447 const scalar_zero = if (is_int) 14448 try mod.intValue(resolved_type.scalarType(mod), 0) 14449 else 14450 try mod.floatValue(resolved_type.scalarType(mod), 0.0); 14451 const ok = if (resolved_type.zigTypeTag(mod) == .Vector) ok: { 14452 const zero_val = try sema.splat(resolved_type, scalar_zero); 14453 const zero = try sema.addConstant(zero_val); 14454 const ok = try block.addCmpVector(casted_rhs, zero, .neq); 14455 break :ok try block.addInst(.{ 14456 .tag = if (is_int) .reduce else .reduce_optimized, 14457 .data = .{ .reduce = .{ 14458 .operand = ok, 14459 .operation = .And, 14460 } }, 14461 }); 14462 } else ok: { 14463 const zero = try sema.addConstant(scalar_zero); 14464 break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); 14465 }; 14466 try sema.addSafetyCheck(block, ok, .divide_by_zero); 14467 } 14468 14469 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag { 14470 if (is_int) return normal; 14471 return switch (block.float_mode) { 14472 .Strict => normal, 14473 .Optimized => optimized, 14474 }; 14475 } 14476 14477 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14478 const mod = sema.mod; 14479 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 14480 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14481 sema.src = src; 14482 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14483 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14484 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14485 const lhs = try sema.resolveInst(extra.lhs); 14486 const rhs = try sema.resolveInst(extra.rhs); 14487 const lhs_ty = sema.typeOf(lhs); 14488 const rhs_ty = sema.typeOf(rhs); 14489 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14490 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14491 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14492 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14493 14494 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14495 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14496 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14497 }); 14498 14499 const is_vector = resolved_type.zigTypeTag(mod) == .Vector; 14500 14501 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14502 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14503 14504 const lhs_scalar_ty = lhs_ty.scalarType(mod); 14505 const rhs_scalar_ty = rhs_ty.scalarType(mod); 14506 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14507 14508 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14509 14510 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem); 14511 14512 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); 14513 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); 14514 14515 const runtime_src = rs: { 14516 // For integers: 14517 // Either operand being undef is a compile error because there exists 14518 // a possible value (TODO what is it?) that would invoke illegal behavior. 14519 // TODO: can lhs undef be handled better? 14520 // 14521 // For floats: 14522 // If the rhs is zero, compile error for division by zero. 14523 // If the rhs is undefined, compile error because there is a possible 14524 // value (zero) for which the division would be illegal behavior. 14525 // If the lhs is undefined, result is undefined. 14526 // 14527 // For either one: if the result would be different between @mod and @rem, 14528 // then emit a compile error saying you have to pick one. 14529 if (is_int) { 14530 if (maybe_lhs_val) |lhs_val| { 14531 if (lhs_val.isUndef(mod)) { 14532 return sema.failWithUseOfUndef(block, lhs_src); 14533 } 14534 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14535 const scalar_zero = switch (scalar_tag) { 14536 .ComptimeFloat, .Float => try mod.floatValue(resolved_type.scalarType(mod), 0.0), 14537 .ComptimeInt, .Int => try mod.intValue(resolved_type.scalarType(mod), 0), 14538 else => unreachable, 14539 }; 14540 const zero_val = if (is_vector) (try mod.intern(.{ .aggregate = .{ 14541 .ty = resolved_type.toIntern(), 14542 .storage = .{ .repeated_elem = scalar_zero.toIntern() }, 14543 } })).toValue() else scalar_zero; 14544 return sema.addConstant(zero_val); 14545 } 14546 } else if (lhs_scalar_ty.isSignedInt(mod)) { 14547 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); 14548 } 14549 if (maybe_rhs_val) |rhs_val| { 14550 if (rhs_val.isUndef(mod)) { 14551 return sema.failWithUseOfUndef(block, rhs_src); 14552 } 14553 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14554 return sema.failWithDivideByZero(block, rhs_src); 14555 } 14556 if (!(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))) { 14557 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); 14558 } 14559 if (maybe_lhs_val) |lhs_val| { 14560 const rem_result = try sema.intRem(resolved_type, lhs_val, rhs_val); 14561 // If this answer could possibly be different by doing `intMod`, 14562 // we must emit a compile error. Otherwise, it's OK. 14563 if (!(try lhs_val.compareAllWithZeroAdvanced(.gte, sema)) and 14564 !(try rem_result.compareAllWithZeroAdvanced(.eq, sema))) 14565 { 14566 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); 14567 } 14568 return sema.addConstant(rem_result); 14569 } 14570 break :rs lhs_src; 14571 } else if (rhs_scalar_ty.isSignedInt(mod)) { 14572 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); 14573 } else { 14574 break :rs rhs_src; 14575 } 14576 } 14577 // float operands 14578 if (maybe_rhs_val) |rhs_val| { 14579 if (rhs_val.isUndef(mod)) { 14580 return sema.failWithUseOfUndef(block, rhs_src); 14581 } 14582 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14583 return sema.failWithDivideByZero(block, rhs_src); 14584 } 14585 if (!(try rhs_val.compareAllWithZeroAdvanced(.gte, sema))) { 14586 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); 14587 } 14588 if (maybe_lhs_val) |lhs_val| { 14589 if (lhs_val.isUndef(mod) or !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema))) { 14590 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); 14591 } 14592 return sema.addConstant( 14593 try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod), 14594 ); 14595 } else { 14596 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); 14597 } 14598 } else { 14599 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); 14600 } 14601 }; 14602 14603 try sema.requireRuntimeBlock(block, src, runtime_src); 14604 14605 if (block.wantSafety()) { 14606 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14607 } 14608 14609 const air_tag = airTag(block, is_int, .rem, .rem_optimized); 14610 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 14611 } 14612 14613 fn intRem( 14614 sema: *Sema, 14615 ty: Type, 14616 lhs: Value, 14617 rhs: Value, 14618 ) CompileError!Value { 14619 const mod = sema.mod; 14620 if (ty.zigTypeTag(mod) == .Vector) { 14621 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 14622 const scalar_ty = ty.scalarType(mod); 14623 for (result_data, 0..) |*scalar, i| { 14624 const lhs_elem = try lhs.elemValue(mod, i); 14625 const rhs_elem = try rhs.elemValue(mod, i); 14626 scalar.* = try (try sema.intRemScalar(lhs_elem, rhs_elem, scalar_ty)).intern(scalar_ty, mod); 14627 } 14628 return (try mod.intern(.{ .aggregate = .{ 14629 .ty = ty.toIntern(), 14630 .storage = .{ .elems = result_data }, 14631 } })).toValue(); 14632 } 14633 return sema.intRemScalar(lhs, rhs, ty); 14634 } 14635 14636 fn intRemScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) CompileError!Value { 14637 const mod = sema.mod; 14638 // TODO is this a performance issue? maybe we should try the operation without 14639 // resorting to BigInt first. 14640 var lhs_space: Value.BigIntSpace = undefined; 14641 var rhs_space: Value.BigIntSpace = undefined; 14642 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 14643 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 14644 const limbs_q = try sema.arena.alloc( 14645 math.big.Limb, 14646 lhs_bigint.limbs.len, 14647 ); 14648 const limbs_r = try sema.arena.alloc( 14649 math.big.Limb, 14650 // TODO: consider reworking Sema to re-use Values rather than 14651 // always producing new Value objects. 14652 rhs_bigint.limbs.len, 14653 ); 14654 const limbs_buffer = try sema.arena.alloc( 14655 math.big.Limb, 14656 math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), 14657 ); 14658 var result_q = math.big.int.Mutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; 14659 var result_r = math.big.int.Mutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; 14660 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); 14661 return mod.intValue_big(scalar_ty, result_r.toConst()); 14662 } 14663 14664 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14665 const mod = sema.mod; 14666 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 14667 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14668 sema.src = src; 14669 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14670 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14671 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14672 const lhs = try sema.resolveInst(extra.lhs); 14673 const rhs = try sema.resolveInst(extra.rhs); 14674 const lhs_ty = sema.typeOf(lhs); 14675 const rhs_ty = sema.typeOf(rhs); 14676 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14677 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14678 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14679 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14680 14681 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14682 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14683 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14684 }); 14685 14686 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14687 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14688 14689 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14690 14691 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14692 14693 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod); 14694 14695 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); 14696 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); 14697 14698 const runtime_src = rs: { 14699 // For integers: 14700 // Either operand being undef is a compile error because there exists 14701 // a possible value (TODO what is it?) that would invoke illegal behavior. 14702 // TODO: can lhs zero be handled better? 14703 // TODO: can lhs undef be handled better? 14704 // 14705 // For floats: 14706 // If the rhs is zero, compile error for division by zero. 14707 // If the rhs is undefined, compile error because there is a possible 14708 // value (zero) for which the division would be illegal behavior. 14709 // If the lhs is undefined, result is undefined. 14710 if (is_int) { 14711 if (maybe_lhs_val) |lhs_val| { 14712 if (lhs_val.isUndef(mod)) { 14713 return sema.failWithUseOfUndef(block, lhs_src); 14714 } 14715 } 14716 if (maybe_rhs_val) |rhs_val| { 14717 if (rhs_val.isUndef(mod)) { 14718 return sema.failWithUseOfUndef(block, rhs_src); 14719 } 14720 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14721 return sema.failWithDivideByZero(block, rhs_src); 14722 } 14723 if (maybe_lhs_val) |lhs_val| { 14724 return sema.addConstant( 14725 try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod), 14726 ); 14727 } 14728 break :rs lhs_src; 14729 } else { 14730 break :rs rhs_src; 14731 } 14732 } 14733 // float operands 14734 if (maybe_rhs_val) |rhs_val| { 14735 if (rhs_val.isUndef(mod)) { 14736 return sema.failWithUseOfUndef(block, rhs_src); 14737 } 14738 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14739 return sema.failWithDivideByZero(block, rhs_src); 14740 } 14741 } 14742 if (maybe_lhs_val) |lhs_val| { 14743 if (lhs_val.isUndef(mod)) { 14744 return sema.addConstUndef(resolved_type); 14745 } 14746 if (maybe_rhs_val) |rhs_val| { 14747 return sema.addConstant( 14748 try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod), 14749 ); 14750 } else break :rs rhs_src; 14751 } else break :rs lhs_src; 14752 }; 14753 14754 try sema.requireRuntimeBlock(block, src, runtime_src); 14755 14756 if (block.wantSafety()) { 14757 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14758 } 14759 14760 const air_tag = airTag(block, is_int, .mod, .mod_optimized); 14761 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 14762 } 14763 14764 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 14765 const mod = sema.mod; 14766 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 14767 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 14768 sema.src = src; 14769 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 14770 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 14771 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 14772 const lhs = try sema.resolveInst(extra.lhs); 14773 const rhs = try sema.resolveInst(extra.rhs); 14774 const lhs_ty = sema.typeOf(lhs); 14775 const rhs_ty = sema.typeOf(rhs); 14776 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 14777 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 14778 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14779 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty); 14780 14781 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 14782 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 14783 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14784 }); 14785 14786 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 14787 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 14788 14789 const scalar_tag = resolved_type.scalarType(mod).zigTypeTag(mod); 14790 14791 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 14792 14793 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem); 14794 14795 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); 14796 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); 14797 14798 const runtime_src = rs: { 14799 // For integers: 14800 // Either operand being undef is a compile error because there exists 14801 // a possible value (TODO what is it?) that would invoke illegal behavior. 14802 // TODO: can lhs zero be handled better? 14803 // TODO: can lhs undef be handled better? 14804 // 14805 // For floats: 14806 // If the rhs is zero, compile error for division by zero. 14807 // If the rhs is undefined, compile error because there is a possible 14808 // value (zero) for which the division would be illegal behavior. 14809 // If the lhs is undefined, result is undefined. 14810 if (is_int) { 14811 if (maybe_lhs_val) |lhs_val| { 14812 if (lhs_val.isUndef(mod)) { 14813 return sema.failWithUseOfUndef(block, lhs_src); 14814 } 14815 } 14816 if (maybe_rhs_val) |rhs_val| { 14817 if (rhs_val.isUndef(mod)) { 14818 return sema.failWithUseOfUndef(block, rhs_src); 14819 } 14820 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14821 return sema.failWithDivideByZero(block, rhs_src); 14822 } 14823 if (maybe_lhs_val) |lhs_val| { 14824 return sema.addConstant( 14825 try sema.intRem(resolved_type, lhs_val, rhs_val), 14826 ); 14827 } 14828 break :rs lhs_src; 14829 } else { 14830 break :rs rhs_src; 14831 } 14832 } 14833 // float operands 14834 if (maybe_rhs_val) |rhs_val| { 14835 if (rhs_val.isUndef(mod)) { 14836 return sema.failWithUseOfUndef(block, rhs_src); 14837 } 14838 if (!(try rhs_val.compareAllWithZeroAdvanced(.neq, sema))) { 14839 return sema.failWithDivideByZero(block, rhs_src); 14840 } 14841 } 14842 if (maybe_lhs_val) |lhs_val| { 14843 if (lhs_val.isUndef(mod)) { 14844 return sema.addConstUndef(resolved_type); 14845 } 14846 if (maybe_rhs_val) |rhs_val| { 14847 return sema.addConstant( 14848 try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod), 14849 ); 14850 } else break :rs rhs_src; 14851 } else break :rs lhs_src; 14852 }; 14853 14854 try sema.requireRuntimeBlock(block, src, runtime_src); 14855 14856 if (block.wantSafety()) { 14857 try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); 14858 } 14859 14860 const air_tag = airTag(block, is_int, .rem, .rem_optimized); 14861 return block.addBinOp(air_tag, casted_lhs, casted_rhs); 14862 } 14863 14864 fn zirOverflowArithmetic( 14865 sema: *Sema, 14866 block: *Block, 14867 extended: Zir.Inst.Extended.InstData, 14868 zir_tag: Zir.Inst.Extended, 14869 ) CompileError!Air.Inst.Ref { 14870 const tracy = trace(@src()); 14871 defer tracy.end(); 14872 14873 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 14874 const src = LazySrcLoc.nodeOffset(extra.node); 14875 14876 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 14877 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 14878 14879 const uncasted_lhs = try sema.resolveInst(extra.lhs); 14880 const uncasted_rhs = try sema.resolveInst(extra.rhs); 14881 14882 const lhs_ty = sema.typeOf(uncasted_lhs); 14883 const rhs_ty = sema.typeOf(uncasted_rhs); 14884 const mod = sema.mod; 14885 14886 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 14887 14888 const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs }; 14889 const dest_ty = if (zir_tag == .shl_with_overflow) 14890 lhs_ty 14891 else 14892 try sema.resolvePeerTypes(block, src, instructions, .{ 14893 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 14894 }); 14895 14896 const rhs_dest_ty = if (zir_tag == .shl_with_overflow) 14897 try sema.log2IntType(block, lhs_ty, src) 14898 else 14899 dest_ty; 14900 14901 const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src); 14902 const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src); 14903 14904 if (dest_ty.scalarType(mod).zigTypeTag(mod) != .Int) { 14905 return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)}); 14906 } 14907 14908 const maybe_lhs_val = try sema.resolveMaybeUndefVal(lhs); 14909 const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs); 14910 14911 const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty); 14912 const overflow_ty = mod.intern_pool.indexToKey(tuple_ty.toIntern()).anon_struct_type.types[1].toType(); 14913 14914 var result: struct { 14915 inst: Air.Inst.Ref = .none, 14916 wrapped: Value = Value.@"unreachable", 14917 overflow_bit: Value, 14918 } = result: { 14919 const zero_bit = try mod.intValue(Type.u1, 0); 14920 switch (zir_tag) { 14921 .add_with_overflow => { 14922 // If either of the arguments is zero, `false` is returned and the other is stored 14923 // to the result, even if it is undefined.. 14924 // Otherwise, if either of the argument is undefined, undefined is returned. 14925 if (maybe_lhs_val) |lhs_val| { 14926 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 14927 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; 14928 } 14929 } 14930 if (maybe_rhs_val) |rhs_val| { 14931 if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 14932 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 14933 } 14934 } 14935 if (maybe_lhs_val) |lhs_val| { 14936 if (maybe_rhs_val) |rhs_val| { 14937 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 14938 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 14939 } 14940 14941 const result = try sema.intAddWithOverflow(lhs_val, rhs_val, dest_ty); 14942 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 14943 } 14944 } 14945 }, 14946 .sub_with_overflow => { 14947 // If the rhs is zero, then the result is lhs and no overflow occured. 14948 // Otherwise, if either result is undefined, both results are undefined. 14949 if (maybe_rhs_val) |rhs_val| { 14950 if (rhs_val.isUndef(mod)) { 14951 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 14952 } else if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14953 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 14954 } else if (maybe_lhs_val) |lhs_val| { 14955 if (lhs_val.isUndef(mod)) { 14956 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 14957 } 14958 14959 const result = try sema.intSubWithOverflow(lhs_val, rhs_val, dest_ty); 14960 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 14961 } 14962 } 14963 }, 14964 .mul_with_overflow => { 14965 // If either of the arguments is zero, the result is zero and no overflow occured. 14966 // If either of the arguments is one, the result is the other and no overflow occured. 14967 // Otherwise, if either of the arguments is undefined, both results are undefined. 14968 const scalar_one = try mod.intValue(dest_ty.scalarType(mod), 1); 14969 if (maybe_lhs_val) |lhs_val| { 14970 if (!lhs_val.isUndef(mod)) { 14971 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14972 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 14973 } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { 14974 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; 14975 } 14976 } 14977 } 14978 14979 if (maybe_rhs_val) |rhs_val| { 14980 if (!rhs_val.isUndef(mod)) { 14981 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 14982 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = rhs }; 14983 } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) { 14984 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 14985 } 14986 } 14987 } 14988 14989 if (maybe_lhs_val) |lhs_val| { 14990 if (maybe_rhs_val) |rhs_val| { 14991 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 14992 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 14993 } 14994 14995 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, mod); 14996 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 14997 } 14998 } 14999 }, 15000 .shl_with_overflow => { 15001 // If lhs is zero, the result is zero and no overflow occurred. 15002 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred. 15003 // Oterhwise if either of the arguments is undefined, both results are undefined. 15004 if (maybe_lhs_val) |lhs_val| { 15005 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15006 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 15007 } 15008 } 15009 if (maybe_rhs_val) |rhs_val| { 15010 if (!rhs_val.isUndef(mod) and (try rhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15011 break :result .{ .overflow_bit = try sema.splat(overflow_ty, zero_bit), .inst = lhs }; 15012 } 15013 } 15014 if (maybe_lhs_val) |lhs_val| { 15015 if (maybe_rhs_val) |rhs_val| { 15016 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 15017 break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef }; 15018 } 15019 15020 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, mod); 15021 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; 15022 } 15023 } 15024 }, 15025 else => unreachable, 15026 } 15027 15028 const air_tag: Air.Inst.Tag = switch (zir_tag) { 15029 .add_with_overflow => .add_with_overflow, 15030 .mul_with_overflow => .mul_with_overflow, 15031 .sub_with_overflow => .sub_with_overflow, 15032 .shl_with_overflow => .shl_with_overflow, 15033 else => unreachable, 15034 }; 15035 15036 const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src; 15037 try sema.requireRuntimeBlock(block, src, runtime_src); 15038 15039 return block.addInst(.{ 15040 .tag = air_tag, 15041 .data = .{ .ty_pl = .{ 15042 .ty = try block.sema.addType(tuple_ty), 15043 .payload = try block.sema.addExtra(Air.Bin{ 15044 .lhs = lhs, 15045 .rhs = rhs, 15046 }), 15047 } }, 15048 }); 15049 }; 15050 15051 if (result.inst != .none) { 15052 if (try sema.resolveMaybeUndefVal(result.inst)) |some| { 15053 result.wrapped = some; 15054 result.inst = .none; 15055 } 15056 } 15057 15058 if (result.inst == .none) { 15059 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 15060 .ty = tuple_ty.toIntern(), 15061 .storage = .{ .elems = &.{ 15062 result.wrapped.toIntern(), 15063 result.overflow_bit.toIntern(), 15064 } }, 15065 } })).toValue()); 15066 } 15067 15068 const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2); 15069 element_refs[0] = result.inst; 15070 element_refs[1] = try sema.addConstant(result.overflow_bit); 15071 return block.addAggregateInit(tuple_ty, element_refs); 15072 } 15073 15074 fn splat(sema: *Sema, ty: Type, val: Value) !Value { 15075 const mod = sema.mod; 15076 if (ty.zigTypeTag(mod) != .Vector) return val; 15077 const repeated = try mod.intern(.{ .aggregate = .{ 15078 .ty = ty.toIntern(), 15079 .storage = .{ .repeated_elem = val.toIntern() }, 15080 } }); 15081 return repeated.toValue(); 15082 } 15083 15084 fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { 15085 const mod = sema.mod; 15086 const ov_ty = if (ty.zigTypeTag(mod) == .Vector) try mod.vectorType(.{ 15087 .len = ty.vectorLen(mod), 15088 .child = .u1_type, 15089 }) else Type.u1; 15090 15091 const types = [2]InternPool.Index{ ty.toIntern(), ov_ty.toIntern() }; 15092 const values = [2]InternPool.Index{ .none, .none }; 15093 const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ 15094 .types = &types, 15095 .values = &values, 15096 .names = &.{}, 15097 } }); 15098 return tuple_ty.toType(); 15099 } 15100 15101 fn analyzeArithmetic( 15102 sema: *Sema, 15103 block: *Block, 15104 /// TODO performance investigation: make this comptime? 15105 zir_tag: Zir.Inst.Tag, 15106 lhs: Air.Inst.Ref, 15107 rhs: Air.Inst.Ref, 15108 src: LazySrcLoc, 15109 lhs_src: LazySrcLoc, 15110 rhs_src: LazySrcLoc, 15111 want_safety: bool, 15112 ) CompileError!Air.Inst.Ref { 15113 const mod = sema.mod; 15114 const lhs_ty = sema.typeOf(lhs); 15115 const rhs_ty = sema.typeOf(rhs); 15116 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 15117 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 15118 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 15119 15120 if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize(mod)) { 15121 .One, .Slice => {}, 15122 .Many, .C => { 15123 const air_tag: Air.Inst.Tag = switch (zir_tag) { 15124 .add => .ptr_add, 15125 .sub => .ptr_sub, 15126 else => return sema.fail(block, src, "invalid pointer arithmetic operator", .{}), 15127 }; 15128 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); 15129 }, 15130 }; 15131 15132 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 15133 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ 15134 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 15135 }); 15136 15137 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 15138 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 15139 15140 const scalar_type = resolved_type.scalarType(mod); 15141 const scalar_tag = scalar_type.zigTypeTag(mod); 15142 15143 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 15144 15145 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag); 15146 15147 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); 15148 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); 15149 const rs: struct { 15150 src: LazySrcLoc, 15151 air_tag: Air.Inst.Tag, 15152 air_tag_safe: Air.Inst.Tag, 15153 } = rs: { 15154 switch (zir_tag) { 15155 .add, .add_unsafe => { 15156 // For integers:intAddSat 15157 // If either of the operands are zero, then the other operand is 15158 // returned, even if it is undefined. 15159 // If either of the operands are undefined, it's a compile error 15160 // because there is a possible value for which the addition would 15161 // overflow (max_int), causing illegal behavior. 15162 // For floats: either operand being undef makes the result undef. 15163 if (maybe_lhs_val) |lhs_val| { 15164 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15165 return casted_rhs; 15166 } 15167 } 15168 if (maybe_rhs_val) |rhs_val| { 15169 if (rhs_val.isUndef(mod)) { 15170 if (is_int) { 15171 return sema.failWithUseOfUndef(block, rhs_src); 15172 } else { 15173 return sema.addConstUndef(resolved_type); 15174 } 15175 } 15176 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15177 return casted_lhs; 15178 } 15179 } 15180 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .add_optimized else .add; 15181 if (maybe_lhs_val) |lhs_val| { 15182 if (lhs_val.isUndef(mod)) { 15183 if (is_int) { 15184 return sema.failWithUseOfUndef(block, lhs_src); 15185 } else { 15186 return sema.addConstUndef(resolved_type); 15187 } 15188 } 15189 if (maybe_rhs_val) |rhs_val| { 15190 if (is_int) { 15191 var overflow_idx: ?usize = null; 15192 const sum = try sema.intAdd(lhs_val, rhs_val, resolved_type, &overflow_idx); 15193 if (overflow_idx) |vec_idx| { 15194 return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vec_idx); 15195 } 15196 return sema.addConstant(sum); 15197 } else { 15198 return sema.addConstant( 15199 try Value.floatAdd(lhs_val, rhs_val, resolved_type, sema.arena, mod), 15200 ); 15201 } 15202 } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .add_safe }; 15203 } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .add_safe }; 15204 }, 15205 .addwrap => { 15206 // Integers only; floats are checked above. 15207 // If either of the operands are zero, the other operand is returned. 15208 // If either of the operands are undefined, the result is undefined. 15209 if (maybe_lhs_val) |lhs_val| { 15210 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15211 return casted_rhs; 15212 } 15213 } 15214 if (maybe_rhs_val) |rhs_val| { 15215 if (rhs_val.isUndef(mod)) { 15216 return sema.addConstUndef(resolved_type); 15217 } 15218 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15219 return casted_lhs; 15220 } 15221 if (maybe_lhs_val) |lhs_val| { 15222 return sema.addConstant( 15223 try sema.numberAddWrapScalar(lhs_val, rhs_val, resolved_type), 15224 ); 15225 } else break :rs .{ .src = lhs_src, .air_tag = .add_wrap, .air_tag_safe = .add_wrap }; 15226 } else break :rs .{ .src = rhs_src, .air_tag = .add_wrap, .air_tag_safe = .add_wrap }; 15227 }, 15228 .add_sat => { 15229 // Integers only; floats are checked above. 15230 // If either of the operands are zero, then the other operand is returned. 15231 // If either of the operands are undefined, the result is undefined. 15232 if (maybe_lhs_val) |lhs_val| { 15233 if (!lhs_val.isUndef(mod) and (try lhs_val.compareAllWithZeroAdvanced(.eq, sema))) { 15234 return casted_rhs; 15235 } 15236 } 15237 if (maybe_rhs_val) |rhs_val| { 15238 if (rhs_val.isUndef(mod)) { 15239 return sema.addConstUndef(resolved_type); 15240 } 15241 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15242 return casted_lhs; 15243 } 15244 if (maybe_lhs_val) |lhs_val| { 15245 const val = if (scalar_tag == .ComptimeInt) 15246 try sema.intAdd(lhs_val, rhs_val, resolved_type, undefined) 15247 else 15248 try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, mod); 15249 15250 return sema.addConstant(val); 15251 } else break :rs .{ 15252 .src = lhs_src, 15253 .air_tag = .add_sat, 15254 .air_tag_safe = .add_sat, 15255 }; 15256 } else break :rs .{ 15257 .src = rhs_src, 15258 .air_tag = .add_sat, 15259 .air_tag_safe = .add_sat, 15260 }; 15261 }, 15262 .sub => { 15263 // For integers: 15264 // If the rhs is zero, then the other operand is 15265 // returned, even if it is undefined. 15266 // If either of the operands are undefined, it's a compile error 15267 // because there is a possible value for which the subtraction would 15268 // overflow, causing illegal behavior. 15269 // For floats: either operand being undef makes the result undef. 15270 if (maybe_rhs_val) |rhs_val| { 15271 if (rhs_val.isUndef(mod)) { 15272 if (is_int) { 15273 return sema.failWithUseOfUndef(block, rhs_src); 15274 } else { 15275 return sema.addConstUndef(resolved_type); 15276 } 15277 } 15278 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15279 return casted_lhs; 15280 } 15281 } 15282 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .sub_optimized else .sub; 15283 if (maybe_lhs_val) |lhs_val| { 15284 if (lhs_val.isUndef(mod)) { 15285 if (is_int) { 15286 return sema.failWithUseOfUndef(block, lhs_src); 15287 } else { 15288 return sema.addConstUndef(resolved_type); 15289 } 15290 } 15291 if (maybe_rhs_val) |rhs_val| { 15292 if (is_int) { 15293 var overflow_idx: ?usize = null; 15294 const diff = try sema.intSub(lhs_val, rhs_val, resolved_type, &overflow_idx); 15295 if (overflow_idx) |vec_idx| { 15296 return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vec_idx); 15297 } 15298 return sema.addConstant(diff); 15299 } else { 15300 return sema.addConstant( 15301 try Value.floatSub(lhs_val, rhs_val, resolved_type, sema.arena, mod), 15302 ); 15303 } 15304 } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .sub_safe }; 15305 } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .sub_safe }; 15306 }, 15307 .subwrap => { 15308 // Integers only; floats are checked above. 15309 // If the RHS is zero, then the other operand is returned, even if it is undefined. 15310 // If either of the operands are undefined, the result is undefined. 15311 if (maybe_rhs_val) |rhs_val| { 15312 if (rhs_val.isUndef(mod)) { 15313 return sema.addConstUndef(resolved_type); 15314 } 15315 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15316 return casted_lhs; 15317 } 15318 } 15319 if (maybe_lhs_val) |lhs_val| { 15320 if (lhs_val.isUndef(mod)) { 15321 return sema.addConstUndef(resolved_type); 15322 } 15323 if (maybe_rhs_val) |rhs_val| { 15324 return sema.addConstant( 15325 try sema.numberSubWrapScalar(lhs_val, rhs_val, resolved_type), 15326 ); 15327 } else break :rs .{ .src = rhs_src, .air_tag = .sub_wrap, .air_tag_safe = .sub_wrap }; 15328 } else break :rs .{ .src = lhs_src, .air_tag = .sub_wrap, .air_tag_safe = .sub_wrap }; 15329 }, 15330 .sub_sat => { 15331 // Integers only; floats are checked above. 15332 // If the RHS is zero, result is LHS. 15333 // If either of the operands are undefined, result is undefined. 15334 if (maybe_rhs_val) |rhs_val| { 15335 if (rhs_val.isUndef(mod)) { 15336 return sema.addConstUndef(resolved_type); 15337 } 15338 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15339 return casted_lhs; 15340 } 15341 } 15342 if (maybe_lhs_val) |lhs_val| { 15343 if (lhs_val.isUndef(mod)) { 15344 return sema.addConstUndef(resolved_type); 15345 } 15346 if (maybe_rhs_val) |rhs_val| { 15347 const val = if (scalar_tag == .ComptimeInt) 15348 try sema.intSub(lhs_val, rhs_val, resolved_type, undefined) 15349 else 15350 try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod); 15351 15352 return sema.addConstant(val); 15353 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat, .air_tag_safe = .sub_sat }; 15354 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat, .air_tag_safe = .sub_sat }; 15355 }, 15356 .mul => { 15357 // For integers: 15358 // If either of the operands are zero, the result is zero. 15359 // If either of the operands are one, the result is the other 15360 // operand, even if it is undefined. 15361 // If either of the operands are undefined, it's a compile error 15362 // because there is a possible value for which the addition would 15363 // overflow (max_int), causing illegal behavior. 15364 // For floats: either operand being undef makes the result undef. 15365 // If either of the operands are inf, and the other operand is zero, 15366 // the result is nan. 15367 // If either of the operands are nan, the result is nan. 15368 const scalar_zero = switch (scalar_tag) { 15369 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), 15370 .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), 15371 else => unreachable, 15372 }; 15373 const scalar_one = switch (scalar_tag) { 15374 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), 15375 .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), 15376 else => unreachable, 15377 }; 15378 if (maybe_lhs_val) |lhs_val| { 15379 if (!lhs_val.isUndef(mod)) { 15380 if (lhs_val.isNan(mod)) { 15381 return sema.addConstant(lhs_val); 15382 } 15383 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) lz: { 15384 if (maybe_rhs_val) |rhs_val| { 15385 if (rhs_val.isNan(mod)) { 15386 return sema.addConstant(rhs_val); 15387 } 15388 if (rhs_val.isInf(mod)) { 15389 return sema.addConstant( 15390 try mod.floatValue(resolved_type, std.math.nan_f128), 15391 ); 15392 } 15393 } else if (resolved_type.isAnyFloat()) { 15394 break :lz; 15395 } 15396 const zero_val = try sema.splat(resolved_type, scalar_zero); 15397 return sema.addConstant(zero_val); 15398 } 15399 if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15400 return casted_rhs; 15401 } 15402 } 15403 } 15404 const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .mul_optimized else .mul; 15405 if (maybe_rhs_val) |rhs_val| { 15406 if (rhs_val.isUndef(mod)) { 15407 if (is_int) { 15408 return sema.failWithUseOfUndef(block, rhs_src); 15409 } else { 15410 return sema.addConstUndef(resolved_type); 15411 } 15412 } 15413 if (rhs_val.isNan(mod)) { 15414 return sema.addConstant(rhs_val); 15415 } 15416 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) rz: { 15417 if (maybe_lhs_val) |lhs_val| { 15418 if (lhs_val.isInf(mod)) { 15419 return sema.addConstant( 15420 try mod.floatValue(resolved_type, std.math.nan_f128), 15421 ); 15422 } 15423 } else if (resolved_type.isAnyFloat()) { 15424 break :rz; 15425 } 15426 const zero_val = try sema.splat(resolved_type, scalar_zero); 15427 return sema.addConstant(zero_val); 15428 } 15429 if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15430 return casted_lhs; 15431 } 15432 if (maybe_lhs_val) |lhs_val| { 15433 if (lhs_val.isUndef(mod)) { 15434 if (is_int) { 15435 return sema.failWithUseOfUndef(block, lhs_src); 15436 } else { 15437 return sema.addConstUndef(resolved_type); 15438 } 15439 } 15440 if (is_int) { 15441 var overflow_idx: ?usize = null; 15442 const product = try lhs_val.intMul(rhs_val, resolved_type, &overflow_idx, sema.arena, mod); 15443 if (overflow_idx) |vec_idx| { 15444 return sema.failWithIntegerOverflow(block, src, resolved_type, product, vec_idx); 15445 } 15446 return sema.addConstant(product); 15447 } else { 15448 return sema.addConstant( 15449 try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, mod), 15450 ); 15451 } 15452 } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .mul_safe }; 15453 } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .mul_safe }; 15454 }, 15455 .mulwrap => { 15456 // Integers only; floats are handled above. 15457 // If either of the operands are zero, result is zero. 15458 // If either of the operands are one, result is the other operand. 15459 // If either of the operands are undefined, result is undefined. 15460 const scalar_zero = switch (scalar_tag) { 15461 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), 15462 .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), 15463 else => unreachable, 15464 }; 15465 const scalar_one = switch (scalar_tag) { 15466 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), 15467 .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), 15468 else => unreachable, 15469 }; 15470 if (maybe_lhs_val) |lhs_val| { 15471 if (!lhs_val.isUndef(mod)) { 15472 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15473 const zero_val = try sema.splat(resolved_type, scalar_zero); 15474 return sema.addConstant(zero_val); 15475 } 15476 if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15477 return casted_rhs; 15478 } 15479 } 15480 } 15481 if (maybe_rhs_val) |rhs_val| { 15482 if (rhs_val.isUndef(mod)) { 15483 return sema.addConstUndef(resolved_type); 15484 } 15485 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15486 const zero_val = try sema.splat(resolved_type, scalar_zero); 15487 return sema.addConstant(zero_val); 15488 } 15489 if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15490 return casted_lhs; 15491 } 15492 if (maybe_lhs_val) |lhs_val| { 15493 if (lhs_val.isUndef(mod)) { 15494 return sema.addConstUndef(resolved_type); 15495 } 15496 return sema.addConstant( 15497 try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, mod), 15498 ); 15499 } else break :rs .{ .src = lhs_src, .air_tag = .mul_wrap, .air_tag_safe = .mul_wrap }; 15500 } else break :rs .{ .src = rhs_src, .air_tag = .mul_wrap, .air_tag_safe = .mul_wrap }; 15501 }, 15502 .mul_sat => { 15503 // Integers only; floats are checked above. 15504 // If either of the operands are zero, result is zero. 15505 // If either of the operands are one, result is the other operand. 15506 // If either of the operands are undefined, result is undefined. 15507 const scalar_zero = switch (scalar_tag) { 15508 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 0.0), 15509 .ComptimeInt, .Int => try mod.intValue(scalar_type, 0), 15510 else => unreachable, 15511 }; 15512 const scalar_one = switch (scalar_tag) { 15513 .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0), 15514 .ComptimeInt, .Int => try mod.intValue(scalar_type, 1), 15515 else => unreachable, 15516 }; 15517 if (maybe_lhs_val) |lhs_val| { 15518 if (!lhs_val.isUndef(mod)) { 15519 if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15520 const zero_val = try sema.splat(resolved_type, scalar_zero); 15521 return sema.addConstant(zero_val); 15522 } 15523 if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15524 return casted_rhs; 15525 } 15526 } 15527 } 15528 if (maybe_rhs_val) |rhs_val| { 15529 if (rhs_val.isUndef(mod)) { 15530 return sema.addConstUndef(resolved_type); 15531 } 15532 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) { 15533 const zero_val = try sema.splat(resolved_type, scalar_zero); 15534 return sema.addConstant(zero_val); 15535 } 15536 if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) { 15537 return casted_lhs; 15538 } 15539 if (maybe_lhs_val) |lhs_val| { 15540 if (lhs_val.isUndef(mod)) { 15541 return sema.addConstUndef(resolved_type); 15542 } 15543 15544 const val = if (scalar_tag == .ComptimeInt) 15545 try lhs_val.intMul(rhs_val, resolved_type, undefined, sema.arena, mod) 15546 else 15547 try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, mod); 15548 15549 return sema.addConstant(val); 15550 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat, .air_tag_safe = .mul_sat }; 15551 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat, .air_tag_safe = .mul_sat }; 15552 }, 15553 else => unreachable, 15554 } 15555 }; 15556 15557 try sema.requireRuntimeBlock(block, src, rs.src); 15558 if (block.wantSafety() and want_safety and scalar_tag == .Int) { 15559 if (mod.backendSupportsFeature(.safety_checked_instructions)) { 15560 _ = try sema.preparePanicId(block, .integer_overflow); 15561 return block.addBinOp(rs.air_tag_safe, casted_lhs, casted_rhs); 15562 } else { 15563 const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) { 15564 .add => .add_with_overflow, 15565 .sub => .sub_with_overflow, 15566 .mul => .mul_with_overflow, 15567 else => null, 15568 }; 15569 if (maybe_op_ov) |op_ov_tag| { 15570 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(resolved_type); 15571 const op_ov = try block.addInst(.{ 15572 .tag = op_ov_tag, 15573 .data = .{ .ty_pl = .{ 15574 .ty = try sema.addType(op_ov_tuple_ty), 15575 .payload = try sema.addExtra(Air.Bin{ 15576 .lhs = casted_lhs, 15577 .rhs = casted_rhs, 15578 }), 15579 } }, 15580 }); 15581 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); 15582 const any_ov_bit = if (resolved_type.zigTypeTag(mod) == .Vector) 15583 try block.addInst(.{ 15584 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 15585 .data = .{ .reduce = .{ 15586 .operand = ov_bit, 15587 .operation = .Or, 15588 } }, 15589 }) 15590 else 15591 ov_bit; 15592 const zero_ov = try sema.addConstant(try mod.intValue(Type.u1, 0)); 15593 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); 15594 15595 try sema.addSafetyCheck(block, no_ov, .integer_overflow); 15596 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); 15597 } 15598 } 15599 } 15600 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); 15601 } 15602 15603 fn analyzePtrArithmetic( 15604 sema: *Sema, 15605 block: *Block, 15606 op_src: LazySrcLoc, 15607 ptr: Air.Inst.Ref, 15608 uncasted_offset: Air.Inst.Ref, 15609 air_tag: Air.Inst.Tag, 15610 ptr_src: LazySrcLoc, 15611 offset_src: LazySrcLoc, 15612 ) CompileError!Air.Inst.Ref { 15613 // TODO if the operand is comptime-known to be negative, or is a negative int, 15614 // coerce to isize instead of usize. 15615 const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); 15616 const mod = sema.mod; 15617 const opt_ptr_val = try sema.resolveMaybeUndefVal(ptr); 15618 const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset); 15619 const ptr_ty = sema.typeOf(ptr); 15620 const ptr_info = ptr_ty.ptrInfo(mod); 15621 assert(ptr_info.flags.size == .Many or ptr_info.flags.size == .C); 15622 15623 const new_ptr_ty = t: { 15624 // Calculate the new pointer alignment. 15625 // This code is duplicated in `elemPtrType`. 15626 if (ptr_info.flags.alignment == .none) { 15627 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. 15628 break :t ptr_ty; 15629 } 15630 // If the addend is not a comptime-known value we can still count on 15631 // it being a multiple of the type size. 15632 const elem_size = ptr_info.child.toType().abiSize(mod); 15633 const addend = if (opt_off_val) |off_val| a: { 15634 const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(mod)); 15635 break :a elem_size * off_int; 15636 } else elem_size; 15637 15638 // The resulting pointer is aligned to the lcd between the offset (an 15639 // arbitrary number) and the alignment factor (always a power of two, 15640 // non zero). 15641 const new_align = @as(Alignment, @enumFromInt(@min( 15642 @ctz(addend), 15643 @intFromEnum(ptr_info.flags.alignment), 15644 ))); 15645 assert(new_align != .none); 15646 15647 break :t try mod.ptrType(.{ 15648 .child = ptr_info.child, 15649 .sentinel = ptr_info.sentinel, 15650 .flags = .{ 15651 .size = ptr_info.flags.size, 15652 .alignment = new_align, 15653 .is_const = ptr_info.flags.is_const, 15654 .is_volatile = ptr_info.flags.is_volatile, 15655 .is_allowzero = ptr_info.flags.is_allowzero, 15656 .address_space = ptr_info.flags.address_space, 15657 }, 15658 }); 15659 }; 15660 15661 const runtime_src = rs: { 15662 if (opt_ptr_val) |ptr_val| { 15663 if (opt_off_val) |offset_val| { 15664 if (ptr_val.isUndef(mod)) return sema.addConstUndef(new_ptr_ty); 15665 15666 const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(mod)); 15667 if (offset_int == 0) return ptr; 15668 if (try ptr_val.getUnsignedIntAdvanced(mod, sema)) |addr| { 15669 const elem_size = ptr_info.child.toType().abiSize(mod); 15670 const new_addr = switch (air_tag) { 15671 .ptr_add => addr + elem_size * offset_int, 15672 .ptr_sub => addr - elem_size * offset_int, 15673 else => unreachable, 15674 }; 15675 const new_ptr_val = try mod.ptrIntValue(new_ptr_ty, new_addr); 15676 return sema.addConstant(new_ptr_val); 15677 } 15678 if (air_tag == .ptr_sub) { 15679 return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{}); 15680 } 15681 const new_ptr_val = try ptr_val.elemPtr(new_ptr_ty, offset_int, mod); 15682 return sema.addConstant(new_ptr_val); 15683 } else break :rs offset_src; 15684 } else break :rs ptr_src; 15685 }; 15686 15687 try sema.requireRuntimeBlock(block, op_src, runtime_src); 15688 return block.addInst(.{ 15689 .tag = air_tag, 15690 .data = .{ .ty_pl = .{ 15691 .ty = try sema.addType(new_ptr_ty), 15692 .payload = try sema.addExtra(Air.Bin{ 15693 .lhs = ptr, 15694 .rhs = offset, 15695 }), 15696 } }, 15697 }); 15698 } 15699 15700 fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 15701 const tracy = trace(@src()); 15702 defer tracy.end(); 15703 15704 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 15705 const src = inst_data.src(); 15706 const ptr_src = src; // TODO better source location 15707 const ptr = try sema.resolveInst(inst_data.operand); 15708 return sema.analyzeLoad(block, src, ptr, ptr_src); 15709 } 15710 15711 fn zirAsm( 15712 sema: *Sema, 15713 block: *Block, 15714 extended: Zir.Inst.Extended.InstData, 15715 tmpl_is_expr: bool, 15716 ) CompileError!Air.Inst.Ref { 15717 const tracy = trace(@src()); 15718 defer tracy.end(); 15719 15720 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); 15721 const src = LazySrcLoc.nodeOffset(extra.data.src_node); 15722 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node }; 15723 const outputs_len = @as(u5, @truncate(extended.small)); 15724 const inputs_len = @as(u5, @truncate(extended.small >> 5)); 15725 const clobbers_len = @as(u5, @truncate(extended.small >> 10)); 15726 const is_volatile = @as(u1, @truncate(extended.small >> 15)) != 0; 15727 const is_global_assembly = sema.func_index == .none; 15728 15729 const asm_source: []const u8 = if (tmpl_is_expr) blk: { 15730 const tmpl = @as(Zir.Inst.Ref, @enumFromInt(extra.data.asm_source)); 15731 const s: []const u8 = try sema.resolveConstString(block, src, tmpl, "assembly code must be comptime-known"); 15732 break :blk s; 15733 } else sema.code.nullTerminatedString(extra.data.asm_source); 15734 15735 if (is_global_assembly) { 15736 if (outputs_len != 0) { 15737 return sema.fail(block, src, "module-level assembly does not support outputs", .{}); 15738 } 15739 if (inputs_len != 0) { 15740 return sema.fail(block, src, "module-level assembly does not support inputs", .{}); 15741 } 15742 if (clobbers_len != 0) { 15743 return sema.fail(block, src, "module-level assembly does not support clobbers", .{}); 15744 } 15745 if (is_volatile) { 15746 return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{}); 15747 } 15748 try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source); 15749 return Air.Inst.Ref.void_value; 15750 } 15751 15752 if (block.is_comptime) { 15753 try sema.requireRuntimeBlock(block, src, null); 15754 } 15755 15756 var extra_i = extra.end; 15757 var output_type_bits = extra.data.output_type_bits; 15758 var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len; 15759 15760 const ConstraintName = struct { c: []const u8, n: []const u8 }; 15761 const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len); 15762 const outputs = try sema.arena.alloc(ConstraintName, outputs_len); 15763 var expr_ty = Air.Inst.Ref.void_type; 15764 15765 for (out_args, 0..) |*arg, out_i| { 15766 const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); 15767 extra_i = output.end; 15768 15769 const is_type = @as(u1, @truncate(output_type_bits)) != 0; 15770 output_type_bits >>= 1; 15771 15772 if (is_type) { 15773 // Indicate the output is the asm instruction return value. 15774 arg.* = .none; 15775 const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); 15776 try sema.queueFullTypeResolution(out_ty); 15777 expr_ty = try sema.addType(out_ty); 15778 } else { 15779 arg.* = try sema.resolveInst(output.data.operand); 15780 } 15781 15782 const constraint = sema.code.nullTerminatedString(output.data.constraint); 15783 const name = sema.code.nullTerminatedString(output.data.name); 15784 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; 15785 15786 outputs[out_i] = .{ .c = constraint, .n = name }; 15787 } 15788 15789 const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len); 15790 const inputs = try sema.arena.alloc(ConstraintName, inputs_len); 15791 const mod = sema.mod; 15792 15793 for (args, 0..) |*arg, arg_i| { 15794 const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); 15795 extra_i = input.end; 15796 15797 const uncasted_arg = try sema.resolveInst(input.data.operand); 15798 const uncasted_arg_ty = sema.typeOf(uncasted_arg); 15799 switch (uncasted_arg_ty.zigTypeTag(mod)) { 15800 .ComptimeInt => arg.* = try sema.coerce(block, Type.usize, uncasted_arg, src), 15801 .ComptimeFloat => arg.* = try sema.coerce(block, Type.f64, uncasted_arg, src), 15802 else => { 15803 arg.* = uncasted_arg; 15804 try sema.queueFullTypeResolution(uncasted_arg_ty); 15805 }, 15806 } 15807 15808 const constraint = sema.code.nullTerminatedString(input.data.constraint); 15809 const name = sema.code.nullTerminatedString(input.data.name); 15810 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; 15811 inputs[arg_i] = .{ .c = constraint, .n = name }; 15812 } 15813 15814 const clobbers = try sema.arena.alloc([]const u8, clobbers_len); 15815 for (clobbers) |*name| { 15816 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); 15817 extra_i += 1; 15818 15819 needed_capacity += name.*.len / 4 + 1; 15820 } 15821 15822 needed_capacity += (asm_source.len + 3) / 4; 15823 15824 const gpa = sema.gpa; 15825 try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); 15826 const asm_air = try block.addInst(.{ 15827 .tag = .assembly, 15828 .data = .{ .ty_pl = .{ 15829 .ty = expr_ty, 15830 .payload = sema.addExtraAssumeCapacity(Air.Asm{ 15831 .source_len = @as(u32, @intCast(asm_source.len)), 15832 .outputs_len = outputs_len, 15833 .inputs_len = @as(u32, @intCast(args.len)), 15834 .flags = (@as(u32, @intFromBool(is_volatile)) << 31) | @as(u32, @intCast(clobbers.len)), 15835 }), 15836 } }, 15837 }); 15838 sema.appendRefsAssumeCapacity(out_args); 15839 sema.appendRefsAssumeCapacity(args); 15840 for (outputs) |o| { 15841 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 15842 @memcpy(buffer[0..o.c.len], o.c); 15843 buffer[o.c.len] = 0; 15844 @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n); 15845 buffer[o.c.len + 1 + o.n.len] = 0; 15846 sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4; 15847 } 15848 for (inputs) |input| { 15849 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 15850 @memcpy(buffer[0..input.c.len], input.c); 15851 buffer[input.c.len] = 0; 15852 @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n); 15853 buffer[input.c.len + 1 + input.n.len] = 0; 15854 sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; 15855 } 15856 for (clobbers) |clobber| { 15857 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 15858 @memcpy(buffer[0..clobber.len], clobber); 15859 buffer[clobber.len] = 0; 15860 sema.air_extra.items.len += clobber.len / 4 + 1; 15861 } 15862 { 15863 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); 15864 @memcpy(buffer[0..asm_source.len], asm_source); 15865 sema.air_extra.items.len += (asm_source.len + 3) / 4; 15866 } 15867 return asm_air; 15868 } 15869 15870 /// Only called for equality operators. See also `zirCmp`. 15871 fn zirCmpEq( 15872 sema: *Sema, 15873 block: *Block, 15874 inst: Zir.Inst.Index, 15875 op: std.math.CompareOperator, 15876 air_tag: Air.Inst.Tag, 15877 ) CompileError!Air.Inst.Ref { 15878 const tracy = trace(@src()); 15879 defer tracy.end(); 15880 15881 const mod = sema.mod; 15882 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 15883 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 15884 const src: LazySrcLoc = inst_data.src(); 15885 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 15886 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 15887 const lhs = try sema.resolveInst(extra.lhs); 15888 const rhs = try sema.resolveInst(extra.rhs); 15889 15890 const lhs_ty = sema.typeOf(lhs); 15891 const rhs_ty = sema.typeOf(rhs); 15892 const lhs_ty_tag = lhs_ty.zigTypeTag(mod); 15893 const rhs_ty_tag = rhs_ty.zigTypeTag(mod); 15894 if (lhs_ty_tag == .Null and rhs_ty_tag == .Null) { 15895 // null == null, null != null 15896 if (op == .eq) { 15897 return Air.Inst.Ref.bool_true; 15898 } else { 15899 return Air.Inst.Ref.bool_false; 15900 } 15901 } 15902 15903 // comparing null with optionals 15904 if (lhs_ty_tag == .Null and (rhs_ty_tag == .Optional or rhs_ty.isCPtr(mod))) { 15905 return sema.analyzeIsNull(block, src, rhs, op == .neq); 15906 } 15907 if (rhs_ty_tag == .Null and (lhs_ty_tag == .Optional or lhs_ty.isCPtr(mod))) { 15908 return sema.analyzeIsNull(block, src, lhs, op == .neq); 15909 } 15910 15911 if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { 15912 const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; 15913 return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(mod)}); 15914 } 15915 15916 if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) { 15917 return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op); 15918 } 15919 if (rhs_ty_tag == .Union and (lhs_ty_tag == .EnumLiteral or lhs_ty_tag == .Enum)) { 15920 return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op); 15921 } 15922 15923 if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { 15924 const runtime_src: LazySrcLoc = src: { 15925 if (try sema.resolveMaybeUndefVal(lhs)) |lval| { 15926 if (try sema.resolveMaybeUndefVal(rhs)) |rval| { 15927 if (lval.isUndef(mod) or rval.isUndef(mod)) { 15928 return sema.addConstUndef(Type.bool); 15929 } 15930 const lkey = mod.intern_pool.indexToKey(lval.toIntern()); 15931 const rkey = mod.intern_pool.indexToKey(rval.toIntern()); 15932 if ((lkey.err.name == rkey.err.name) == (op == .eq)) { 15933 return Air.Inst.Ref.bool_true; 15934 } else { 15935 return Air.Inst.Ref.bool_false; 15936 } 15937 } else { 15938 break :src rhs_src; 15939 } 15940 } else { 15941 break :src lhs_src; 15942 } 15943 }; 15944 try sema.requireRuntimeBlock(block, src, runtime_src); 15945 return block.addBinOp(air_tag, lhs, rhs); 15946 } 15947 if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { 15948 const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); 15949 const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); 15950 if (lhs_as_type.eql(rhs_as_type, mod) == (op == .eq)) { 15951 return Air.Inst.Ref.bool_true; 15952 } else { 15953 return Air.Inst.Ref.bool_false; 15954 } 15955 } 15956 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); 15957 } 15958 15959 fn analyzeCmpUnionTag( 15960 sema: *Sema, 15961 block: *Block, 15962 src: LazySrcLoc, 15963 un: Air.Inst.Ref, 15964 un_src: LazySrcLoc, 15965 tag: Air.Inst.Ref, 15966 tag_src: LazySrcLoc, 15967 op: std.math.CompareOperator, 15968 ) CompileError!Air.Inst.Ref { 15969 const mod = sema.mod; 15970 const union_ty = try sema.resolveTypeFields(sema.typeOf(un)); 15971 const union_tag_ty = union_ty.unionTagType(mod) orelse { 15972 const msg = msg: { 15973 const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); 15974 errdefer msg.destroy(sema.gpa); 15975 try mod.errNoteNonLazy(union_ty.declSrcLoc(mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(mod)}); 15976 break :msg msg; 15977 }; 15978 return sema.failWithOwnedErrorMsg(msg); 15979 }; 15980 // Coerce both the union and the tag to the union's tag type, and then execute the 15981 // enum comparison codepath. 15982 const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); 15983 const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); 15984 15985 if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| { 15986 if (enum_val.isUndef(mod)) return sema.addConstUndef(Type.bool); 15987 const field_ty = union_ty.unionFieldType(enum_val, mod); 15988 if (field_ty.zigTypeTag(mod) == .NoReturn) { 15989 return Air.Inst.Ref.bool_false; 15990 } 15991 } 15992 15993 return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src); 15994 } 15995 15996 /// Only called for non-equality operators. See also `zirCmpEq`. 15997 fn zirCmp( 15998 sema: *Sema, 15999 block: *Block, 16000 inst: Zir.Inst.Index, 16001 op: std.math.CompareOperator, 16002 ) CompileError!Air.Inst.Ref { 16003 const tracy = trace(@src()); 16004 defer tracy.end(); 16005 16006 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 16007 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 16008 const src: LazySrcLoc = inst_data.src(); 16009 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 16010 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 16011 const lhs = try sema.resolveInst(extra.lhs); 16012 const rhs = try sema.resolveInst(extra.rhs); 16013 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false); 16014 } 16015 16016 fn analyzeCmp( 16017 sema: *Sema, 16018 block: *Block, 16019 src: LazySrcLoc, 16020 lhs: Air.Inst.Ref, 16021 rhs: Air.Inst.Ref, 16022 op: std.math.CompareOperator, 16023 lhs_src: LazySrcLoc, 16024 rhs_src: LazySrcLoc, 16025 is_equality_cmp: bool, 16026 ) CompileError!Air.Inst.Ref { 16027 const mod = sema.mod; 16028 const lhs_ty = sema.typeOf(lhs); 16029 const rhs_ty = sema.typeOf(rhs); 16030 if (lhs_ty.zigTypeTag(mod) != .Optional and rhs_ty.zigTypeTag(mod) != .Optional) { 16031 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 16032 } 16033 16034 if (lhs_ty.zigTypeTag(mod) == .Vector and rhs_ty.zigTypeTag(mod) == .Vector) { 16035 return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src); 16036 } 16037 if (lhs_ty.isNumeric(mod) and rhs_ty.isNumeric(mod)) { 16038 // This operation allows any combination of integer and float types, regardless of the 16039 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for 16040 // numeric types. 16041 return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); 16042 } 16043 if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorUnion and rhs_ty.zigTypeTag(mod) == .ErrorSet) { 16044 const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs); 16045 return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src); 16046 } 16047 if (is_equality_cmp and lhs_ty.zigTypeTag(mod) == .ErrorSet and rhs_ty.zigTypeTag(mod) == .ErrorUnion) { 16048 const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs); 16049 return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src); 16050 } 16051 const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; 16052 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); 16053 if (!resolved_type.isSelfComparable(mod, is_equality_cmp)) { 16054 return sema.fail(block, src, "operator {s} not allowed for type '{}'", .{ 16055 compareOperatorName(op), resolved_type.fmt(mod), 16056 }); 16057 } 16058 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); 16059 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); 16060 return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src); 16061 } 16062 16063 fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { 16064 return switch (comp) { 16065 .lt => "<", 16066 .lte => "<=", 16067 .eq => "==", 16068 .gte => ">=", 16069 .gt => ">", 16070 .neq => "!=", 16071 }; 16072 } 16073 16074 fn cmpSelf( 16075 sema: *Sema, 16076 block: *Block, 16077 src: LazySrcLoc, 16078 casted_lhs: Air.Inst.Ref, 16079 casted_rhs: Air.Inst.Ref, 16080 op: std.math.CompareOperator, 16081 lhs_src: LazySrcLoc, 16082 rhs_src: LazySrcLoc, 16083 ) CompileError!Air.Inst.Ref { 16084 const mod = sema.mod; 16085 const resolved_type = sema.typeOf(casted_lhs); 16086 const runtime_src: LazySrcLoc = src: { 16087 if (try sema.resolveMaybeUndefVal(casted_lhs)) |lhs_val| { 16088 if (lhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool); 16089 if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| { 16090 if (rhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool); 16091 16092 if (resolved_type.zigTypeTag(mod) == .Vector) { 16093 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type); 16094 return sema.addConstant(cmp_val); 16095 } 16096 16097 if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type)) { 16098 return Air.Inst.Ref.bool_true; 16099 } else { 16100 return Air.Inst.Ref.bool_false; 16101 } 16102 } else { 16103 if (resolved_type.zigTypeTag(mod) == .Bool) { 16104 // We can lower bool eq/neq more efficiently. 16105 return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src); 16106 } 16107 break :src rhs_src; 16108 } 16109 } else { 16110 // For bools, we still check the other operand, because we can lower 16111 // bool eq/neq more efficiently. 16112 if (resolved_type.zigTypeTag(mod) == .Bool) { 16113 if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| { 16114 if (rhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool); 16115 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src); 16116 } 16117 } 16118 break :src lhs_src; 16119 } 16120 }; 16121 try sema.requireRuntimeBlock(block, src, runtime_src); 16122 if (resolved_type.zigTypeTag(mod) == .Vector) { 16123 return block.addCmpVector(casted_lhs, casted_rhs, op); 16124 } 16125 const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized); 16126 return block.addBinOp(tag, casted_lhs, casted_rhs); 16127 } 16128 16129 /// cmp_eq (x, false) => not(x) 16130 /// cmp_eq (x, true ) => x 16131 /// cmp_neq(x, false) => x 16132 /// cmp_neq(x, true ) => not(x) 16133 fn runtimeBoolCmp( 16134 sema: *Sema, 16135 block: *Block, 16136 src: LazySrcLoc, 16137 op: std.math.CompareOperator, 16138 lhs: Air.Inst.Ref, 16139 rhs: bool, 16140 runtime_src: LazySrcLoc, 16141 ) CompileError!Air.Inst.Ref { 16142 if ((op == .neq) == rhs) { 16143 try sema.requireRuntimeBlock(block, src, runtime_src); 16144 return block.addTyOp(.not, Type.bool, lhs); 16145 } else { 16146 return lhs; 16147 } 16148 } 16149 16150 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16151 const mod = sema.mod; 16152 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 16153 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 16154 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 16155 switch (ty.zigTypeTag(mod)) { 16156 .Fn, 16157 .NoReturn, 16158 .Undefined, 16159 .Null, 16160 .Opaque, 16161 => return sema.fail(block, operand_src, "no size available for type '{}'", .{ty.fmt(mod)}), 16162 16163 .Type, 16164 .EnumLiteral, 16165 .ComptimeFloat, 16166 .ComptimeInt, 16167 .Void, 16168 => return sema.addIntUnsigned(Type.comptime_int, 0), 16169 16170 .Bool, 16171 .Int, 16172 .Float, 16173 .Pointer, 16174 .Array, 16175 .Struct, 16176 .Optional, 16177 .ErrorUnion, 16178 .ErrorSet, 16179 .Enum, 16180 .Union, 16181 .Vector, 16182 .Frame, 16183 .AnyFrame, 16184 => {}, 16185 } 16186 const val = try ty.lazyAbiSize(mod); 16187 if (val.isLazySize(mod)) { 16188 try sema.queueFullTypeResolution(ty); 16189 } 16190 return sema.addConstant(val); 16191 } 16192 16193 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16194 const mod = sema.mod; 16195 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 16196 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 16197 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); 16198 switch (operand_ty.zigTypeTag(mod)) { 16199 .Fn, 16200 .NoReturn, 16201 .Undefined, 16202 .Null, 16203 .Opaque, 16204 => return sema.fail(block, operand_src, "no size available for type '{}'", .{operand_ty.fmt(mod)}), 16205 16206 .Type, 16207 .EnumLiteral, 16208 .ComptimeFloat, 16209 .ComptimeInt, 16210 .Void, 16211 => return sema.addIntUnsigned(Type.comptime_int, 0), 16212 16213 .Bool, 16214 .Int, 16215 .Float, 16216 .Pointer, 16217 .Array, 16218 .Struct, 16219 .Optional, 16220 .ErrorUnion, 16221 .ErrorSet, 16222 .Enum, 16223 .Union, 16224 .Vector, 16225 .Frame, 16226 .AnyFrame, 16227 => {}, 16228 } 16229 const bit_size = try operand_ty.bitSizeAdvanced(mod, sema); 16230 return sema.addIntUnsigned(Type.comptime_int, bit_size); 16231 } 16232 16233 fn zirThis( 16234 sema: *Sema, 16235 block: *Block, 16236 extended: Zir.Inst.Extended.InstData, 16237 ) CompileError!Air.Inst.Ref { 16238 const mod = sema.mod; 16239 const this_decl_index = mod.namespaceDeclIndex(block.namespace); 16240 const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand))); 16241 return sema.analyzeDeclVal(block, src, this_decl_index); 16242 } 16243 16244 fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 16245 const inst_data = sema.code.instructions.items(.data)[inst].un_tok; 16246 // Closures are not necessarily constant values. For example, the 16247 // code might do something like this: 16248 // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; } 16249 // ...in which case the closure_capture instruction has access to a runtime 16250 // value only. In such case we preserve the type and use a dummy runtime value. 16251 const operand = try sema.resolveInst(inst_data.operand); 16252 const ty = sema.typeOf(operand); 16253 const capture: CaptureScope.Capture = blk: { 16254 if (try sema.resolveMaybeUndefValAllowVariables(operand)) |val| { 16255 const ip_index = try val.intern(ty, sema.mod); 16256 break :blk .{ .comptime_val = ip_index }; 16257 } 16258 break :blk .{ .runtime_val = ty.toIntern() }; 16259 }; 16260 try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, capture); 16261 } 16262 16263 fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16264 const mod = sema.mod; 16265 const inst_data = sema.code.instructions.items(.data)[inst].inst_node; 16266 var scope: *CaptureScope = mod.declPtr(block.src_decl).src_scope.?; 16267 // Note: The target closure must be in this scope list. 16268 // If it's not here, the zir is invalid, or the list is broken. 16269 const capture = while (true) { 16270 // Note: We don't need to add a dependency here, because 16271 // decls always depend on their lexical parents. 16272 16273 // Fail this decl if a scope it depended on failed. 16274 if (scope.failed()) { 16275 if (sema.owner_func) |owner_func| { 16276 owner_func.state = .dependency_failure; 16277 } else { 16278 sema.owner_decl.analysis = .dependency_failure; 16279 } 16280 return error.AnalysisFail; 16281 } 16282 if (scope.captures.get(inst_data.inst)) |capture| { 16283 break capture; 16284 } 16285 scope = scope.parent.?; 16286 }; 16287 16288 if (capture == .runtime_val and !block.is_typeof and sema.func_index == .none) { 16289 const msg = msg: { 16290 const name = name: { 16291 const file = sema.owner_decl.getFileScope(mod); 16292 const tree = file.getTree(sema.gpa) catch |err| { 16293 // In this case we emit a warning + a less precise source location. 16294 log.warn("unable to load {s}: {s}", .{ 16295 file.sub_file_path, @errorName(err), 16296 }); 16297 break :name null; 16298 }; 16299 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node); 16300 const token = tree.nodes.items(.main_token)[node]; 16301 break :name tree.tokenSlice(token); 16302 }; 16303 16304 const msg = if (name) |some| 16305 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible outside function scope", .{some}) 16306 else 16307 try sema.errMsg(block, inst_data.src(), "variable not accessible outside function scope", .{}); 16308 errdefer msg.destroy(sema.gpa); 16309 16310 // TODO add "declared here" note 16311 break :msg msg; 16312 }; 16313 return sema.failWithOwnedErrorMsg(msg); 16314 } 16315 16316 if (capture == .runtime_val and !block.is_typeof and !block.is_comptime and sema.func_index != .none) { 16317 const msg = msg: { 16318 const name = name: { 16319 const file = sema.owner_decl.getFileScope(mod); 16320 const tree = file.getTree(sema.gpa) catch |err| { 16321 // In this case we emit a warning + a less precise source location. 16322 log.warn("unable to load {s}: {s}", .{ 16323 file.sub_file_path, @errorName(err), 16324 }); 16325 break :name null; 16326 }; 16327 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node); 16328 const token = tree.nodes.items(.main_token)[node]; 16329 break :name tree.tokenSlice(token); 16330 }; 16331 16332 const msg = if (name) |some| 16333 try sema.errMsg(block, inst_data.src(), "'{s}' not accessible from inner function", .{some}) 16334 else 16335 try sema.errMsg(block, inst_data.src(), "variable not accessible from inner function", .{}); 16336 errdefer msg.destroy(sema.gpa); 16337 16338 try sema.errNote(block, LazySrcLoc.nodeOffset(0), msg, "crossed function definition here", .{}); 16339 16340 // TODO add "declared here" note 16341 break :msg msg; 16342 }; 16343 return sema.failWithOwnedErrorMsg(msg); 16344 } 16345 16346 switch (capture) { 16347 .runtime_val => |ty_ip_index| { 16348 assert(block.is_typeof); 16349 // We need a dummy runtime instruction with the correct type. 16350 return block.addTy(.alloc, ty_ip_index.toType()); 16351 }, 16352 .comptime_val => |val_ip_index| { 16353 return sema.addConstant(val_ip_index.toValue()); 16354 }, 16355 } 16356 } 16357 16358 fn zirRetAddr( 16359 sema: *Sema, 16360 block: *Block, 16361 extended: Zir.Inst.Extended.InstData, 16362 ) CompileError!Air.Inst.Ref { 16363 _ = extended; 16364 if (block.is_comptime) { 16365 // TODO: we could give a meaningful lazy value here. #14938 16366 return sema.addIntUnsigned(Type.usize, 0); 16367 } else { 16368 return block.addNoOp(.ret_addr); 16369 } 16370 } 16371 16372 fn zirFrameAddress( 16373 sema: *Sema, 16374 block: *Block, 16375 extended: Zir.Inst.Extended.InstData, 16376 ) CompileError!Air.Inst.Ref { 16377 const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand))); 16378 try sema.requireRuntimeBlock(block, src, null); 16379 return try block.addNoOp(.frame_addr); 16380 } 16381 16382 fn zirBuiltinSrc( 16383 sema: *Sema, 16384 block: *Block, 16385 extended: Zir.Inst.Extended.InstData, 16386 ) CompileError!Air.Inst.Ref { 16387 const tracy = trace(@src()); 16388 defer tracy.end(); 16389 16390 const mod = sema.mod; 16391 const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data; 16392 const src = LazySrcLoc.nodeOffset(extra.node); 16393 const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{}); 16394 const fn_owner_decl = mod.declPtr(func.owner_decl); 16395 16396 const func_name_val = blk: { 16397 var anon_decl = try block.startAnonDecl(); 16398 defer anon_decl.deinit(); 16399 // TODO: write something like getCoercedInts to avoid needing to dupe 16400 const name = try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(fn_owner_decl.name)); 16401 const new_decl_ty = try mod.arrayType(.{ 16402 .len = name.len, 16403 .sentinel = .zero_u8, 16404 .child = .u8_type, 16405 }); 16406 const new_decl = try anon_decl.finish( 16407 new_decl_ty, 16408 (try mod.intern(.{ .aggregate = .{ 16409 .ty = new_decl_ty.toIntern(), 16410 .storage = .{ .bytes = name }, 16411 } })).toValue(), 16412 .none, // default alignment 16413 ); 16414 break :blk try mod.intern(.{ .ptr = .{ 16415 .ty = .slice_const_u8_sentinel_0_type, 16416 .addr = .{ .decl = new_decl }, 16417 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 16418 } }); 16419 }; 16420 16421 const file_name_val = blk: { 16422 var anon_decl = try block.startAnonDecl(); 16423 defer anon_decl.deinit(); 16424 // The compiler must not call realpath anywhere. 16425 const name = try fn_owner_decl.getFileScope(mod).fullPathZ(sema.arena); 16426 const new_decl_ty = try mod.arrayType(.{ 16427 .len = name.len, 16428 .sentinel = .zero_u8, 16429 .child = .u8_type, 16430 }); 16431 const new_decl = try anon_decl.finish( 16432 new_decl_ty, 16433 (try mod.intern(.{ .aggregate = .{ 16434 .ty = new_decl_ty.toIntern(), 16435 .storage = .{ .bytes = name }, 16436 } })).toValue(), 16437 .none, // default alignment 16438 ); 16439 break :blk try mod.intern(.{ .ptr = .{ 16440 .ty = .slice_const_u8_sentinel_0_type, 16441 .addr = .{ .decl = new_decl }, 16442 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 16443 } }); 16444 }; 16445 16446 const src_loc_ty = try sema.getBuiltinType("SourceLocation"); 16447 const fields = .{ 16448 // file: [:0]const u8, 16449 file_name_val, 16450 // fn_name: [:0]const u8, 16451 func_name_val, 16452 // line: u32, 16453 try mod.intern(.{ .runtime_value = .{ 16454 .ty = .u32_type, 16455 .val = (try mod.intValue(Type.u32, extra.line + 1)).toIntern(), 16456 } }), 16457 // column: u32, 16458 (try mod.intValue(Type.u32, extra.column + 1)).toIntern(), 16459 }; 16460 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 16461 .ty = src_loc_ty.toIntern(), 16462 .storage = .{ .elems = &fields }, 16463 } })).toValue()); 16464 } 16465 16466 fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 16467 const mod = sema.mod; 16468 const gpa = sema.gpa; 16469 const ip = &mod.intern_pool; 16470 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 16471 const src = inst_data.src(); 16472 const ty = try sema.resolveType(block, src, inst_data.operand); 16473 const type_info_ty = try sema.getBuiltinType("Type"); 16474 const type_info_tag_ty = type_info_ty.unionTagType(mod).?; 16475 16476 switch (ty.zigTypeTag(mod)) { 16477 .Type, 16478 .Void, 16479 .Bool, 16480 .NoReturn, 16481 .ComptimeFloat, 16482 .ComptimeInt, 16483 .Undefined, 16484 .Null, 16485 .EnumLiteral, 16486 => |type_info_tag| return sema.addConstant((try mod.intern(.{ .un = .{ 16487 .ty = type_info_ty.toIntern(), 16488 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(type_info_tag))).toIntern(), 16489 .val = .void_value, 16490 } })).toValue()), 16491 .Fn => { 16492 // TODO: look into memoizing this result. 16493 var params_anon_decl = try block.startAnonDecl(); 16494 defer params_anon_decl.deinit(); 16495 16496 const fn_info_decl_index = (try sema.namespaceLookup( 16497 block, 16498 src, 16499 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16500 try ip.getOrPutString(gpa, "Fn"), 16501 )).?; 16502 try mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index); 16503 try sema.ensureDeclAnalyzed(fn_info_decl_index); 16504 const fn_info_decl = mod.declPtr(fn_info_decl_index); 16505 const fn_info_ty = fn_info_decl.val.toType(); 16506 16507 const param_info_decl_index = (try sema.namespaceLookup( 16508 block, 16509 src, 16510 fn_info_ty.getNamespaceIndex(mod).unwrap().?, 16511 try ip.getOrPutString(gpa, "Param"), 16512 )).?; 16513 try mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index); 16514 try sema.ensureDeclAnalyzed(param_info_decl_index); 16515 const param_info_decl = mod.declPtr(param_info_decl_index); 16516 const param_info_ty = param_info_decl.val.toType(); 16517 16518 const param_vals = try sema.arena.alloc(InternPool.Index, mod.typeToFunc(ty).?.param_types.len); 16519 for (param_vals, 0..) |*param_val, i| { 16520 const info = mod.typeToFunc(ty).?; 16521 const param_ty = info.param_types[i]; 16522 const is_generic = param_ty == .generic_poison_type; 16523 const param_ty_val = try ip.get(gpa, .{ .opt = .{ 16524 .ty = try ip.get(gpa, .{ .opt_type = .type_type }), 16525 .val = if (is_generic) .none else param_ty, 16526 } }); 16527 16528 const is_noalias = blk: { 16529 const index = std.math.cast(u5, i) orelse break :blk false; 16530 break :blk @as(u1, @truncate(info.noalias_bits >> index)) != 0; 16531 }; 16532 16533 const param_fields = .{ 16534 // is_generic: bool, 16535 Value.makeBool(is_generic).toIntern(), 16536 // is_noalias: bool, 16537 Value.makeBool(is_noalias).toIntern(), 16538 // type: ?type, 16539 param_ty_val, 16540 }; 16541 param_val.* = try mod.intern(.{ .aggregate = .{ 16542 .ty = param_info_ty.toIntern(), 16543 .storage = .{ .elems = ¶m_fields }, 16544 } }); 16545 } 16546 16547 const args_val = v: { 16548 const new_decl_ty = try mod.arrayType(.{ 16549 .len = param_vals.len, 16550 .child = param_info_ty.toIntern(), 16551 }); 16552 const new_decl = try params_anon_decl.finish( 16553 new_decl_ty, 16554 (try mod.intern(.{ .aggregate = .{ 16555 .ty = new_decl_ty.toIntern(), 16556 .storage = .{ .elems = param_vals }, 16557 } })).toValue(), 16558 .none, // default alignment 16559 ); 16560 break :v try mod.intern(.{ .ptr = .{ 16561 .ty = (try mod.ptrType(.{ 16562 .child = param_info_ty.toIntern(), 16563 .flags = .{ 16564 .size = .Slice, 16565 .is_const = true, 16566 }, 16567 })).toIntern(), 16568 .addr = .{ .decl = new_decl }, 16569 .len = (try mod.intValue(Type.usize, param_vals.len)).toIntern(), 16570 } }); 16571 }; 16572 16573 const info = mod.typeToFunc(ty).?; 16574 const ret_ty_opt = try mod.intern(.{ .opt = .{ 16575 .ty = try ip.get(gpa, .{ .opt_type = .type_type }), 16576 .val = if (info.return_type == .generic_poison_type) .none else info.return_type, 16577 } }); 16578 16579 const callconv_ty = try sema.getBuiltinType("CallingConvention"); 16580 16581 const field_values = .{ 16582 // calling_convention: CallingConvention, 16583 (try mod.enumValueFieldIndex(callconv_ty, @intFromEnum(info.cc))).toIntern(), 16584 // alignment: comptime_int, 16585 (try mod.intValue(Type.comptime_int, ty.abiAlignment(mod))).toIntern(), 16586 // is_generic: bool, 16587 Value.makeBool(info.is_generic).toIntern(), 16588 // is_var_args: bool, 16589 Value.makeBool(info.is_var_args).toIntern(), 16590 // return_type: ?type, 16591 ret_ty_opt, 16592 // args: []const Fn.Param, 16593 args_val, 16594 }; 16595 return sema.addConstant((try mod.intern(.{ .un = .{ 16596 .ty = type_info_ty.toIntern(), 16597 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Fn))).toIntern(), 16598 .val = try mod.intern(.{ .aggregate = .{ 16599 .ty = fn_info_ty.toIntern(), 16600 .storage = .{ .elems = &field_values }, 16601 } }), 16602 } })).toValue()); 16603 }, 16604 .Int => { 16605 const int_info_decl_index = (try sema.namespaceLookup( 16606 block, 16607 src, 16608 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16609 try ip.getOrPutString(gpa, "Int"), 16610 )).?; 16611 try mod.declareDeclDependency(sema.owner_decl_index, int_info_decl_index); 16612 try sema.ensureDeclAnalyzed(int_info_decl_index); 16613 const int_info_decl = mod.declPtr(int_info_decl_index); 16614 const int_info_ty = int_info_decl.val.toType(); 16615 16616 const signedness_ty = try sema.getBuiltinType("Signedness"); 16617 const info = ty.intInfo(mod); 16618 const field_values = .{ 16619 // signedness: Signedness, 16620 try (try mod.enumValueFieldIndex(signedness_ty, @intFromEnum(info.signedness))).intern(signedness_ty, mod), 16621 // bits: u16, 16622 (try mod.intValue(Type.u16, info.bits)).toIntern(), 16623 }; 16624 return sema.addConstant((try mod.intern(.{ .un = .{ 16625 .ty = type_info_ty.toIntern(), 16626 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Int))).toIntern(), 16627 .val = try mod.intern(.{ .aggregate = .{ 16628 .ty = int_info_ty.toIntern(), 16629 .storage = .{ .elems = &field_values }, 16630 } }), 16631 } })).toValue()); 16632 }, 16633 .Float => { 16634 const float_info_decl_index = (try sema.namespaceLookup( 16635 block, 16636 src, 16637 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16638 try ip.getOrPutString(gpa, "Float"), 16639 )).?; 16640 try mod.declareDeclDependency(sema.owner_decl_index, float_info_decl_index); 16641 try sema.ensureDeclAnalyzed(float_info_decl_index); 16642 const float_info_decl = mod.declPtr(float_info_decl_index); 16643 const float_info_ty = float_info_decl.val.toType(); 16644 16645 const field_vals = .{ 16646 // bits: u16, 16647 (try mod.intValue(Type.u16, ty.bitSize(mod))).toIntern(), 16648 }; 16649 return sema.addConstant((try mod.intern(.{ .un = .{ 16650 .ty = type_info_ty.toIntern(), 16651 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Float))).toIntern(), 16652 .val = try mod.intern(.{ .aggregate = .{ 16653 .ty = float_info_ty.toIntern(), 16654 .storage = .{ .elems = &field_vals }, 16655 } }), 16656 } })).toValue()); 16657 }, 16658 .Pointer => { 16659 const info = ty.ptrInfo(mod); 16660 const alignment = if (info.flags.alignment.toByteUnitsOptional()) |alignment| 16661 try mod.intValue(Type.comptime_int, alignment) 16662 else 16663 try info.child.toType().lazyAbiAlignment(mod); 16664 16665 const addrspace_ty = try sema.getBuiltinType("AddressSpace"); 16666 const pointer_ty = t: { 16667 const decl_index = (try sema.namespaceLookup( 16668 block, 16669 src, 16670 (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, 16671 try ip.getOrPutString(gpa, "Pointer"), 16672 )).?; 16673 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 16674 try sema.ensureDeclAnalyzed(decl_index); 16675 const decl = mod.declPtr(decl_index); 16676 break :t decl.val.toType(); 16677 }; 16678 const ptr_size_ty = t: { 16679 const decl_index = (try sema.namespaceLookup( 16680 block, 16681 src, 16682 pointer_ty.getNamespaceIndex(mod).unwrap().?, 16683 try ip.getOrPutString(gpa, "Size"), 16684 )).?; 16685 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 16686 try sema.ensureDeclAnalyzed(decl_index); 16687 const decl = mod.declPtr(decl_index); 16688 break :t decl.val.toType(); 16689 }; 16690 16691 const field_values = .{ 16692 // size: Size, 16693 try (try mod.enumValueFieldIndex(ptr_size_ty, @intFromEnum(info.flags.size))).intern(ptr_size_ty, mod), 16694 // is_const: bool, 16695 Value.makeBool(info.flags.is_const).toIntern(), 16696 // is_volatile: bool, 16697 Value.makeBool(info.flags.is_volatile).toIntern(), 16698 // alignment: comptime_int, 16699 alignment.toIntern(), 16700 // address_space: AddressSpace 16701 try (try mod.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).intern(addrspace_ty, mod), 16702 // child: type, 16703 info.child, 16704 // is_allowzero: bool, 16705 Value.makeBool(info.flags.is_allowzero).toIntern(), 16706 // sentinel: ?*const anyopaque, 16707 (try sema.optRefValue(block, info.child.toType(), switch (info.sentinel) { 16708 .none => null, 16709 else => info.sentinel.toValue(), 16710 })).toIntern(), 16711 }; 16712 return sema.addConstant((try mod.intern(.{ .un = .{ 16713 .ty = type_info_ty.toIntern(), 16714 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Pointer))).toIntern(), 16715 .val = try mod.intern(.{ .aggregate = .{ 16716 .ty = pointer_ty.toIntern(), 16717 .storage = .{ .elems = &field_values }, 16718 } }), 16719 } })).toValue()); 16720 }, 16721 .Array => { 16722 const array_field_ty = t: { 16723 const array_field_ty_decl_index = (try sema.namespaceLookup( 16724 block, 16725 src, 16726 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16727 try ip.getOrPutString(gpa, "Array"), 16728 )).?; 16729 try mod.declareDeclDependency(sema.owner_decl_index, array_field_ty_decl_index); 16730 try sema.ensureDeclAnalyzed(array_field_ty_decl_index); 16731 const array_field_ty_decl = mod.declPtr(array_field_ty_decl_index); 16732 break :t array_field_ty_decl.val.toType(); 16733 }; 16734 16735 const info = ty.arrayInfo(mod); 16736 const field_values = .{ 16737 // len: comptime_int, 16738 (try mod.intValue(Type.comptime_int, info.len)).toIntern(), 16739 // child: type, 16740 info.elem_type.toIntern(), 16741 // sentinel: ?*const anyopaque, 16742 (try sema.optRefValue(block, info.elem_type, info.sentinel)).toIntern(), 16743 }; 16744 return sema.addConstant((try mod.intern(.{ .un = .{ 16745 .ty = type_info_ty.toIntern(), 16746 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Array))).toIntern(), 16747 .val = try mod.intern(.{ .aggregate = .{ 16748 .ty = array_field_ty.toIntern(), 16749 .storage = .{ .elems = &field_values }, 16750 } }), 16751 } })).toValue()); 16752 }, 16753 .Vector => { 16754 const vector_field_ty = t: { 16755 const vector_field_ty_decl_index = (try sema.namespaceLookup( 16756 block, 16757 src, 16758 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16759 try ip.getOrPutString(gpa, "Vector"), 16760 )).?; 16761 try mod.declareDeclDependency(sema.owner_decl_index, vector_field_ty_decl_index); 16762 try sema.ensureDeclAnalyzed(vector_field_ty_decl_index); 16763 const vector_field_ty_decl = mod.declPtr(vector_field_ty_decl_index); 16764 break :t vector_field_ty_decl.val.toType(); 16765 }; 16766 16767 const info = ty.arrayInfo(mod); 16768 const field_values = .{ 16769 // len: comptime_int, 16770 (try mod.intValue(Type.comptime_int, info.len)).toIntern(), 16771 // child: type, 16772 info.elem_type.toIntern(), 16773 }; 16774 return sema.addConstant((try mod.intern(.{ .un = .{ 16775 .ty = type_info_ty.toIntern(), 16776 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Vector))).toIntern(), 16777 .val = try mod.intern(.{ .aggregate = .{ 16778 .ty = vector_field_ty.toIntern(), 16779 .storage = .{ .elems = &field_values }, 16780 } }), 16781 } })).toValue()); 16782 }, 16783 .Optional => { 16784 const optional_field_ty = t: { 16785 const optional_field_ty_decl_index = (try sema.namespaceLookup( 16786 block, 16787 src, 16788 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16789 try ip.getOrPutString(gpa, "Optional"), 16790 )).?; 16791 try mod.declareDeclDependency(sema.owner_decl_index, optional_field_ty_decl_index); 16792 try sema.ensureDeclAnalyzed(optional_field_ty_decl_index); 16793 const optional_field_ty_decl = mod.declPtr(optional_field_ty_decl_index); 16794 break :t optional_field_ty_decl.val.toType(); 16795 }; 16796 16797 const field_values = .{ 16798 // child: type, 16799 ty.optionalChild(mod).toIntern(), 16800 }; 16801 return sema.addConstant((try mod.intern(.{ .un = .{ 16802 .ty = type_info_ty.toIntern(), 16803 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Optional))).toIntern(), 16804 .val = try mod.intern(.{ .aggregate = .{ 16805 .ty = optional_field_ty.toIntern(), 16806 .storage = .{ .elems = &field_values }, 16807 } }), 16808 } })).toValue()); 16809 }, 16810 .ErrorSet => { 16811 var fields_anon_decl = try block.startAnonDecl(); 16812 defer fields_anon_decl.deinit(); 16813 16814 // Get the Error type 16815 const error_field_ty = t: { 16816 const set_field_ty_decl_index = (try sema.namespaceLookup( 16817 block, 16818 src, 16819 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16820 try ip.getOrPutString(gpa, "Error"), 16821 )).?; 16822 try mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index); 16823 try sema.ensureDeclAnalyzed(set_field_ty_decl_index); 16824 const set_field_ty_decl = mod.declPtr(set_field_ty_decl_index); 16825 break :t set_field_ty_decl.val.toType(); 16826 }; 16827 16828 try sema.queueFullTypeResolution(error_field_ty); 16829 16830 // If the error set is inferred it must be resolved at this point 16831 try sema.resolveInferredErrorSetTy(block, src, ty); 16832 16833 // Build our list of Error values 16834 // Optional value is only null if anyerror 16835 // Value can be zero-length slice otherwise 16836 const error_field_vals = if (ty.isAnyError(mod)) null else blk: { 16837 const vals = try sema.arena.alloc(InternPool.Index, ty.errorSetNames(mod).len); 16838 for (vals, 0..) |*field_val, i| { 16839 // TODO: write something like getCoercedInts to avoid needing to dupe 16840 const name = try sema.arena.dupe(u8, ip.stringToSlice(ty.errorSetNames(mod)[i])); 16841 const name_val = v: { 16842 var anon_decl = try block.startAnonDecl(); 16843 defer anon_decl.deinit(); 16844 const new_decl_ty = try mod.arrayType(.{ 16845 .len = name.len, 16846 .child = .u8_type, 16847 }); 16848 const new_decl = try anon_decl.finish( 16849 new_decl_ty, 16850 (try mod.intern(.{ .aggregate = .{ 16851 .ty = new_decl_ty.toIntern(), 16852 .storage = .{ .bytes = name }, 16853 } })).toValue(), 16854 .none, // default alignment 16855 ); 16856 break :v try mod.intern(.{ .ptr = .{ 16857 .ty = .slice_const_u8_type, 16858 .addr = .{ .decl = new_decl }, 16859 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 16860 } }); 16861 }; 16862 16863 const error_field_fields = .{ 16864 // name: []const u8, 16865 name_val, 16866 }; 16867 field_val.* = try mod.intern(.{ .aggregate = .{ 16868 .ty = error_field_ty.toIntern(), 16869 .storage = .{ .elems = &error_field_fields }, 16870 } }); 16871 } 16872 16873 break :blk vals; 16874 }; 16875 16876 // Build our ?[]const Error value 16877 const slice_errors_ty = try mod.ptrType(.{ 16878 .child = error_field_ty.toIntern(), 16879 .flags = .{ 16880 .size = .Slice, 16881 .is_const = true, 16882 }, 16883 }); 16884 const opt_slice_errors_ty = try mod.optionalType(slice_errors_ty.toIntern()); 16885 const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: { 16886 const array_errors_ty = try mod.arrayType(.{ 16887 .len = vals.len, 16888 .child = error_field_ty.toIntern(), 16889 }); 16890 const new_decl = try fields_anon_decl.finish( 16891 array_errors_ty, 16892 (try mod.intern(.{ .aggregate = .{ 16893 .ty = array_errors_ty.toIntern(), 16894 .storage = .{ .elems = vals }, 16895 } })).toValue(), 16896 .none, // default alignment 16897 ); 16898 break :v try mod.intern(.{ .ptr = .{ 16899 .ty = slice_errors_ty.toIntern(), 16900 .addr = .{ .decl = new_decl }, 16901 .len = (try mod.intValue(Type.usize, vals.len)).toIntern(), 16902 } }); 16903 } else .none; 16904 const errors_val = try mod.intern(.{ .opt = .{ 16905 .ty = opt_slice_errors_ty.toIntern(), 16906 .val = errors_payload_val, 16907 } }); 16908 16909 // Construct Type{ .ErrorSet = errors_val } 16910 return sema.addConstant((try mod.intern(.{ .un = .{ 16911 .ty = type_info_ty.toIntern(), 16912 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.ErrorSet))).toIntern(), 16913 .val = errors_val, 16914 } })).toValue()); 16915 }, 16916 .ErrorUnion => { 16917 const error_union_field_ty = t: { 16918 const error_union_field_ty_decl_index = (try sema.namespaceLookup( 16919 block, 16920 src, 16921 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16922 try ip.getOrPutString(gpa, "ErrorUnion"), 16923 )).?; 16924 try mod.declareDeclDependency(sema.owner_decl_index, error_union_field_ty_decl_index); 16925 try sema.ensureDeclAnalyzed(error_union_field_ty_decl_index); 16926 const error_union_field_ty_decl = mod.declPtr(error_union_field_ty_decl_index); 16927 break :t error_union_field_ty_decl.val.toType(); 16928 }; 16929 16930 const field_values = .{ 16931 // error_set: type, 16932 ty.errorUnionSet(mod).toIntern(), 16933 // payload: type, 16934 ty.errorUnionPayload(mod).toIntern(), 16935 }; 16936 return sema.addConstant((try mod.intern(.{ .un = .{ 16937 .ty = type_info_ty.toIntern(), 16938 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.ErrorUnion))).toIntern(), 16939 .val = try mod.intern(.{ .aggregate = .{ 16940 .ty = error_union_field_ty.toIntern(), 16941 .storage = .{ .elems = &field_values }, 16942 } }), 16943 } })).toValue()); 16944 }, 16945 .Enum => { 16946 // TODO: look into memoizing this result. 16947 const is_exhaustive = Value.makeBool(ip.indexToKey(ty.toIntern()).enum_type.tag_mode != .nonexhaustive); 16948 16949 var fields_anon_decl = try block.startAnonDecl(); 16950 defer fields_anon_decl.deinit(); 16951 16952 const enum_field_ty = t: { 16953 const enum_field_ty_decl_index = (try sema.namespaceLookup( 16954 block, 16955 src, 16956 type_info_ty.getNamespaceIndex(mod).unwrap().?, 16957 try ip.getOrPutString(gpa, "EnumField"), 16958 )).?; 16959 try mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index); 16960 try sema.ensureDeclAnalyzed(enum_field_ty_decl_index); 16961 const enum_field_ty_decl = mod.declPtr(enum_field_ty_decl_index); 16962 break :t enum_field_ty_decl.val.toType(); 16963 }; 16964 16965 const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.indexToKey(ty.toIntern()).enum_type.names.len); 16966 for (enum_field_vals, 0..) |*field_val, i| { 16967 const enum_type = ip.indexToKey(ty.toIntern()).enum_type; 16968 const value_val = if (enum_type.values.len > 0) 16969 try mod.intern_pool.getCoerced(gpa, enum_type.values[i], .comptime_int_type) 16970 else 16971 try mod.intern(.{ .int = .{ 16972 .ty = .comptime_int_type, 16973 .storage = .{ .u64 = @as(u64, @intCast(i)) }, 16974 } }); 16975 // TODO: write something like getCoercedInts to avoid needing to dupe 16976 const name = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names[i])); 16977 const name_val = v: { 16978 var anon_decl = try block.startAnonDecl(); 16979 defer anon_decl.deinit(); 16980 const new_decl_ty = try mod.arrayType(.{ 16981 .len = name.len, 16982 .child = .u8_type, 16983 }); 16984 const new_decl = try anon_decl.finish( 16985 new_decl_ty, 16986 (try mod.intern(.{ .aggregate = .{ 16987 .ty = new_decl_ty.toIntern(), 16988 .storage = .{ .bytes = name }, 16989 } })).toValue(), 16990 .none, // default alignment 16991 ); 16992 break :v try mod.intern(.{ .ptr = .{ 16993 .ty = .slice_const_u8_type, 16994 .addr = .{ .decl = new_decl }, 16995 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 16996 } }); 16997 }; 16998 16999 const enum_field_fields = .{ 17000 // name: []const u8, 17001 name_val, 17002 // value: comptime_int, 17003 value_val, 17004 }; 17005 field_val.* = try mod.intern(.{ .aggregate = .{ 17006 .ty = enum_field_ty.toIntern(), 17007 .storage = .{ .elems = &enum_field_fields }, 17008 } }); 17009 } 17010 17011 const fields_val = v: { 17012 const fields_array_ty = try mod.arrayType(.{ 17013 .len = enum_field_vals.len, 17014 .child = enum_field_ty.toIntern(), 17015 }); 17016 const new_decl = try fields_anon_decl.finish( 17017 fields_array_ty, 17018 (try mod.intern(.{ .aggregate = .{ 17019 .ty = fields_array_ty.toIntern(), 17020 .storage = .{ .elems = enum_field_vals }, 17021 } })).toValue(), 17022 .none, // default alignment 17023 ); 17024 break :v try mod.intern(.{ .ptr = .{ 17025 .ty = (try mod.ptrType(.{ 17026 .child = enum_field_ty.toIntern(), 17027 .flags = .{ 17028 .size = .Slice, 17029 .is_const = true, 17030 }, 17031 })).toIntern(), 17032 .addr = .{ .decl = new_decl }, 17033 .len = (try mod.intValue(Type.usize, enum_field_vals.len)).toIntern(), 17034 } }); 17035 }; 17036 17037 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ip.indexToKey(ty.toIntern()).enum_type.namespace); 17038 17039 const type_enum_ty = t: { 17040 const type_enum_ty_decl_index = (try sema.namespaceLookup( 17041 block, 17042 src, 17043 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17044 try ip.getOrPutString(gpa, "Enum"), 17045 )).?; 17046 try mod.declareDeclDependency(sema.owner_decl_index, type_enum_ty_decl_index); 17047 try sema.ensureDeclAnalyzed(type_enum_ty_decl_index); 17048 const type_enum_ty_decl = mod.declPtr(type_enum_ty_decl_index); 17049 break :t type_enum_ty_decl.val.toType(); 17050 }; 17051 17052 const field_values = .{ 17053 // tag_type: type, 17054 ip.indexToKey(ty.toIntern()).enum_type.tag_ty, 17055 // fields: []const EnumField, 17056 fields_val, 17057 // decls: []const Declaration, 17058 decls_val, 17059 // is_exhaustive: bool, 17060 is_exhaustive.toIntern(), 17061 }; 17062 return sema.addConstant((try mod.intern(.{ .un = .{ 17063 .ty = type_info_ty.toIntern(), 17064 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Enum))).toIntern(), 17065 .val = try mod.intern(.{ .aggregate = .{ 17066 .ty = type_enum_ty.toIntern(), 17067 .storage = .{ .elems = &field_values }, 17068 } }), 17069 } })).toValue()); 17070 }, 17071 .Union => { 17072 // TODO: look into memoizing this result. 17073 17074 var fields_anon_decl = try block.startAnonDecl(); 17075 defer fields_anon_decl.deinit(); 17076 17077 const type_union_ty = t: { 17078 const type_union_ty_decl_index = (try sema.namespaceLookup( 17079 block, 17080 src, 17081 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17082 try ip.getOrPutString(gpa, "Union"), 17083 )).?; 17084 try mod.declareDeclDependency(sema.owner_decl_index, type_union_ty_decl_index); 17085 try sema.ensureDeclAnalyzed(type_union_ty_decl_index); 17086 const type_union_ty_decl = mod.declPtr(type_union_ty_decl_index); 17087 break :t type_union_ty_decl.val.toType(); 17088 }; 17089 17090 const union_field_ty = t: { 17091 const union_field_ty_decl_index = (try sema.namespaceLookup( 17092 block, 17093 src, 17094 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17095 try ip.getOrPutString(gpa, "UnionField"), 17096 )).?; 17097 try mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index); 17098 try sema.ensureDeclAnalyzed(union_field_ty_decl_index); 17099 const union_field_ty_decl = mod.declPtr(union_field_ty_decl_index); 17100 break :t union_field_ty_decl.val.toType(); 17101 }; 17102 17103 const union_ty = try sema.resolveTypeFields(ty); 17104 try sema.resolveTypeLayout(ty); // Getting alignment requires type layout 17105 const layout = union_ty.containerLayout(mod); 17106 17107 const union_fields = union_ty.unionFields(mod); 17108 const union_field_vals = try gpa.alloc(InternPool.Index, union_fields.count()); 17109 defer gpa.free(union_field_vals); 17110 17111 for (union_field_vals, 0..) |*field_val, i| { 17112 const field = union_fields.values()[i]; 17113 // TODO: write something like getCoercedInts to avoid needing to dupe 17114 const name = try sema.arena.dupe(u8, ip.stringToSlice(union_fields.keys()[i])); 17115 const name_val = v: { 17116 var anon_decl = try block.startAnonDecl(); 17117 defer anon_decl.deinit(); 17118 const new_decl_ty = try mod.arrayType(.{ 17119 .len = name.len, 17120 .child = .u8_type, 17121 }); 17122 const new_decl = try anon_decl.finish( 17123 new_decl_ty, 17124 (try mod.intern(.{ .aggregate = .{ 17125 .ty = new_decl_ty.toIntern(), 17126 .storage = .{ .bytes = name }, 17127 } })).toValue(), 17128 .none, // default alignment 17129 ); 17130 break :v try mod.intern(.{ .ptr = .{ 17131 .ty = .slice_const_u8_type, 17132 .addr = .{ .decl = new_decl }, 17133 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17134 } }); 17135 }; 17136 17137 const alignment = switch (layout) { 17138 .Auto, .Extern => try sema.unionFieldAlignment(field), 17139 .Packed => 0, 17140 }; 17141 17142 const union_field_fields = .{ 17143 // name: []const u8, 17144 name_val, 17145 // type: type, 17146 field.ty.toIntern(), 17147 // alignment: comptime_int, 17148 (try mod.intValue(Type.comptime_int, alignment)).toIntern(), 17149 }; 17150 field_val.* = try mod.intern(.{ .aggregate = .{ 17151 .ty = union_field_ty.toIntern(), 17152 .storage = .{ .elems = &union_field_fields }, 17153 } }); 17154 } 17155 17156 const fields_val = v: { 17157 const array_fields_ty = try mod.arrayType(.{ 17158 .len = union_field_vals.len, 17159 .child = union_field_ty.toIntern(), 17160 }); 17161 const new_decl = try fields_anon_decl.finish( 17162 array_fields_ty, 17163 (try mod.intern(.{ .aggregate = .{ 17164 .ty = array_fields_ty.toIntern(), 17165 .storage = .{ .elems = union_field_vals }, 17166 } })).toValue(), 17167 .none, // default alignment 17168 ); 17169 break :v try mod.intern(.{ .ptr = .{ 17170 .ty = (try mod.ptrType(.{ 17171 .child = union_field_ty.toIntern(), 17172 .flags = .{ 17173 .size = .Slice, 17174 .is_const = true, 17175 }, 17176 })).toIntern(), 17177 .addr = .{ .decl = new_decl }, 17178 .len = (try mod.intValue(Type.usize, union_field_vals.len)).toIntern(), 17179 } }); 17180 }; 17181 17182 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespaceIndex(mod)); 17183 17184 const enum_tag_ty_val = try mod.intern(.{ .opt = .{ 17185 .ty = (try mod.optionalType(.type_type)).toIntern(), 17186 .val = if (union_ty.unionTagType(mod)) |tag_ty| tag_ty.toIntern() else .none, 17187 } }); 17188 17189 const container_layout_ty = t: { 17190 const decl_index = (try sema.namespaceLookup( 17191 block, 17192 src, 17193 (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, 17194 try ip.getOrPutString(gpa, "ContainerLayout"), 17195 )).?; 17196 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 17197 try sema.ensureDeclAnalyzed(decl_index); 17198 const decl = mod.declPtr(decl_index); 17199 break :t decl.val.toType(); 17200 }; 17201 17202 const field_values = .{ 17203 // layout: ContainerLayout, 17204 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17205 17206 // tag_type: ?type, 17207 enum_tag_ty_val, 17208 // fields: []const UnionField, 17209 fields_val, 17210 // decls: []const Declaration, 17211 decls_val, 17212 }; 17213 return sema.addConstant((try mod.intern(.{ .un = .{ 17214 .ty = type_info_ty.toIntern(), 17215 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Union))).toIntern(), 17216 .val = try mod.intern(.{ .aggregate = .{ 17217 .ty = type_union_ty.toIntern(), 17218 .storage = .{ .elems = &field_values }, 17219 } }), 17220 } })).toValue()); 17221 }, 17222 .Struct => { 17223 // TODO: look into memoizing this result. 17224 17225 var fields_anon_decl = try block.startAnonDecl(); 17226 defer fields_anon_decl.deinit(); 17227 17228 const type_struct_ty = t: { 17229 const type_struct_ty_decl_index = (try sema.namespaceLookup( 17230 block, 17231 src, 17232 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17233 try ip.getOrPutString(gpa, "Struct"), 17234 )).?; 17235 try mod.declareDeclDependency(sema.owner_decl_index, type_struct_ty_decl_index); 17236 try sema.ensureDeclAnalyzed(type_struct_ty_decl_index); 17237 const type_struct_ty_decl = mod.declPtr(type_struct_ty_decl_index); 17238 break :t type_struct_ty_decl.val.toType(); 17239 }; 17240 17241 const struct_field_ty = t: { 17242 const struct_field_ty_decl_index = (try sema.namespaceLookup( 17243 block, 17244 src, 17245 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17246 try ip.getOrPutString(gpa, "StructField"), 17247 )).?; 17248 try mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index); 17249 try sema.ensureDeclAnalyzed(struct_field_ty_decl_index); 17250 const struct_field_ty_decl = mod.declPtr(struct_field_ty_decl_index); 17251 break :t struct_field_ty_decl.val.toType(); 17252 }; 17253 17254 const struct_ty = try sema.resolveTypeFields(ty); 17255 try sema.resolveTypeLayout(ty); // Getting alignment requires type layout 17256 const layout = struct_ty.containerLayout(mod); 17257 17258 var struct_field_vals: []InternPool.Index = &.{}; 17259 defer gpa.free(struct_field_vals); 17260 fv: { 17261 const struct_type = switch (ip.indexToKey(struct_ty.toIntern())) { 17262 .anon_struct_type => |tuple| { 17263 struct_field_vals = try gpa.alloc(InternPool.Index, tuple.types.len); 17264 for (struct_field_vals, 0..) |*struct_field_val, i| { 17265 const anon_struct_type = ip.indexToKey(struct_ty.toIntern()).anon_struct_type; 17266 const field_ty = anon_struct_type.types[i]; 17267 const field_val = anon_struct_type.values[i]; 17268 const name_val = v: { 17269 var anon_decl = try block.startAnonDecl(); 17270 defer anon_decl.deinit(); 17271 // TODO: write something like getCoercedInts to avoid needing to dupe 17272 const bytes = if (tuple.names.len != 0) 17273 // https://github.com/ziglang/zig/issues/15709 17274 try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(struct_ty.toIntern()).anon_struct_type.names[i])) 17275 else 17276 try std.fmt.allocPrint(sema.arena, "{d}", .{i}); 17277 const new_decl_ty = try mod.arrayType(.{ 17278 .len = bytes.len, 17279 .child = .u8_type, 17280 }); 17281 const new_decl = try anon_decl.finish( 17282 new_decl_ty, 17283 (try mod.intern(.{ .aggregate = .{ 17284 .ty = new_decl_ty.toIntern(), 17285 .storage = .{ .bytes = bytes }, 17286 } })).toValue(), 17287 .none, // default alignment 17288 ); 17289 break :v try mod.intern(.{ .ptr = .{ 17290 .ty = .slice_const_u8_type, 17291 .addr = .{ .decl = new_decl }, 17292 .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), 17293 } }); 17294 }; 17295 17296 try sema.resolveTypeLayout(field_ty.toType()); 17297 17298 const is_comptime = field_val != .none; 17299 const opt_default_val = if (is_comptime) field_val.toValue() else null; 17300 const default_val_ptr = try sema.optRefValue(block, field_ty.toType(), opt_default_val); 17301 const struct_field_fields = .{ 17302 // name: []const u8, 17303 name_val, 17304 // type: type, 17305 field_ty, 17306 // default_value: ?*const anyopaque, 17307 default_val_ptr.toIntern(), 17308 // is_comptime: bool, 17309 Value.makeBool(is_comptime).toIntern(), 17310 // alignment: comptime_int, 17311 (try mod.intValue(Type.comptime_int, field_ty.toType().abiAlignment(mod))).toIntern(), 17312 }; 17313 struct_field_val.* = try mod.intern(.{ .aggregate = .{ 17314 .ty = struct_field_ty.toIntern(), 17315 .storage = .{ .elems = &struct_field_fields }, 17316 } }); 17317 } 17318 break :fv; 17319 }, 17320 .struct_type => |s| s, 17321 else => unreachable, 17322 }; 17323 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :fv; 17324 struct_field_vals = try gpa.alloc(InternPool.Index, struct_obj.fields.count()); 17325 17326 for ( 17327 struct_field_vals, 17328 struct_obj.fields.keys(), 17329 struct_obj.fields.values(), 17330 ) |*field_val, name_nts, field| { 17331 // TODO: write something like getCoercedInts to avoid needing to dupe 17332 const name = try sema.arena.dupe(u8, ip.stringToSlice(name_nts)); 17333 const name_val = v: { 17334 var anon_decl = try block.startAnonDecl(); 17335 defer anon_decl.deinit(); 17336 const new_decl_ty = try mod.arrayType(.{ 17337 .len = name.len, 17338 .child = .u8_type, 17339 }); 17340 const new_decl = try anon_decl.finish( 17341 new_decl_ty, 17342 (try mod.intern(.{ .aggregate = .{ 17343 .ty = new_decl_ty.toIntern(), 17344 .storage = .{ .bytes = name }, 17345 } })).toValue(), 17346 .none, // default alignment 17347 ); 17348 break :v try mod.intern(.{ .ptr = .{ 17349 .ty = .slice_const_u8_type, 17350 .addr = .{ .decl = new_decl }, 17351 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17352 } }); 17353 }; 17354 17355 const opt_default_val = if (field.default_val == .none) 17356 null 17357 else 17358 field.default_val.toValue(); 17359 const default_val_ptr = try sema.optRefValue(block, field.ty, opt_default_val); 17360 const alignment = field.alignment(mod, layout); 17361 17362 const struct_field_fields = .{ 17363 // name: []const u8, 17364 name_val, 17365 // type: type, 17366 field.ty.toIntern(), 17367 // default_value: ?*const anyopaque, 17368 default_val_ptr.toIntern(), 17369 // is_comptime: bool, 17370 Value.makeBool(field.is_comptime).toIntern(), 17371 // alignment: comptime_int, 17372 (try mod.intValue(Type.comptime_int, alignment)).toIntern(), 17373 }; 17374 field_val.* = try mod.intern(.{ .aggregate = .{ 17375 .ty = struct_field_ty.toIntern(), 17376 .storage = .{ .elems = &struct_field_fields }, 17377 } }); 17378 } 17379 } 17380 17381 const fields_val = v: { 17382 const array_fields_ty = try mod.arrayType(.{ 17383 .len = struct_field_vals.len, 17384 .child = struct_field_ty.toIntern(), 17385 }); 17386 const new_decl = try fields_anon_decl.finish( 17387 array_fields_ty, 17388 (try mod.intern(.{ .aggregate = .{ 17389 .ty = array_fields_ty.toIntern(), 17390 .storage = .{ .elems = struct_field_vals }, 17391 } })).toValue(), 17392 .none, // default alignment 17393 ); 17394 break :v try mod.intern(.{ .ptr = .{ 17395 .ty = (try mod.ptrType(.{ 17396 .child = struct_field_ty.toIntern(), 17397 .flags = .{ 17398 .size = .Slice, 17399 .is_const = true, 17400 }, 17401 })).toIntern(), 17402 .addr = .{ .decl = new_decl }, 17403 .len = (try mod.intValue(Type.usize, struct_field_vals.len)).toIntern(), 17404 } }); 17405 }; 17406 17407 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespaceIndex(mod)); 17408 17409 const backing_integer_val = try mod.intern(.{ .opt = .{ 17410 .ty = (try mod.optionalType(.type_type)).toIntern(), 17411 .val = if (layout == .Packed) val: { 17412 const struct_obj = mod.typeToStruct(struct_ty).?; 17413 assert(struct_obj.haveLayout()); 17414 assert(struct_obj.backing_int_ty.isInt(mod)); 17415 break :val struct_obj.backing_int_ty.toIntern(); 17416 } else .none, 17417 } }); 17418 17419 const container_layout_ty = t: { 17420 const decl_index = (try sema.namespaceLookup( 17421 block, 17422 src, 17423 (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, 17424 try ip.getOrPutString(gpa, "ContainerLayout"), 17425 )).?; 17426 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 17427 try sema.ensureDeclAnalyzed(decl_index); 17428 const decl = mod.declPtr(decl_index); 17429 break :t decl.val.toType(); 17430 }; 17431 17432 const field_values = [_]InternPool.Index{ 17433 // layout: ContainerLayout, 17434 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17435 // backing_integer: ?type, 17436 backing_integer_val, 17437 // fields: []const StructField, 17438 fields_val, 17439 // decls: []const Declaration, 17440 decls_val, 17441 // is_tuple: bool, 17442 Value.makeBool(struct_ty.isTuple(mod)).toIntern(), 17443 }; 17444 return sema.addConstant((try mod.intern(.{ .un = .{ 17445 .ty = type_info_ty.toIntern(), 17446 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Struct))).toIntern(), 17447 .val = try mod.intern(.{ .aggregate = .{ 17448 .ty = type_struct_ty.toIntern(), 17449 .storage = .{ .elems = &field_values }, 17450 } }), 17451 } })).toValue()); 17452 }, 17453 .Opaque => { 17454 // TODO: look into memoizing this result. 17455 17456 const type_opaque_ty = t: { 17457 const type_opaque_ty_decl_index = (try sema.namespaceLookup( 17458 block, 17459 src, 17460 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17461 try ip.getOrPutString(gpa, "Opaque"), 17462 )).?; 17463 try mod.declareDeclDependency(sema.owner_decl_index, type_opaque_ty_decl_index); 17464 try sema.ensureDeclAnalyzed(type_opaque_ty_decl_index); 17465 const type_opaque_ty_decl = mod.declPtr(type_opaque_ty_decl_index); 17466 break :t type_opaque_ty_decl.val.toType(); 17467 }; 17468 17469 const opaque_ty = try sema.resolveTypeFields(ty); 17470 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespaceIndex(mod)); 17471 17472 const field_values = .{ 17473 // decls: []const Declaration, 17474 decls_val, 17475 }; 17476 return sema.addConstant((try mod.intern(.{ .un = .{ 17477 .ty = type_info_ty.toIntern(), 17478 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Opaque))).toIntern(), 17479 .val = try mod.intern(.{ .aggregate = .{ 17480 .ty = type_opaque_ty.toIntern(), 17481 .storage = .{ .elems = &field_values }, 17482 } }), 17483 } })).toValue()); 17484 }, 17485 .Frame => return sema.failWithUseOfAsync(block, src), 17486 .AnyFrame => return sema.failWithUseOfAsync(block, src), 17487 } 17488 } 17489 17490 fn typeInfoDecls( 17491 sema: *Sema, 17492 block: *Block, 17493 src: LazySrcLoc, 17494 type_info_ty: Type, 17495 opt_namespace: Module.Namespace.OptionalIndex, 17496 ) CompileError!InternPool.Index { 17497 const mod = sema.mod; 17498 const gpa = sema.gpa; 17499 17500 var decls_anon_decl = try block.startAnonDecl(); 17501 defer decls_anon_decl.deinit(); 17502 17503 const declaration_ty = t: { 17504 const declaration_ty_decl_index = (try sema.namespaceLookup( 17505 block, 17506 src, 17507 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17508 try mod.intern_pool.getOrPutString(gpa, "Declaration"), 17509 )).?; 17510 try mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index); 17511 try sema.ensureDeclAnalyzed(declaration_ty_decl_index); 17512 const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index); 17513 break :t declaration_ty_decl.val.toType(); 17514 }; 17515 try sema.queueFullTypeResolution(declaration_ty); 17516 17517 var decl_vals = std.ArrayList(InternPool.Index).init(gpa); 17518 defer decl_vals.deinit(); 17519 17520 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); 17521 defer seen_namespaces.deinit(); 17522 17523 if (opt_namespace.unwrap()) |namespace_index| { 17524 const namespace = mod.namespacePtr(namespace_index); 17525 try sema.typeInfoNamespaceDecls(block, namespace, declaration_ty, &decl_vals, &seen_namespaces); 17526 } 17527 17528 const array_decl_ty = try mod.arrayType(.{ 17529 .len = decl_vals.items.len, 17530 .child = declaration_ty.toIntern(), 17531 }); 17532 const new_decl = try decls_anon_decl.finish( 17533 array_decl_ty, 17534 (try mod.intern(.{ .aggregate = .{ 17535 .ty = array_decl_ty.toIntern(), 17536 .storage = .{ .elems = decl_vals.items }, 17537 } })).toValue(), 17538 .none, // default alignment 17539 ); 17540 return try mod.intern(.{ .ptr = .{ 17541 .ty = (try mod.ptrType(.{ 17542 .child = declaration_ty.toIntern(), 17543 .flags = .{ 17544 .size = .Slice, 17545 .is_const = true, 17546 }, 17547 })).toIntern(), 17548 .addr = .{ .decl = new_decl }, 17549 .len = (try mod.intValue(Type.usize, decl_vals.items.len)).toIntern(), 17550 } }); 17551 } 17552 17553 fn typeInfoNamespaceDecls( 17554 sema: *Sema, 17555 block: *Block, 17556 namespace: *Namespace, 17557 declaration_ty: Type, 17558 decl_vals: *std.ArrayList(InternPool.Index), 17559 seen_namespaces: *std.AutoHashMap(*Namespace, void), 17560 ) !void { 17561 const mod = sema.mod; 17562 const ip = &mod.intern_pool; 17563 const gop = try seen_namespaces.getOrPut(namespace); 17564 if (gop.found_existing) return; 17565 const decls = namespace.decls.keys(); 17566 for (decls) |decl_index| { 17567 const decl = mod.declPtr(decl_index); 17568 if (decl.kind == .@"usingnamespace") { 17569 if (decl.analysis == .in_progress) continue; 17570 try mod.ensureDeclAnalyzed(decl_index); 17571 const new_ns = decl.val.toType().getNamespace(mod).?; 17572 try sema.typeInfoNamespaceDecls(block, new_ns, declaration_ty, decl_vals, seen_namespaces); 17573 continue; 17574 } 17575 if (decl.kind != .named) continue; 17576 const name_val = v: { 17577 var anon_decl = try block.startAnonDecl(); 17578 defer anon_decl.deinit(); 17579 // TODO: write something like getCoercedInts to avoid needing to dupe 17580 const name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name)); 17581 const new_decl_ty = try mod.arrayType(.{ 17582 .len = name.len, 17583 .child = .u8_type, 17584 }); 17585 const new_decl = try anon_decl.finish( 17586 new_decl_ty, 17587 (try mod.intern(.{ .aggregate = .{ 17588 .ty = new_decl_ty.toIntern(), 17589 .storage = .{ .bytes = name }, 17590 } })).toValue(), 17591 .none, // default alignment 17592 ); 17593 break :v try mod.intern(.{ .ptr = .{ 17594 .ty = .slice_const_u8_type, 17595 .addr = .{ .decl = new_decl }, 17596 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17597 } }); 17598 }; 17599 17600 const fields = .{ 17601 //name: []const u8, 17602 name_val, 17603 //is_pub: bool, 17604 Value.makeBool(decl.is_pub).toIntern(), 17605 }; 17606 try decl_vals.append(try mod.intern(.{ .aggregate = .{ 17607 .ty = declaration_ty.toIntern(), 17608 .storage = .{ .elems = &fields }, 17609 } })); 17610 } 17611 } 17612 17613 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17614 _ = block; 17615 const zir_datas = sema.code.instructions.items(.data); 17616 const inst_data = zir_datas[inst].un_node; 17617 const operand = try sema.resolveInst(inst_data.operand); 17618 const operand_ty = sema.typeOf(operand); 17619 return sema.addType(operand_ty); 17620 } 17621 17622 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17623 const pl_node = sema.code.instructions.items(.data)[inst].pl_node; 17624 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 17625 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 17626 17627 var child_block: Block = .{ 17628 .parent = block, 17629 .sema = sema, 17630 .src_decl = block.src_decl, 17631 .namespace = block.namespace, 17632 .wip_capture_scope = block.wip_capture_scope, 17633 .instructions = .{}, 17634 .inlining = block.inlining, 17635 .is_comptime = false, 17636 .is_typeof = true, 17637 .want_safety = false, 17638 .error_return_trace_index = block.error_return_trace_index, 17639 }; 17640 defer child_block.instructions.deinit(sema.gpa); 17641 17642 const operand = try sema.resolveBody(&child_block, body, inst); 17643 const operand_ty = sema.typeOf(operand); 17644 if (operand_ty.isGenericPoison()) return error.GenericPoison; 17645 return sema.addType(operand_ty); 17646 } 17647 17648 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17649 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17650 const src = inst_data.src(); 17651 const operand = try sema.resolveInst(inst_data.operand); 17652 const operand_ty = sema.typeOf(operand); 17653 const res_ty = try sema.log2IntType(block, operand_ty, src); 17654 return sema.addType(res_ty); 17655 } 17656 17657 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type { 17658 const mod = sema.mod; 17659 switch (operand.zigTypeTag(mod)) { 17660 .ComptimeInt => return Type.comptime_int, 17661 .Int => { 17662 const bits = operand.bitSize(mod); 17663 const count = if (bits == 0) 17664 0 17665 else blk: { 17666 var count: u16 = 0; 17667 var s = bits - 1; 17668 while (s != 0) : (s >>= 1) { 17669 count += 1; 17670 } 17671 break :blk count; 17672 }; 17673 return mod.intType(.unsigned, count); 17674 }, 17675 .Vector => { 17676 const elem_ty = operand.elemType2(mod); 17677 const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); 17678 return mod.vectorType(.{ 17679 .len = operand.vectorLen(mod), 17680 .child = log2_elem_ty.toIntern(), 17681 }); 17682 }, 17683 else => {}, 17684 } 17685 return sema.fail( 17686 block, 17687 src, 17688 "bit shifting operation expected integer type, found '{}'", 17689 .{operand.fmt(mod)}, 17690 ); 17691 } 17692 17693 fn zirTypeofPeer( 17694 sema: *Sema, 17695 block: *Block, 17696 extended: Zir.Inst.Extended.InstData, 17697 ) CompileError!Air.Inst.Ref { 17698 const tracy = trace(@src()); 17699 defer tracy.end(); 17700 17701 const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); 17702 const src = LazySrcLoc.nodeOffset(extra.data.src_node); 17703 const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len]; 17704 17705 var child_block: Block = .{ 17706 .parent = block, 17707 .sema = sema, 17708 .src_decl = block.src_decl, 17709 .namespace = block.namespace, 17710 .wip_capture_scope = block.wip_capture_scope, 17711 .instructions = .{}, 17712 .inlining = block.inlining, 17713 .is_comptime = false, 17714 .is_typeof = true, 17715 .runtime_cond = block.runtime_cond, 17716 .runtime_loop = block.runtime_loop, 17717 .runtime_index = block.runtime_index, 17718 }; 17719 defer child_block.instructions.deinit(sema.gpa); 17720 // Ignore the result, we only care about the instructions in `args`. 17721 _ = try sema.analyzeBodyBreak(&child_block, body); 17722 17723 const args = sema.code.refSlice(extra.end, extended.small); 17724 17725 const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len); 17726 defer sema.gpa.free(inst_list); 17727 17728 for (args, 0..) |arg_ref, i| { 17729 inst_list[i] = try sema.resolveInst(arg_ref); 17730 } 17731 17732 const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); 17733 return sema.addType(result_type); 17734 } 17735 17736 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17737 const tracy = trace(@src()); 17738 defer tracy.end(); 17739 17740 const mod = sema.mod; 17741 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17742 const src = inst_data.src(); 17743 const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 17744 const uncasted_operand = try sema.resolveInst(inst_data.operand); 17745 17746 const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src); 17747 if (try sema.resolveMaybeUndefVal(operand)) |val| { 17748 return if (val.isUndef(mod)) 17749 sema.addConstUndef(Type.bool) 17750 else if (val.toBool()) 17751 Air.Inst.Ref.bool_false 17752 else 17753 Air.Inst.Ref.bool_true; 17754 } 17755 try sema.requireRuntimeBlock(block, src, null); 17756 return block.addTyOp(.not, Type.bool, operand); 17757 } 17758 17759 fn zirBoolBr( 17760 sema: *Sema, 17761 parent_block: *Block, 17762 inst: Zir.Inst.Index, 17763 is_bool_or: bool, 17764 ) CompileError!Air.Inst.Ref { 17765 const tracy = trace(@src()); 17766 defer tracy.end(); 17767 17768 const mod = sema.mod; 17769 const datas = sema.code.instructions.items(.data); 17770 const inst_data = datas[inst].bool_br; 17771 const lhs = try sema.resolveInst(inst_data.lhs); 17772 const lhs_src = sema.src; 17773 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 17774 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 17775 const gpa = sema.gpa; 17776 17777 if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| { 17778 if (is_bool_or and lhs_val.toBool()) { 17779 return Air.Inst.Ref.bool_true; 17780 } else if (!is_bool_or and !lhs_val.toBool()) { 17781 return Air.Inst.Ref.bool_false; 17782 } 17783 // comptime-known left-hand side. No need for a block here; the result 17784 // is simply the rhs expression. Here we rely on there only being 1 17785 // break instruction (`break_inline`). 17786 return sema.resolveBody(parent_block, body, inst); 17787 } 17788 17789 const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 17790 try sema.air_instructions.append(gpa, .{ 17791 .tag = .block, 17792 .data = .{ .ty_pl = .{ 17793 .ty = .bool_type, 17794 .payload = undefined, 17795 } }, 17796 }); 17797 17798 var child_block = parent_block.makeSubBlock(); 17799 child_block.runtime_loop = null; 17800 child_block.runtime_cond = lhs_src; 17801 child_block.runtime_index.increment(); 17802 defer child_block.instructions.deinit(gpa); 17803 17804 var then_block = child_block.makeSubBlock(); 17805 defer then_block.instructions.deinit(gpa); 17806 17807 var else_block = child_block.makeSubBlock(); 17808 defer else_block.instructions.deinit(gpa); 17809 17810 const lhs_block = if (is_bool_or) &then_block else &else_block; 17811 const rhs_block = if (is_bool_or) &else_block else &then_block; 17812 17813 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false; 17814 _ = try lhs_block.addBr(block_inst, lhs_result); 17815 17816 const rhs_result = try sema.resolveBody(rhs_block, body, inst); 17817 if (!sema.typeOf(rhs_result).isNoReturn(mod)) { 17818 _ = try rhs_block.addBr(block_inst, rhs_result); 17819 } 17820 17821 const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst); 17822 if (!sema.typeOf(rhs_result).isNoReturn(mod)) { 17823 if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| { 17824 if (is_bool_or and rhs_val.toBool()) { 17825 return Air.Inst.Ref.bool_true; 17826 } else if (!is_bool_or and !rhs_val.toBool()) { 17827 return Air.Inst.Ref.bool_false; 17828 } 17829 } 17830 } 17831 17832 return result; 17833 } 17834 17835 fn finishCondBr( 17836 sema: *Sema, 17837 parent_block: *Block, 17838 child_block: *Block, 17839 then_block: *Block, 17840 else_block: *Block, 17841 cond: Air.Inst.Ref, 17842 block_inst: Air.Inst.Index, 17843 ) !Air.Inst.Ref { 17844 const gpa = sema.gpa; 17845 17846 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 17847 then_block.instructions.items.len + else_block.instructions.items.len + 17848 @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1); 17849 17850 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 17851 .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)), 17852 .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)), 17853 }); 17854 sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items); 17855 sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); 17856 17857 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 17858 .operand = cond, 17859 .payload = cond_br_payload, 17860 } } }); 17861 17862 sema.air_instructions.items(.data)[block_inst].ty_pl.payload = sema.addExtraAssumeCapacity( 17863 Air.Block{ .body_len = @as(u32, @intCast(child_block.instructions.items.len)) }, 17864 ); 17865 sema.air_extra.appendSliceAssumeCapacity(child_block.instructions.items); 17866 17867 try parent_block.instructions.append(gpa, block_inst); 17868 return Air.indexToRef(block_inst); 17869 } 17870 17871 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 17872 const mod = sema.mod; 17873 switch (ty.zigTypeTag(mod)) { 17874 .Optional, .Null, .Undefined => return, 17875 .Pointer => if (ty.isPtrLikeOptional(mod)) return, 17876 else => {}, 17877 } 17878 return sema.failWithExpectedOptionalType(block, src, ty); 17879 } 17880 17881 fn zirIsNonNull( 17882 sema: *Sema, 17883 block: *Block, 17884 inst: Zir.Inst.Index, 17885 ) CompileError!Air.Inst.Ref { 17886 const tracy = trace(@src()); 17887 defer tracy.end(); 17888 17889 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17890 const src = inst_data.src(); 17891 const operand = try sema.resolveInst(inst_data.operand); 17892 try sema.checkNullableType(block, src, sema.typeOf(operand)); 17893 return sema.analyzeIsNull(block, src, operand, true); 17894 } 17895 17896 fn zirIsNonNullPtr( 17897 sema: *Sema, 17898 block: *Block, 17899 inst: Zir.Inst.Index, 17900 ) CompileError!Air.Inst.Ref { 17901 const tracy = trace(@src()); 17902 defer tracy.end(); 17903 17904 const mod = sema.mod; 17905 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17906 const src = inst_data.src(); 17907 const ptr = try sema.resolveInst(inst_data.operand); 17908 try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(mod)); 17909 if ((try sema.resolveMaybeUndefVal(ptr)) == null) { 17910 return block.addUnOp(.is_non_null_ptr, ptr); 17911 } 17912 const loaded = try sema.analyzeLoad(block, src, ptr, src); 17913 return sema.analyzeIsNull(block, src, loaded, true); 17914 } 17915 17916 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 17917 const mod = sema.mod; 17918 switch (ty.zigTypeTag(mod)) { 17919 .ErrorSet, .ErrorUnion, .Undefined => return, 17920 else => return sema.fail(block, src, "expected error union type, found '{}'", .{ 17921 ty.fmt(mod), 17922 }), 17923 } 17924 } 17925 17926 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17927 const tracy = trace(@src()); 17928 defer tracy.end(); 17929 17930 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17931 const src = inst_data.src(); 17932 const operand = try sema.resolveInst(inst_data.operand); 17933 try sema.checkErrorType(block, src, sema.typeOf(operand)); 17934 return sema.analyzeIsNonErr(block, src, operand); 17935 } 17936 17937 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17938 const tracy = trace(@src()); 17939 defer tracy.end(); 17940 17941 const mod = sema.mod; 17942 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17943 const src = inst_data.src(); 17944 const ptr = try sema.resolveInst(inst_data.operand); 17945 try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(mod)); 17946 const loaded = try sema.analyzeLoad(block, src, ptr, src); 17947 return sema.analyzeIsNonErr(block, src, loaded); 17948 } 17949 17950 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17951 const tracy = trace(@src()); 17952 defer tracy.end(); 17953 17954 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17955 const src = inst_data.src(); 17956 const operand = try sema.resolveInst(inst_data.operand); 17957 return sema.analyzeIsNonErr(block, src, operand); 17958 } 17959 17960 fn zirCondbr( 17961 sema: *Sema, 17962 parent_block: *Block, 17963 inst: Zir.Inst.Index, 17964 ) CompileError!Zir.Inst.Index { 17965 const tracy = trace(@src()); 17966 defer tracy.end(); 17967 17968 const mod = sema.mod; 17969 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 17970 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; 17971 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 17972 17973 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; 17974 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; 17975 17976 const uncasted_cond = try sema.resolveInst(extra.data.condition); 17977 const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src); 17978 17979 if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| { 17980 const body = if (cond_val.toBool()) then_body else else_body; 17981 17982 try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src); 17983 // We use `analyzeBodyInner` since we want to propagate any possible 17984 // `error.ComptimeBreak` to the caller. 17985 return sema.analyzeBodyInner(parent_block, body); 17986 } 17987 17988 const gpa = sema.gpa; 17989 17990 // We'll re-use the sub block to save on memory bandwidth, and yank out the 17991 // instructions array in between using it for the then block and else block. 17992 var sub_block = parent_block.makeSubBlock(); 17993 sub_block.runtime_loop = null; 17994 sub_block.runtime_cond = cond_src; 17995 sub_block.runtime_index.increment(); 17996 defer sub_block.instructions.deinit(gpa); 17997 17998 try sema.analyzeBodyRuntimeBreak(&sub_block, then_body); 17999 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); 18000 defer gpa.free(true_instructions); 18001 18002 const err_cond = blk: { 18003 const index = Zir.refToIndex(extra.data.condition) orelse break :blk null; 18004 if (sema.code.instructions.items(.tag)[index] != .is_non_err) break :blk null; 18005 18006 const err_inst_data = sema.code.instructions.items(.data)[index].un_node; 18007 const err_operand = try sema.resolveInst(err_inst_data.operand); 18008 const operand_ty = sema.typeOf(err_operand); 18009 assert(operand_ty.zigTypeTag(mod) == .ErrorUnion); 18010 const result_ty = operand_ty.errorUnionSet(mod); 18011 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); 18012 }; 18013 18014 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) { 18015 // nothing to do 18016 } else { 18017 try sema.analyzeBodyRuntimeBreak(&sub_block, else_body); 18018 } 18019 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 18020 true_instructions.len + sub_block.instructions.items.len); 18021 _ = try parent_block.addInst(.{ 18022 .tag = .cond_br, 18023 .data = .{ .pl_op = .{ 18024 .operand = cond, 18025 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18026 .then_body_len = @as(u32, @intCast(true_instructions.len)), 18027 .else_body_len = @as(u32, @intCast(sub_block.instructions.items.len)), 18028 }), 18029 } }, 18030 }); 18031 sema.air_extra.appendSliceAssumeCapacity(true_instructions); 18032 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); 18033 return always_noreturn; 18034 } 18035 18036 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18037 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 18038 const src = inst_data.src(); 18039 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 18040 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18041 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 18042 const err_union = try sema.resolveInst(extra.data.operand); 18043 const err_union_ty = sema.typeOf(err_union); 18044 const mod = sema.mod; 18045 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 18046 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ 18047 err_union_ty.fmt(mod), 18048 }); 18049 } 18050 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18051 if (is_non_err != .none) { 18052 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18053 if (is_non_err_val.toBool()) { 18054 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); 18055 } 18056 // We can analyze the body directly in the parent block because we know there are 18057 // no breaks from the body possible, and that the body is noreturn. 18058 return sema.resolveBody(parent_block, body, inst); 18059 } 18060 18061 var sub_block = parent_block.makeSubBlock(); 18062 defer sub_block.instructions.deinit(sema.gpa); 18063 18064 // This body is guaranteed to end with noreturn and has no breaks. 18065 _ = try sema.analyzeBodyInner(&sub_block, body); 18066 18067 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + 18068 sub_block.instructions.items.len); 18069 const try_inst = try parent_block.addInst(.{ 18070 .tag = .@"try", 18071 .data = .{ .pl_op = .{ 18072 .operand = err_union, 18073 .payload = sema.addExtraAssumeCapacity(Air.Try{ 18074 .body_len = @as(u32, @intCast(sub_block.instructions.items.len)), 18075 }), 18076 } }, 18077 }); 18078 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); 18079 return try_inst; 18080 } 18081 18082 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18083 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 18084 const src = inst_data.src(); 18085 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 18086 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18087 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 18088 const operand = try sema.resolveInst(extra.data.operand); 18089 const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); 18090 const err_union_ty = sema.typeOf(err_union); 18091 const mod = sema.mod; 18092 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 18093 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ 18094 err_union_ty.fmt(mod), 18095 }); 18096 } 18097 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18098 if (is_non_err != .none) { 18099 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18100 if (is_non_err_val.toBool()) { 18101 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); 18102 } 18103 // We can analyze the body directly in the parent block because we know there are 18104 // no breaks from the body possible, and that the body is noreturn. 18105 return sema.resolveBody(parent_block, body, inst); 18106 } 18107 18108 var sub_block = parent_block.makeSubBlock(); 18109 defer sub_block.instructions.deinit(sema.gpa); 18110 18111 // This body is guaranteed to end with noreturn and has no breaks. 18112 _ = try sema.analyzeBodyInner(&sub_block, body); 18113 18114 const operand_ty = sema.typeOf(operand); 18115 const ptr_info = operand_ty.ptrInfo(mod); 18116 const res_ty = try mod.ptrType(.{ 18117 .child = err_union_ty.errorUnionPayload(mod).toIntern(), 18118 .flags = .{ 18119 .is_const = ptr_info.flags.is_const, 18120 .is_volatile = ptr_info.flags.is_volatile, 18121 .is_allowzero = ptr_info.flags.is_allowzero, 18122 .address_space = ptr_info.flags.address_space, 18123 }, 18124 }); 18125 const res_ty_ref = try sema.addType(res_ty); 18126 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len + 18127 sub_block.instructions.items.len); 18128 const try_inst = try parent_block.addInst(.{ 18129 .tag = .try_ptr, 18130 .data = .{ .ty_pl = .{ 18131 .ty = res_ty_ref, 18132 .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ 18133 .ptr = operand, 18134 .body_len = @as(u32, @intCast(sub_block.instructions.items.len)), 18135 }), 18136 } }, 18137 }); 18138 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); 18139 return try_inst; 18140 } 18141 18142 // A `break` statement is inside a runtime condition, but trying to 18143 // break from an inline loop. In such case we must convert it to 18144 // a runtime break. 18145 fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void { 18146 const gop = sema.inst_map.getOrPutAssumeCapacity(break_data.block_inst); 18147 const labeled_block = if (!gop.found_existing) blk: { 18148 try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); 18149 18150 const new_block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 18151 gop.value_ptr.* = Air.indexToRef(new_block_inst); 18152 try sema.air_instructions.append(sema.gpa, .{ 18153 .tag = .block, 18154 .data = undefined, 18155 }); 18156 const labeled_block = try sema.gpa.create(LabeledBlock); 18157 labeled_block.* = .{ 18158 .label = .{ 18159 .zir_block = break_data.block_inst, 18160 .merges = .{ 18161 .src_locs = .{}, 18162 .results = .{}, 18163 .br_list = .{}, 18164 .block_inst = new_block_inst, 18165 }, 18166 }, 18167 .block = .{ 18168 .parent = child_block, 18169 .sema = sema, 18170 .src_decl = child_block.src_decl, 18171 .namespace = child_block.namespace, 18172 .wip_capture_scope = child_block.wip_capture_scope, 18173 .instructions = .{}, 18174 .label = &labeled_block.label, 18175 .inlining = child_block.inlining, 18176 .is_comptime = child_block.is_comptime, 18177 }, 18178 }; 18179 sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); 18180 break :blk labeled_block; 18181 } else blk: { 18182 const new_block_inst = Air.refToIndex(gop.value_ptr.*).?; 18183 const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?; 18184 break :blk labeled_block; 18185 }; 18186 18187 const operand = try sema.resolveInst(break_data.operand); 18188 const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); 18189 try labeled_block.label.merges.results.append(sema.gpa, operand); 18190 try labeled_block.label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?); 18191 labeled_block.block.runtime_index.increment(); 18192 if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { 18193 labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; 18194 labeled_block.block.runtime_loop = child_block.runtime_loop; 18195 } 18196 } 18197 18198 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18199 const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable"; 18200 const src = inst_data.src(); 18201 18202 if (block.is_comptime) { 18203 return sema.fail(block, src, "reached unreachable code", .{}); 18204 } 18205 // TODO Add compile error for @optimizeFor occurring too late in a scope. 18206 try block.addUnreachable(true); 18207 return always_noreturn; 18208 } 18209 18210 fn zirRetErrValue( 18211 sema: *Sema, 18212 block: *Block, 18213 inst: Zir.Inst.Index, 18214 ) CompileError!Zir.Inst.Index { 18215 const mod = sema.mod; 18216 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 18217 const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 18218 _ = try mod.getErrorValue(err_name); 18219 const src = inst_data.src(); 18220 // Return the error code from the function. 18221 const error_set_type = try mod.singleErrorSetType(err_name); 18222 const result_inst = try sema.addConstant((try mod.intern(.{ .err = .{ 18223 .ty = error_set_type.toIntern(), 18224 .name = err_name, 18225 } })).toValue()); 18226 return sema.analyzeRet(block, result_inst, src); 18227 } 18228 18229 fn zirRetImplicit( 18230 sema: *Sema, 18231 block: *Block, 18232 inst: Zir.Inst.Index, 18233 ) CompileError!Zir.Inst.Index { 18234 const tracy = trace(@src()); 18235 defer tracy.end(); 18236 18237 const mod = sema.mod; 18238 const inst_data = sema.code.instructions.items(.data)[inst].un_tok; 18239 const operand = try sema.resolveInst(inst_data.operand); 18240 18241 const r_brace_src = inst_data.src(); 18242 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 18243 const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod); 18244 if (base_tag == .NoReturn) { 18245 const msg = msg: { 18246 const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{ 18247 sema.fn_ret_ty.fmt(mod), 18248 }); 18249 errdefer msg.destroy(sema.gpa); 18250 try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); 18251 break :msg msg; 18252 }; 18253 return sema.failWithOwnedErrorMsg(msg); 18254 } else if (base_tag != .Void) { 18255 const msg = msg: { 18256 const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{ 18257 sema.fn_ret_ty.fmt(mod), 18258 }); 18259 errdefer msg.destroy(sema.gpa); 18260 try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); 18261 break :msg msg; 18262 }; 18263 return sema.failWithOwnedErrorMsg(msg); 18264 } 18265 18266 return sema.analyzeRet(block, operand, .unneeded); 18267 } 18268 18269 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18270 const tracy = trace(@src()); 18271 defer tracy.end(); 18272 18273 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 18274 const operand = try sema.resolveInst(inst_data.operand); 18275 const src = inst_data.src(); 18276 18277 return sema.analyzeRet(block, operand, src); 18278 } 18279 18280 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18281 const tracy = trace(@src()); 18282 defer tracy.end(); 18283 18284 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 18285 const src = inst_data.src(); 18286 const ret_ptr = try sema.resolveInst(inst_data.operand); 18287 18288 if (block.is_comptime or block.inlining != null) { 18289 const operand = try sema.analyzeLoad(block, src, ret_ptr, src); 18290 return sema.analyzeRet(block, operand, src); 18291 } 18292 18293 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18294 const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); 18295 return sema.retWithErrTracing(block, is_non_err, .ret_load, ret_ptr); 18296 } 18297 18298 _ = try block.addUnOp(.ret_load, ret_ptr); 18299 return always_noreturn; 18300 } 18301 18302 fn retWithErrTracing( 18303 sema: *Sema, 18304 block: *Block, 18305 is_non_err: Air.Inst.Ref, 18306 ret_tag: Air.Inst.Tag, 18307 operand: Air.Inst.Ref, 18308 ) CompileError!Zir.Inst.Index { 18309 const mod = sema.mod; 18310 const need_check = switch (is_non_err) { 18311 .bool_true => { 18312 _ = try block.addUnOp(ret_tag, operand); 18313 return always_noreturn; 18314 }, 18315 .bool_false => false, 18316 else => true, 18317 }; 18318 const gpa = sema.gpa; 18319 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 18320 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 18321 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 18322 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); 18323 const return_err_fn = try sema.getBuiltin("returnError"); 18324 const args: [1]Air.Inst.Ref = .{err_return_trace}; 18325 18326 if (!need_check) { 18327 try sema.callBuiltin(block, return_err_fn, .never_inline, &args); 18328 _ = try block.addUnOp(ret_tag, operand); 18329 return always_noreturn; 18330 } 18331 18332 var then_block = block.makeSubBlock(); 18333 defer then_block.instructions.deinit(gpa); 18334 _ = try then_block.addUnOp(ret_tag, operand); 18335 18336 var else_block = block.makeSubBlock(); 18337 defer else_block.instructions.deinit(gpa); 18338 try sema.callBuiltin(&else_block, return_err_fn, .never_inline, &args); 18339 _ = try else_block.addUnOp(ret_tag, operand); 18340 18341 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 18342 then_block.instructions.items.len + else_block.instructions.items.len + 18343 @typeInfo(Air.Block).Struct.fields.len + 1); 18344 18345 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18346 .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)), 18347 .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)), 18348 }); 18349 sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items); 18350 sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); 18351 18352 _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 18353 .operand = is_non_err, 18354 .payload = cond_br_payload, 18355 } } }); 18356 18357 return always_noreturn; 18358 } 18359 18360 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { 18361 const mod = sema.mod; 18362 if (!mod.backendSupportsFeature(.error_return_trace)) return false; 18363 18364 return fn_ret_ty.isError(mod) and 18365 mod.comp.bin_file.options.error_return_tracing; 18366 } 18367 18368 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18369 const mod = sema.mod; 18370 const inst_data = sema.code.instructions.items(.data)[inst].save_err_ret_index; 18371 18372 if (!mod.backendSupportsFeature(.error_return_trace)) return; 18373 if (!mod.comp.bin_file.options.error_return_tracing) return; 18374 18375 // This is only relevant at runtime. 18376 if (block.is_comptime or block.is_typeof) return; 18377 18378 const save_index = inst_data.operand == .none or b: { 18379 const operand = try sema.resolveInst(inst_data.operand); 18380 const operand_ty = sema.typeOf(operand); 18381 break :b operand_ty.isError(mod); 18382 }; 18383 18384 if (save_index) 18385 block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block); 18386 } 18387 18388 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { 18389 const inst_data = sema.code.instructions.items(.data)[inst].restore_err_ret_index; 18390 const src = sema.src; // TODO 18391 18392 // This is only relevant at runtime. 18393 if (start_block.is_comptime or start_block.is_typeof) return; 18394 18395 if (!sema.mod.backendSupportsFeature(.error_return_trace)) return; 18396 if (!sema.owner_func.?.calls_or_awaits_errorable_fn) return; 18397 if (!sema.mod.comp.bin_file.options.error_return_tracing) return; 18398 18399 const tracy = trace(@src()); 18400 defer tracy.end(); 18401 18402 const saved_index = if (Zir.refToIndexAllowNone(inst_data.block)) |zir_block| b: { 18403 var block = start_block; 18404 while (true) { 18405 if (block.label) |label| { 18406 if (label.zir_block == zir_block) { 18407 const target_trace_index = if (block.parent) |parent_block| tgt: { 18408 break :tgt parent_block.error_return_trace_index; 18409 } else sema.error_return_trace_index_on_fn_entry; 18410 18411 if (start_block.error_return_trace_index != target_trace_index) 18412 break :b target_trace_index; 18413 18414 return; // No need to restore 18415 } 18416 } 18417 block = block.parent.?; 18418 } 18419 } else b: { 18420 if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry) 18421 break :b sema.error_return_trace_index_on_fn_entry; 18422 18423 return; // No need to restore 18424 }; 18425 18426 assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere 18427 18428 const operand = try sema.resolveInstAllowNone(inst_data.operand); 18429 return sema.popErrorReturnTrace(start_block, src, operand, saved_index); 18430 } 18431 18432 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { 18433 const mod = sema.mod; 18434 const gpa = sema.gpa; 18435 const ip = &mod.intern_pool; 18436 assert(sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion); 18437 18438 if (mod.typeToInferredErrorSet(sema.fn_ret_ty.errorUnionSet(mod))) |ies| { 18439 const op_ty = sema.typeOf(uncasted_operand); 18440 switch (op_ty.zigTypeTag(mod)) { 18441 .ErrorSet => try ies.addErrorSet(op_ty, ip, gpa), 18442 .ErrorUnion => try ies.addErrorSet(op_ty.errorUnionSet(mod), ip, gpa), 18443 else => {}, 18444 } 18445 } 18446 } 18447 18448 fn analyzeRet( 18449 sema: *Sema, 18450 block: *Block, 18451 uncasted_operand: Air.Inst.Ref, 18452 src: LazySrcLoc, 18453 ) CompileError!Zir.Inst.Index { 18454 // Special case for returning an error to an inferred error set; we need to 18455 // add the error tag to the inferred error set of the in-scope function, so 18456 // that the coercion below works correctly. 18457 const mod = sema.mod; 18458 if (sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) { 18459 try sema.addToInferredErrorSet(uncasted_operand); 18460 } 18461 const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) { 18462 error.NotCoercible => unreachable, 18463 else => |e| return e, 18464 }; 18465 18466 if (block.inlining) |inlining| { 18467 if (block.is_comptime) { 18468 _ = try sema.resolveConstMaybeUndefVal(block, src, operand, "value being returned at comptime must be comptime-known"); 18469 inlining.comptime_result = operand; 18470 return error.ComptimeReturn; 18471 } 18472 // We are inlining a function call; rewrite the `ret` as a `break`. 18473 try inlining.merges.results.append(sema.gpa, operand); 18474 _ = try block.addBr(inlining.merges.block_inst, operand); 18475 return always_noreturn; 18476 } else if (block.is_comptime) { 18477 return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{}); 18478 } 18479 18480 try sema.resolveTypeLayout(sema.fn_ret_ty); 18481 18482 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18483 // Avoid adding a frame to the error return trace in case the value is comptime-known 18484 // to be not an error. 18485 const is_non_err = try sema.analyzeIsNonErr(block, src, operand); 18486 return sema.retWithErrTracing(block, is_non_err, .ret, operand); 18487 } 18488 18489 _ = try block.addUnOp(.ret, operand); 18490 18491 return always_noreturn; 18492 } 18493 18494 fn floatOpAllowed(tag: Zir.Inst.Tag) bool { 18495 // extend this swich as additional operators are implemented 18496 return switch (tag) { 18497 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true, 18498 else => false, 18499 }; 18500 } 18501 18502 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18503 const tracy = trace(@src()); 18504 defer tracy.end(); 18505 18506 const mod = sema.mod; 18507 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; 18508 const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); 18509 const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node }; 18510 const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node }; 18511 const align_src: LazySrcLoc = .{ .node_offset_ptr_align = extra.data.src_node }; 18512 const addrspace_src: LazySrcLoc = .{ .node_offset_ptr_addrspace = extra.data.src_node }; 18513 const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node }; 18514 const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node }; 18515 18516 const elem_ty = blk: { 18517 const air_inst = try sema.resolveInst(extra.data.elem_type); 18518 const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| { 18519 if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(mod)) { 18520 try sema.errNote(block, elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{}); 18521 } 18522 return err; 18523 }; 18524 if (ty.isGenericPoison()) return error.GenericPoison; 18525 break :blk ty; 18526 }; 18527 18528 if (elem_ty.zigTypeTag(mod) == .NoReturn) 18529 return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); 18530 18531 const target = mod.getTarget(); 18532 18533 var extra_i = extra.end; 18534 18535 const sentinel = if (inst_data.flags.has_sentinel) blk: { 18536 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18537 extra_i += 1; 18538 const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src); 18539 const val = try sema.resolveConstValue(block, sentinel_src, coerced, "pointer sentinel value must be comptime-known"); 18540 break :blk val.toIntern(); 18541 } else .none; 18542 18543 const abi_align: Alignment = if (inst_data.flags.has_align) blk: { 18544 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18545 extra_i += 1; 18546 const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src); 18547 const val = try sema.resolveConstValue(block, align_src, coerced, "pointer alignment must be comptime-known"); 18548 // Check if this happens to be the lazy alignment of our element type, in 18549 // which case we can make this 0 without resolving it. 18550 switch (mod.intern_pool.indexToKey(val.toIntern())) { 18551 .int => |int| switch (int.storage) { 18552 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none, 18553 else => {}, 18554 }, 18555 else => {}, 18556 } 18557 const abi_align = @as(u32, @intCast((try val.getUnsignedIntAdvanced(mod, sema)).?)); 18558 try sema.validateAlign(block, align_src, abi_align); 18559 break :blk Alignment.fromByteUnits(abi_align); 18560 } else .none; 18561 18562 const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: { 18563 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18564 extra_i += 1; 18565 break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer); 18566 } else if (elem_ty.zigTypeTag(mod) == .Fn and target.cpu.arch == .avr) .flash else .generic; 18567 18568 const bit_offset = if (inst_data.flags.has_bit_range) blk: { 18569 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18570 extra_i += 1; 18571 const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16, "pointer bit-offset must be comptime-known"); 18572 break :blk @as(u16, @intCast(bit_offset)); 18573 } else 0; 18574 18575 const host_size: u16 = if (inst_data.flags.has_bit_range) blk: { 18576 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18577 extra_i += 1; 18578 const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16, "pointer host size must be comptime-known"); 18579 break :blk @as(u16, @intCast(host_size)); 18580 } else 0; 18581 18582 if (host_size != 0 and bit_offset >= host_size * 8) { 18583 return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{}); 18584 } 18585 18586 if (elem_ty.zigTypeTag(mod) == .Fn) { 18587 if (inst_data.size != .One) { 18588 return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); 18589 } 18590 const fn_align = mod.typeToFunc(elem_ty).?.alignment; 18591 if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and 18592 abi_align != fn_align) 18593 { 18594 return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); 18595 } 18596 } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { 18597 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); 18598 } else if (inst_data.size == .C) { 18599 if (!try sema.validateExternType(elem_ty, .other)) { 18600 const msg = msg: { 18601 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)}); 18602 errdefer msg.destroy(sema.gpa); 18603 18604 const src_decl = mod.declPtr(block.src_decl); 18605 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other); 18606 18607 try sema.addDeclaredHereNote(msg, elem_ty); 18608 break :msg msg; 18609 }; 18610 return sema.failWithOwnedErrorMsg(msg); 18611 } 18612 if (elem_ty.zigTypeTag(mod) == .Opaque) { 18613 return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{}); 18614 } 18615 } 18616 18617 const ty = try mod.ptrType(.{ 18618 .child = elem_ty.toIntern(), 18619 .sentinel = sentinel, 18620 .flags = .{ 18621 .alignment = abi_align, 18622 .address_space = address_space, 18623 .is_const = !inst_data.flags.is_mutable, 18624 .is_allowzero = inst_data.flags.is_allowzero, 18625 .is_volatile = inst_data.flags.is_volatile, 18626 .size = inst_data.size, 18627 }, 18628 .packed_offset = .{ 18629 .bit_offset = bit_offset, 18630 .host_size = host_size, 18631 }, 18632 }); 18633 return sema.addType(ty); 18634 } 18635 18636 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18637 const tracy = trace(@src()); 18638 defer tracy.end(); 18639 18640 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 18641 const src = inst_data.src(); 18642 const obj_ty = try sema.resolveType(block, src, inst_data.operand); 18643 const mod = sema.mod; 18644 18645 switch (obj_ty.zigTypeTag(mod)) { 18646 .Struct => return sema.structInitEmpty(block, obj_ty, src, src), 18647 .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty), 18648 .Void => return sema.addConstant(Value.void), 18649 .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}), 18650 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), 18651 } 18652 } 18653 18654 fn structInitEmpty( 18655 sema: *Sema, 18656 block: *Block, 18657 obj_ty: Type, 18658 dest_src: LazySrcLoc, 18659 init_src: LazySrcLoc, 18660 ) CompileError!Air.Inst.Ref { 18661 const mod = sema.mod; 18662 const gpa = sema.gpa; 18663 // This logic must be synchronized with that in `zirStructInit`. 18664 const struct_ty = try sema.resolveTypeFields(obj_ty); 18665 18666 // The init values to use for the struct instance. 18667 const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(mod)); 18668 defer gpa.free(field_inits); 18669 @memset(field_inits, .none); 18670 18671 return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false); 18672 } 18673 18674 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { 18675 const mod = sema.mod; 18676 const arr_len = obj_ty.arrayLen(mod); 18677 if (arr_len != 0) { 18678 if (obj_ty.zigTypeTag(mod) == .Array) { 18679 return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len}); 18680 } else { 18681 return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len}); 18682 } 18683 } 18684 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 18685 .ty = obj_ty.toIntern(), 18686 .storage = .{ .elems = &.{} }, 18687 } })).toValue()); 18688 } 18689 18690 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18691 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 18692 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 18693 const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 18694 const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 18695 const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; 18696 const union_ty = try sema.resolveType(block, ty_src, extra.union_type); 18697 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "name of field being initialized must be comptime-known"); 18698 const init = try sema.resolveInst(extra.init); 18699 return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); 18700 } 18701 18702 fn unionInit( 18703 sema: *Sema, 18704 block: *Block, 18705 uncasted_init: Air.Inst.Ref, 18706 init_src: LazySrcLoc, 18707 union_ty: Type, 18708 union_ty_src: LazySrcLoc, 18709 field_name: InternPool.NullTerminatedString, 18710 field_src: LazySrcLoc, 18711 ) CompileError!Air.Inst.Ref { 18712 const mod = sema.mod; 18713 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); 18714 const field = union_ty.unionFields(mod).values()[field_index]; 18715 const init = try sema.coerce(block, field.ty, uncasted_init, init_src); 18716 18717 if (try sema.resolveMaybeUndefVal(init)) |init_val| { 18718 const tag_ty = union_ty.unionTagTypeHypothetical(mod); 18719 const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?)); 18720 const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); 18721 return sema.addConstant((try mod.intern(.{ .un = .{ 18722 .ty = union_ty.toIntern(), 18723 .tag = try tag_val.intern(tag_ty, mod), 18724 .val = try init_val.intern(field.ty, mod), 18725 } })).toValue()); 18726 } 18727 18728 try sema.requireRuntimeBlock(block, init_src, null); 18729 _ = union_ty_src; 18730 try sema.queueFullTypeResolution(union_ty); 18731 return block.addUnionInit(union_ty, field_index, init); 18732 } 18733 18734 fn zirStructInit( 18735 sema: *Sema, 18736 block: *Block, 18737 inst: Zir.Inst.Index, 18738 is_ref: bool, 18739 ) CompileError!Air.Inst.Ref { 18740 const gpa = sema.gpa; 18741 const zir_datas = sema.code.instructions.items(.data); 18742 const inst_data = zir_datas[inst].pl_node; 18743 const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); 18744 const src = inst_data.src(); 18745 18746 const mod = sema.mod; 18747 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; 18748 const first_field_type_data = zir_datas[first_item.field_type].pl_node; 18749 const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; 18750 const resolved_ty = try sema.resolveType(block, src, first_field_type_extra.container_type); 18751 try sema.resolveTypeLayout(resolved_ty); 18752 18753 if (resolved_ty.zigTypeTag(mod) == .Struct) { 18754 // This logic must be synchronized with that in `zirStructInitEmpty`. 18755 18756 // Maps field index to field_type index of where it was already initialized. 18757 // For making sure all fields are accounted for and no fields are duplicated. 18758 const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(mod)); 18759 defer gpa.free(found_fields); 18760 18761 // The init values to use for the struct instance. 18762 const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(mod)); 18763 defer gpa.free(field_inits); 18764 @memset(field_inits, .none); 18765 18766 var field_i: u32 = 0; 18767 var extra_index = extra.end; 18768 18769 const is_packed = resolved_ty.containerLayout(mod) == .Packed; 18770 while (field_i < extra.data.fields_len) : (field_i += 1) { 18771 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); 18772 extra_index = item.end; 18773 18774 const field_type_data = zir_datas[item.data.field_type].pl_node; 18775 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; 18776 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 18777 const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); 18778 const field_index = if (resolved_ty.isTuple(mod)) 18779 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) 18780 else 18781 try sema.structFieldIndex(block, resolved_ty, field_name, field_src); 18782 if (field_inits[field_index] != .none) { 18783 const other_field_type = found_fields[field_index]; 18784 const other_field_type_data = zir_datas[other_field_type].pl_node; 18785 const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_type_data.src_node }; 18786 const msg = msg: { 18787 const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); 18788 errdefer msg.destroy(gpa); 18789 try sema.errNote(block, other_field_src, msg, "other field here", .{}); 18790 break :msg msg; 18791 }; 18792 return sema.failWithOwnedErrorMsg(msg); 18793 } 18794 found_fields[field_index] = item.data.field_type; 18795 field_inits[field_index] = try sema.resolveInst(item.data.init); 18796 if (!is_packed) if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| { 18797 const init_val = (try sema.resolveMaybeUndefVal(field_inits[field_index])) orelse { 18798 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); 18799 }; 18800 18801 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) { 18802 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); 18803 } 18804 }; 18805 } 18806 18807 return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref); 18808 } else if (resolved_ty.zigTypeTag(mod) == .Union) { 18809 if (extra.data.fields_len != 1) { 18810 return sema.fail(block, src, "union initialization expects exactly one field", .{}); 18811 } 18812 18813 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end); 18814 18815 const field_type_data = zir_datas[item.data.field_type].pl_node; 18816 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; 18817 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 18818 const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); 18819 const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); 18820 const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); 18821 const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?)); 18822 const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); 18823 18824 const init_inst = try sema.resolveInst(item.data.init); 18825 if (try sema.resolveMaybeUndefVal(init_inst)) |val| { 18826 const field = resolved_ty.unionFields(mod).values()[field_index]; 18827 return sema.addConstantMaybeRef(block, resolved_ty, (try mod.intern(.{ .un = .{ 18828 .ty = resolved_ty.toIntern(), 18829 .tag = try tag_val.intern(tag_ty, mod), 18830 .val = try val.intern(field.ty, mod), 18831 } })).toValue(), is_ref); 18832 } 18833 18834 if (is_ref) { 18835 const target = mod.getTarget(); 18836 const alloc_ty = try mod.ptrType(.{ 18837 .child = resolved_ty.toIntern(), 18838 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 18839 }); 18840 const alloc = try block.addTy(.alloc, alloc_ty); 18841 const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true); 18842 try sema.storePtr(block, src, field_ptr, init_inst); 18843 const new_tag = try sema.addConstant(tag_val); 18844 _ = try block.addBinOp(.set_union_tag, alloc, new_tag); 18845 return sema.makePtrConst(block, alloc); 18846 } 18847 18848 try sema.requireRuntimeBlock(block, src, null); 18849 try sema.queueFullTypeResolution(resolved_ty); 18850 return block.addUnionInit(resolved_ty, field_index, init_inst); 18851 } else if (resolved_ty.isAnonStruct(mod)) { 18852 return sema.fail(block, src, "TODO anon struct init validation", .{}); 18853 } 18854 unreachable; 18855 } 18856 18857 fn finishStructInit( 18858 sema: *Sema, 18859 block: *Block, 18860 init_src: LazySrcLoc, 18861 dest_src: LazySrcLoc, 18862 field_inits: []Air.Inst.Ref, 18863 struct_ty: Type, 18864 is_ref: bool, 18865 ) CompileError!Air.Inst.Ref { 18866 const mod = sema.mod; 18867 const ip = &mod.intern_pool; 18868 18869 var root_msg: ?*Module.ErrorMsg = null; 18870 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 18871 18872 switch (ip.indexToKey(struct_ty.toIntern())) { 18873 .anon_struct_type => |anon_struct| { 18874 for (anon_struct.values, 0..) |default_val, i| { 18875 if (field_inits[i] != .none) continue; 18876 18877 if (default_val == .none) { 18878 if (anon_struct.names.len == 0) { 18879 const template = "missing tuple field with index {d}"; 18880 if (root_msg) |msg| { 18881 try sema.errNote(block, init_src, msg, template, .{i}); 18882 } else { 18883 root_msg = try sema.errMsg(block, init_src, template, .{i}); 18884 } 18885 } else { 18886 const field_name = anon_struct.names[i]; 18887 const template = "missing struct field: {}"; 18888 const args = .{field_name.fmt(ip)}; 18889 if (root_msg) |msg| { 18890 try sema.errNote(block, init_src, msg, template, args); 18891 } else { 18892 root_msg = try sema.errMsg(block, init_src, template, args); 18893 } 18894 } 18895 } else { 18896 field_inits[i] = try sema.addConstant(default_val.toValue()); 18897 } 18898 } 18899 }, 18900 .struct_type => |struct_type| { 18901 const struct_obj = mod.structPtrUnwrap(struct_type.index).?; 18902 for (struct_obj.fields.values(), 0..) |field, i| { 18903 if (field_inits[i] != .none) continue; 18904 18905 if (field.default_val == .none) { 18906 const field_name = struct_obj.fields.keys()[i]; 18907 const template = "missing struct field: {}"; 18908 const args = .{field_name.fmt(ip)}; 18909 if (root_msg) |msg| { 18910 try sema.errNote(block, init_src, msg, template, args); 18911 } else { 18912 root_msg = try sema.errMsg(block, init_src, template, args); 18913 } 18914 } else { 18915 field_inits[i] = try sema.addConstant(field.default_val.toValue()); 18916 } 18917 } 18918 }, 18919 else => unreachable, 18920 } 18921 18922 if (root_msg) |msg| { 18923 if (mod.typeToStruct(struct_ty)) |struct_obj| { 18924 const fqn = try struct_obj.getFullyQualifiedName(mod); 18925 try mod.errNoteNonLazy( 18926 struct_obj.srcLoc(mod), 18927 msg, 18928 "struct '{}' declared here", 18929 .{fqn.fmt(ip)}, 18930 ); 18931 } 18932 root_msg = null; 18933 return sema.failWithOwnedErrorMsg(msg); 18934 } 18935 18936 // Find which field forces the expression to be runtime, if any. 18937 const opt_runtime_index = for (field_inits, 0..) |field_init, i| { 18938 if (!(try sema.isComptimeKnown(field_init))) { 18939 break i; 18940 } 18941 } else null; 18942 18943 const runtime_index = opt_runtime_index orelse { 18944 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); 18945 for (elems, field_inits, 0..) |*elem, field_init, field_i| { 18946 elem.* = try (sema.resolveMaybeUndefVal(field_init) catch unreachable).? 18947 .intern(struct_ty.structFieldType(field_i, mod), mod); 18948 } 18949 const struct_val = try mod.intern(.{ .aggregate = .{ 18950 .ty = struct_ty.toIntern(), 18951 .storage = .{ .elems = elems }, 18952 } }); 18953 return sema.addConstantMaybeRef(block, struct_ty, struct_val.toValue(), is_ref); 18954 }; 18955 18956 if (is_ref) { 18957 try sema.resolveStructLayout(struct_ty); 18958 const target = sema.mod.getTarget(); 18959 const alloc_ty = try mod.ptrType(.{ 18960 .child = struct_ty.toIntern(), 18961 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 18962 }); 18963 const alloc = try block.addTy(.alloc, alloc_ty); 18964 for (field_inits, 0..) |field_init, i_usize| { 18965 const i = @as(u32, @intCast(i_usize)); 18966 const field_src = dest_src; 18967 const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, alloc, i, field_src, struct_ty, true); 18968 try sema.storePtr(block, dest_src, field_ptr, field_init); 18969 } 18970 18971 return sema.makePtrConst(block, alloc); 18972 } 18973 18974 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 18975 error.NeededSourceLocation => { 18976 const decl = mod.declPtr(block.src_decl); 18977 const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index); 18978 try sema.requireRuntimeBlock(block, dest_src, field_src); 18979 unreachable; 18980 }, 18981 else => |e| return e, 18982 }; 18983 try sema.queueFullTypeResolution(struct_ty); 18984 return block.addAggregateInit(struct_ty, field_inits); 18985 } 18986 18987 fn zirStructInitAnon( 18988 sema: *Sema, 18989 block: *Block, 18990 inst: Zir.Inst.Index, 18991 is_ref: bool, 18992 ) CompileError!Air.Inst.Ref { 18993 const mod = sema.mod; 18994 const gpa = sema.gpa; 18995 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 18996 const src = inst_data.src(); 18997 const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); 18998 const types = try sema.arena.alloc(InternPool.Index, extra.data.fields_len); 18999 const values = try sema.arena.alloc(InternPool.Index, types.len); 19000 var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena); 19001 try fields.ensureUnusedCapacity(types.len); 19002 19003 // Find which field forces the expression to be runtime, if any. 19004 const opt_runtime_index = rs: { 19005 var runtime_index: ?usize = null; 19006 var extra_index = extra.end; 19007 for (types, 0..) |*field_ty, i_usize| { 19008 const i = @as(u32, @intCast(i_usize)); 19009 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); 19010 extra_index = item.end; 19011 19012 const name = sema.code.nullTerminatedString(item.data.field_name); 19013 const name_ip = try mod.intern_pool.getOrPutString(gpa, name); 19014 const gop = fields.getOrPutAssumeCapacity(name_ip); 19015 if (gop.found_existing) { 19016 const msg = msg: { 19017 const decl = mod.declPtr(block.src_decl); 19018 const field_src = mod.initSrc(src.node_offset.x, decl, i); 19019 const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); 19020 errdefer msg.destroy(gpa); 19021 19022 const prev_source = mod.initSrc(src.node_offset.x, decl, gop.value_ptr.*); 19023 try sema.errNote(block, prev_source, msg, "other field here", .{}); 19024 break :msg msg; 19025 }; 19026 return sema.failWithOwnedErrorMsg(msg); 19027 } 19028 gop.value_ptr.* = i; 19029 19030 const init = try sema.resolveInst(item.data.init); 19031 field_ty.* = sema.typeOf(init).toIntern(); 19032 if (field_ty.toType().zigTypeTag(mod) == .Opaque) { 19033 const msg = msg: { 19034 const decl = mod.declPtr(block.src_decl); 19035 const field_src = mod.initSrc(src.node_offset.x, decl, i); 19036 const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19037 errdefer msg.destroy(sema.gpa); 19038 19039 try sema.addDeclaredHereNote(msg, field_ty.toType()); 19040 break :msg msg; 19041 }; 19042 return sema.failWithOwnedErrorMsg(msg); 19043 } 19044 if (try sema.resolveMaybeUndefVal(init)) |init_val| { 19045 values[i] = try init_val.intern(field_ty.toType(), mod); 19046 } else { 19047 values[i] = .none; 19048 runtime_index = i; 19049 } 19050 } 19051 break :rs runtime_index; 19052 }; 19053 19054 const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ 19055 .names = fields.keys(), 19056 .types = types, 19057 .values = values, 19058 } }); 19059 19060 const runtime_index = opt_runtime_index orelse { 19061 const tuple_val = try mod.intern(.{ .aggregate = .{ 19062 .ty = tuple_ty, 19063 .storage = .{ .elems = values }, 19064 } }); 19065 return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); 19066 }; 19067 19068 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 19069 error.NeededSourceLocation => { 19070 const decl = mod.declPtr(block.src_decl); 19071 const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index); 19072 try sema.requireRuntimeBlock(block, src, field_src); 19073 unreachable; 19074 }, 19075 else => |e| return e, 19076 }; 19077 19078 if (is_ref) { 19079 const target = mod.getTarget(); 19080 const alloc_ty = try mod.ptrType(.{ 19081 .child = tuple_ty, 19082 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19083 }); 19084 const alloc = try block.addTy(.alloc, alloc_ty); 19085 var extra_index = extra.end; 19086 for (types, 0..) |field_ty, i_usize| { 19087 const i = @as(u32, @intCast(i_usize)); 19088 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); 19089 extra_index = item.end; 19090 19091 const field_ptr_ty = try mod.ptrType(.{ 19092 .child = field_ty, 19093 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19094 }); 19095 if (values[i] == .none) { 19096 const init = try sema.resolveInst(item.data.init); 19097 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19098 _ = try block.addBinOp(.store, field_ptr, init); 19099 } 19100 } 19101 19102 return sema.makePtrConst(block, alloc); 19103 } 19104 19105 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); 19106 var extra_index = extra.end; 19107 for (types, 0..) |_, i| { 19108 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); 19109 extra_index = item.end; 19110 element_refs[i] = try sema.resolveInst(item.data.init); 19111 } 19112 19113 return block.addAggregateInit(tuple_ty.toType(), element_refs); 19114 } 19115 19116 fn zirArrayInit( 19117 sema: *Sema, 19118 block: *Block, 19119 inst: Zir.Inst.Index, 19120 is_ref: bool, 19121 ) CompileError!Air.Inst.Ref { 19122 const mod = sema.mod; 19123 const gpa = sema.gpa; 19124 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 19125 const src = inst_data.src(); 19126 19127 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19128 const args = sema.code.refSlice(extra.end, extra.data.operands_len); 19129 assert(args.len >= 2); // array_ty + at least one element 19130 19131 const array_ty = try sema.resolveType(block, src, args[0]); 19132 const sentinel_val = array_ty.sentinel(mod); 19133 19134 const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @intFromBool(sentinel_val != null)); 19135 defer gpa.free(resolved_args); 19136 for (args[1..], 0..) |arg, i| { 19137 const resolved_arg = try sema.resolveInst(arg); 19138 const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct) 19139 array_ty.structFieldType(i, mod) 19140 else 19141 array_ty.elemType2(mod); 19142 resolved_args[i] = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) { 19143 error.NeededSourceLocation => { 19144 const decl = mod.declPtr(block.src_decl); 19145 const elem_src = mod.initSrc(src.node_offset.x, decl, i); 19146 _ = try sema.coerce(block, elem_ty, resolved_arg, elem_src); 19147 unreachable; 19148 }, 19149 else => return err, 19150 }; 19151 } 19152 19153 if (sentinel_val) |some| { 19154 resolved_args[resolved_args.len - 1] = try sema.addConstant(some); 19155 } 19156 19157 const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| { 19158 const comptime_known = try sema.isComptimeKnown(arg); 19159 if (!comptime_known) break @as(u32, @intCast(i)); 19160 } else null; 19161 19162 const runtime_index = opt_runtime_index orelse { 19163 const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len); 19164 for (elem_vals, resolved_args, 0..) |*val, arg, i| { 19165 const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct) 19166 array_ty.structFieldType(i, mod) 19167 else 19168 array_ty.elemType2(mod); 19169 // We checked that all args are comptime above. 19170 val.* = try ((sema.resolveMaybeUndefVal(arg) catch unreachable).?).intern(elem_ty, mod); 19171 } 19172 return sema.addConstantMaybeRef(block, array_ty, (try mod.intern(.{ .aggregate = .{ 19173 .ty = array_ty.toIntern(), 19174 .storage = .{ .elems = elem_vals }, 19175 } })).toValue(), is_ref); 19176 }; 19177 19178 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 19179 error.NeededSourceLocation => { 19180 const decl = mod.declPtr(block.src_decl); 19181 const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index); 19182 try sema.requireRuntimeBlock(block, src, elem_src); 19183 unreachable; 19184 }, 19185 else => return err, 19186 }; 19187 try sema.queueFullTypeResolution(array_ty); 19188 19189 if (is_ref) { 19190 const target = mod.getTarget(); 19191 const alloc_ty = try mod.ptrType(.{ 19192 .child = array_ty.toIntern(), 19193 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19194 }); 19195 const alloc = try block.addTy(.alloc, alloc_ty); 19196 19197 if (array_ty.isTuple(mod)) { 19198 for (resolved_args, 0..) |arg, i| { 19199 const elem_ptr_ty = try mod.ptrType(.{ 19200 .child = array_ty.structFieldType(i, mod).toIntern(), 19201 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19202 }); 19203 const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); 19204 19205 const index = try sema.addIntUnsigned(Type.usize, i); 19206 const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); 19207 _ = try block.addBinOp(.store, elem_ptr, arg); 19208 } 19209 return sema.makePtrConst(block, alloc); 19210 } 19211 19212 const elem_ptr_ty = try mod.ptrType(.{ 19213 .child = array_ty.elemType2(mod).toIntern(), 19214 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19215 }); 19216 const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); 19217 19218 for (resolved_args, 0..) |arg, i| { 19219 const index = try sema.addIntUnsigned(Type.usize, i); 19220 const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); 19221 _ = try block.addBinOp(.store, elem_ptr, arg); 19222 } 19223 return sema.makePtrConst(block, alloc); 19224 } 19225 19226 return block.addAggregateInit(array_ty, resolved_args); 19227 } 19228 19229 fn zirArrayInitAnon( 19230 sema: *Sema, 19231 block: *Block, 19232 inst: Zir.Inst.Index, 19233 is_ref: bool, 19234 ) CompileError!Air.Inst.Ref { 19235 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 19236 const src = inst_data.src(); 19237 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19238 const operands = sema.code.refSlice(extra.end, extra.data.operands_len); 19239 const mod = sema.mod; 19240 19241 const types = try sema.arena.alloc(InternPool.Index, operands.len); 19242 const values = try sema.arena.alloc(InternPool.Index, operands.len); 19243 19244 const opt_runtime_src = rs: { 19245 var runtime_src: ?LazySrcLoc = null; 19246 for (operands, 0..) |operand, i| { 19247 const operand_src = src; // TODO better source location 19248 const elem = try sema.resolveInst(operand); 19249 types[i] = sema.typeOf(elem).toIntern(); 19250 if (types[i].toType().zigTypeTag(mod) == .Opaque) { 19251 const msg = msg: { 19252 const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19253 errdefer msg.destroy(sema.gpa); 19254 19255 try sema.addDeclaredHereNote(msg, types[i].toType()); 19256 break :msg msg; 19257 }; 19258 return sema.failWithOwnedErrorMsg(msg); 19259 } 19260 if (try sema.resolveMaybeUndefVal(elem)) |val| { 19261 values[i] = val.toIntern(); 19262 } else { 19263 values[i] = .none; 19264 runtime_src = operand_src; 19265 } 19266 } 19267 break :rs runtime_src; 19268 }; 19269 19270 const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ 19271 .types = types, 19272 .values = values, 19273 .names = &.{}, 19274 } }); 19275 19276 const runtime_src = opt_runtime_src orelse { 19277 const tuple_val = try mod.intern(.{ .aggregate = .{ 19278 .ty = tuple_ty, 19279 .storage = .{ .elems = values }, 19280 } }); 19281 return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); 19282 }; 19283 19284 try sema.requireRuntimeBlock(block, src, runtime_src); 19285 19286 if (is_ref) { 19287 const target = sema.mod.getTarget(); 19288 const alloc_ty = try mod.ptrType(.{ 19289 .child = tuple_ty, 19290 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19291 }); 19292 const alloc = try block.addTy(.alloc, alloc_ty); 19293 for (operands, 0..) |operand, i_usize| { 19294 const i = @as(u32, @intCast(i_usize)); 19295 const field_ptr_ty = try mod.ptrType(.{ 19296 .child = types[i], 19297 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19298 }); 19299 if (values[i] == .none) { 19300 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19301 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); 19302 } 19303 } 19304 19305 return sema.makePtrConst(block, alloc); 19306 } 19307 19308 const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 19309 for (operands, 0..) |operand, i| { 19310 element_refs[i] = try sema.resolveInst(operand); 19311 } 19312 19313 return block.addAggregateInit(tuple_ty.toType(), element_refs); 19314 } 19315 19316 fn addConstantMaybeRef( 19317 sema: *Sema, 19318 block: *Block, 19319 ty: Type, 19320 val: Value, 19321 is_ref: bool, 19322 ) !Air.Inst.Ref { 19323 if (!is_ref) return sema.addConstant(val); 19324 19325 var anon_decl = try block.startAnonDecl(); 19326 defer anon_decl.deinit(); 19327 const decl = try anon_decl.finish( 19328 ty, 19329 val, 19330 .none, // default alignment 19331 ); 19332 return sema.analyzeDeclRef(decl); 19333 } 19334 19335 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19336 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 19337 const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data; 19338 const ty_src = inst_data.src(); 19339 const field_src = inst_data.src(); 19340 const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); 19341 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "field name must be comptime-known"); 19342 return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); 19343 } 19344 19345 fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19346 const mod = sema.mod; 19347 const ip = &mod.intern_pool; 19348 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 19349 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; 19350 const ty_src = inst_data.src(); 19351 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; 19352 const aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) { 19353 // Since this is a ZIR instruction that returns a type, encountering 19354 // generic poison should not result in a failed compilation, but the 19355 // generic poison type. This prevents unnecessary failures when 19356 // constructing types at compile-time. 19357 error.GenericPoison => return Air.Inst.Ref.generic_poison_type, 19358 else => |e| return e, 19359 }; 19360 const zir_field_name = sema.code.nullTerminatedString(extra.name_start); 19361 const field_name = try ip.getOrPutString(sema.gpa, zir_field_name); 19362 return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); 19363 } 19364 19365 fn fieldType( 19366 sema: *Sema, 19367 block: *Block, 19368 aggregate_ty: Type, 19369 field_name: InternPool.NullTerminatedString, 19370 field_src: LazySrcLoc, 19371 ty_src: LazySrcLoc, 19372 ) CompileError!Air.Inst.Ref { 19373 const mod = sema.mod; 19374 var cur_ty = aggregate_ty; 19375 while (true) { 19376 const resolved_ty = try sema.resolveTypeFields(cur_ty); 19377 cur_ty = resolved_ty; 19378 switch (cur_ty.zigTypeTag(mod)) { 19379 .Struct => switch (mod.intern_pool.indexToKey(cur_ty.toIntern())) { 19380 .anon_struct_type => |anon_struct| { 19381 const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); 19382 return sema.addType(anon_struct.types[field_index].toType()); 19383 }, 19384 .struct_type => |struct_type| { 19385 const struct_obj = mod.structPtrUnwrap(struct_type.index).?; 19386 const field = struct_obj.fields.get(field_name) orelse 19387 return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); 19388 return sema.addType(field.ty); 19389 }, 19390 else => unreachable, 19391 }, 19392 .Union => { 19393 const union_obj = mod.typeToUnion(cur_ty).?; 19394 const field = union_obj.fields.get(field_name) orelse 19395 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); 19396 return sema.addType(field.ty); 19397 }, 19398 .Optional => { 19399 // Struct/array init through optional requires the child type to not be a pointer. 19400 // If the child of .optional is a pointer it'll error on the next loop. 19401 cur_ty = mod.intern_pool.indexToKey(cur_ty.toIntern()).opt_type.toType(); 19402 continue; 19403 }, 19404 .ErrorUnion => { 19405 cur_ty = cur_ty.errorUnionPayload(mod); 19406 continue; 19407 }, 19408 else => {}, 19409 } 19410 return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{ 19411 resolved_ty.fmt(sema.mod), 19412 }); 19413 } 19414 } 19415 19416 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 19417 return sema.getErrorReturnTrace(block); 19418 } 19419 19420 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 19421 const mod = sema.mod; 19422 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 19423 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 19424 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 19425 const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern()); 19426 19427 if (sema.owner_func != null and 19428 sema.owner_func.?.calls_or_awaits_errorable_fn and 19429 mod.comp.bin_file.options.error_return_tracing and 19430 mod.backendSupportsFeature(.error_return_trace)) 19431 { 19432 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); 19433 } 19434 return sema.addConstant((try mod.intern(.{ .opt = .{ 19435 .ty = opt_ptr_stack_trace_ty.toIntern(), 19436 .val = .none, 19437 } })).toValue()); 19438 } 19439 19440 fn zirFrame( 19441 sema: *Sema, 19442 block: *Block, 19443 extended: Zir.Inst.Extended.InstData, 19444 ) CompileError!Air.Inst.Ref { 19445 const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand))); 19446 return sema.failWithUseOfAsync(block, src); 19447 } 19448 19449 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19450 const mod = sema.mod; 19451 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19452 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19453 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 19454 if (ty.isNoReturn(mod)) { 19455 return sema.fail(block, operand_src, "no align available for type '{}'", .{ty.fmt(sema.mod)}); 19456 } 19457 const val = try ty.lazyAbiAlignment(mod); 19458 if (val.isLazyAlign(mod)) { 19459 try sema.queueFullTypeResolution(ty); 19460 } 19461 return sema.addConstant(val); 19462 } 19463 19464 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19465 const mod = sema.mod; 19466 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19467 const operand = try sema.resolveInst(inst_data.operand); 19468 if (try sema.resolveMaybeUndefVal(operand)) |val| { 19469 if (val.isUndef(mod)) return sema.addConstUndef(Type.u1); 19470 if (val.toBool()) return sema.addConstant(try mod.intValue(Type.u1, 1)); 19471 return sema.addConstant(try mod.intValue(Type.u1, 0)); 19472 } 19473 return block.addUnOp(.int_from_bool, operand); 19474 } 19475 19476 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19477 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19478 const operand = try sema.resolveInst(inst_data.operand); 19479 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19480 19481 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 19482 const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name; 19483 return sema.addStrLit(block, sema.mod.intern_pool.stringToSlice(err_name)); 19484 } 19485 19486 // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass 19487 // might be able to resolve the result at compile time. 19488 return block.addUnOp(.error_name, operand); 19489 } 19490 19491 fn zirUnaryMath( 19492 sema: *Sema, 19493 block: *Block, 19494 inst: Zir.Inst.Index, 19495 air_tag: Air.Inst.Tag, 19496 comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value, 19497 ) CompileError!Air.Inst.Ref { 19498 const tracy = trace(@src()); 19499 defer tracy.end(); 19500 19501 const mod = sema.mod; 19502 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19503 const operand = try sema.resolveInst(inst_data.operand); 19504 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19505 const operand_ty = sema.typeOf(operand); 19506 19507 switch (operand_ty.zigTypeTag(mod)) { 19508 .ComptimeFloat, .Float => {}, 19509 .Vector => { 19510 const scalar_ty = operand_ty.scalarType(mod); 19511 switch (scalar_ty.zigTypeTag(mod)) { 19512 .ComptimeFloat, .Float => {}, 19513 else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}), 19514 } 19515 }, 19516 else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}), 19517 } 19518 19519 switch (operand_ty.zigTypeTag(mod)) { 19520 .Vector => { 19521 const scalar_ty = operand_ty.scalarType(mod); 19522 const vec_len = operand_ty.vectorLen(mod); 19523 const result_ty = try mod.vectorType(.{ 19524 .len = vec_len, 19525 .child = scalar_ty.toIntern(), 19526 }); 19527 if (try sema.resolveMaybeUndefVal(operand)) |val| { 19528 if (val.isUndef(mod)) 19529 return sema.addConstUndef(result_ty); 19530 19531 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 19532 for (elems, 0..) |*elem, i| { 19533 const elem_val = try val.elemValue(sema.mod, i); 19534 elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod); 19535 } 19536 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 19537 .ty = result_ty.toIntern(), 19538 .storage = .{ .elems = elems }, 19539 } })).toValue()); 19540 } 19541 19542 try sema.requireRuntimeBlock(block, operand_src, null); 19543 return block.addUnOp(air_tag, operand); 19544 }, 19545 .ComptimeFloat, .Float => { 19546 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { 19547 if (operand_val.isUndef(mod)) 19548 return sema.addConstUndef(operand_ty); 19549 const result_val = try eval(operand_val, operand_ty, sema.arena, sema.mod); 19550 return sema.addConstant(result_val); 19551 } 19552 19553 try sema.requireRuntimeBlock(block, operand_src, null); 19554 return block.addUnOp(air_tag, operand); 19555 }, 19556 else => unreachable, 19557 } 19558 } 19559 19560 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19561 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19562 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19563 const src = inst_data.src(); 19564 const operand = try sema.resolveInst(inst_data.operand); 19565 const operand_ty = sema.typeOf(operand); 19566 const mod = sema.mod; 19567 const ip = &mod.intern_pool; 19568 19569 try sema.resolveTypeLayout(operand_ty); 19570 const enum_ty = switch (operand_ty.zigTypeTag(mod)) { 19571 .EnumLiteral => { 19572 const val = try sema.resolveConstValue(block, .unneeded, operand, ""); 19573 const tag_name = ip.indexToKey(val.toIntern()).enum_literal; 19574 return sema.addStrLit(block, ip.stringToSlice(tag_name)); 19575 }, 19576 .Enum => operand_ty, 19577 .Union => operand_ty.unionTagType(mod) orelse { 19578 const msg = msg: { 19579 const msg = try sema.errMsg(block, src, "union '{}' is untagged", .{ 19580 operand_ty.fmt(sema.mod), 19581 }); 19582 errdefer msg.destroy(sema.gpa); 19583 try sema.addDeclaredHereNote(msg, operand_ty); 19584 break :msg msg; 19585 }; 19586 return sema.failWithOwnedErrorMsg(msg); 19587 }, 19588 else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{ 19589 operand_ty.fmt(mod), 19590 }), 19591 }; 19592 if (enum_ty.enumFieldCount(mod) == 0) { 19593 // TODO I don't think this is the correct way to handle this but 19594 // it prevents a crash. 19595 return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{}'", .{ 19596 enum_ty.fmt(mod), 19597 }); 19598 } 19599 const enum_decl_index = enum_ty.getOwnerDecl(mod); 19600 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); 19601 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { 19602 const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse { 19603 const enum_decl = mod.declPtr(enum_decl_index); 19604 const msg = msg: { 19605 const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{}'", .{ 19606 val.fmtValue(enum_ty, sema.mod), enum_decl.name.fmt(ip), 19607 }); 19608 errdefer msg.destroy(sema.gpa); 19609 try mod.errNoteNonLazy(enum_decl.srcLoc(mod), msg, "declared here", .{}); 19610 break :msg msg; 19611 }; 19612 return sema.failWithOwnedErrorMsg(msg); 19613 }; 19614 // TODO: write something like getCoercedInts to avoid needing to dupe 19615 const field_name = enum_ty.enumFieldName(field_index, mod); 19616 return sema.addStrLit(block, ip.stringToSlice(field_name)); 19617 } 19618 try sema.requireRuntimeBlock(block, src, operand_src); 19619 if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) { 19620 const ok = try block.addUnOp(.is_named_enum_value, casted_operand); 19621 try sema.addSafetyCheck(block, ok, .invalid_enum_value); 19622 } 19623 // In case the value is runtime-known, we have an AIR instruction for this instead 19624 // of trying to lower it in Sema because an optimization pass may result in the operand 19625 // being comptime-known, which would let us elide the `tag_name` AIR instruction. 19626 return block.addUnOp(.tag_name, casted_operand); 19627 } 19628 19629 fn zirReify( 19630 sema: *Sema, 19631 block: *Block, 19632 extended: Zir.Inst.Extended.InstData, 19633 inst: Zir.Inst.Index, 19634 ) CompileError!Air.Inst.Ref { 19635 const mod = sema.mod; 19636 const gpa = sema.gpa; 19637 const ip = &mod.intern_pool; 19638 const name_strategy = @as(Zir.Inst.NameStrategy, @enumFromInt(extended.small)); 19639 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 19640 const src = LazySrcLoc.nodeOffset(extra.node); 19641 const type_info_ty = try sema.getBuiltinType("Type"); 19642 const uncasted_operand = try sema.resolveInst(extra.operand); 19643 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 19644 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); 19645 const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime-known"); 19646 const union_val = ip.indexToKey(val.toIntern()).un; 19647 const target = mod.getTarget(); 19648 if (try union_val.val.toValue().anyUndef(mod)) return sema.failWithUseOfUndef(block, src); 19649 const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag.toValue(), mod).?; 19650 switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { 19651 .Type => return Air.Inst.Ref.type_type, 19652 .Void => return Air.Inst.Ref.void_type, 19653 .Bool => return Air.Inst.Ref.bool_type, 19654 .NoReturn => return Air.Inst.Ref.noreturn_type, 19655 .ComptimeFloat => return Air.Inst.Ref.comptime_float_type, 19656 .ComptimeInt => return Air.Inst.Ref.comptime_int_type, 19657 .Undefined => return Air.Inst.Ref.undefined_type, 19658 .Null => return Air.Inst.Ref.null_type, 19659 .AnyFrame => return sema.failWithUseOfAsync(block, src), 19660 .EnumLiteral => return Air.Inst.Ref.enum_literal_type, 19661 .Int => { 19662 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19663 const signedness_val = try union_val.val.toValue().fieldValue( 19664 mod, 19665 fields.getIndex(try ip.getOrPutString(gpa, "signedness")).?, 19666 ); 19667 const bits_val = try union_val.val.toValue().fieldValue( 19668 mod, 19669 fields.getIndex(try ip.getOrPutString(gpa, "bits")).?, 19670 ); 19671 19672 const signedness = mod.toEnum(std.builtin.Signedness, signedness_val); 19673 const bits = @as(u16, @intCast(bits_val.toUnsignedInt(mod))); 19674 const ty = try mod.intType(signedness, bits); 19675 return sema.addType(ty); 19676 }, 19677 .Vector => { 19678 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19679 const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19680 try ip.getOrPutString(gpa, "len"), 19681 ).?); 19682 const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19683 try ip.getOrPutString(gpa, "child"), 19684 ).?); 19685 19686 const len = @as(u32, @intCast(len_val.toUnsignedInt(mod))); 19687 const child_ty = child_val.toType(); 19688 19689 try sema.checkVectorElemType(block, src, child_ty); 19690 19691 const ty = try mod.vectorType(.{ 19692 .len = len, 19693 .child = child_ty.toIntern(), 19694 }); 19695 return sema.addType(ty); 19696 }, 19697 .Float => { 19698 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19699 const bits_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19700 try ip.getOrPutString(gpa, "bits"), 19701 ).?); 19702 19703 const bits = @as(u16, @intCast(bits_val.toUnsignedInt(mod))); 19704 const ty = switch (bits) { 19705 16 => Type.f16, 19706 32 => Type.f32, 19707 64 => Type.f64, 19708 80 => Type.f80, 19709 128 => Type.f128, 19710 else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}), 19711 }; 19712 return sema.addType(ty); 19713 }, 19714 .Pointer => { 19715 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19716 const size_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19717 try ip.getOrPutString(gpa, "size"), 19718 ).?); 19719 const is_const_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19720 try ip.getOrPutString(gpa, "is_const"), 19721 ).?); 19722 const is_volatile_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19723 try ip.getOrPutString(gpa, "is_volatile"), 19724 ).?); 19725 const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19726 try ip.getOrPutString(gpa, "alignment"), 19727 ).?); 19728 const address_space_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19729 try ip.getOrPutString(gpa, "address_space"), 19730 ).?); 19731 const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19732 try ip.getOrPutString(gpa, "child"), 19733 ).?); 19734 const is_allowzero_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19735 try ip.getOrPutString(gpa, "is_allowzero"), 19736 ).?); 19737 const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19738 try ip.getOrPutString(gpa, "sentinel"), 19739 ).?); 19740 19741 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { 19742 return sema.fail(block, src, "alignment must fit in 'u32'", .{}); 19743 } 19744 19745 const abi_align = Alignment.fromByteUnits( 19746 (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?, 19747 ); 19748 19749 const unresolved_elem_ty = child_val.toType(); 19750 const elem_ty = if (abi_align == .none) 19751 unresolved_elem_ty 19752 else t: { 19753 const elem_ty = try sema.resolveTypeFields(unresolved_elem_ty); 19754 try sema.resolveTypeLayout(elem_ty); 19755 break :t elem_ty; 19756 }; 19757 19758 const ptr_size = mod.toEnum(std.builtin.Type.Pointer.Size, size_val); 19759 19760 const actual_sentinel: InternPool.Index = s: { 19761 if (!sentinel_val.isNull(mod)) { 19762 if (ptr_size == .One or ptr_size == .C) { 19763 return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); 19764 } 19765 const sentinel_ptr_val = sentinel_val.optionalValue(mod).?; 19766 const ptr_ty = try mod.singleMutPtrType(elem_ty); 19767 const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; 19768 break :s sent_val.toIntern(); 19769 } 19770 break :s .none; 19771 }; 19772 19773 if (elem_ty.zigTypeTag(mod) == .NoReturn) { 19774 return sema.fail(block, src, "pointer to noreturn not allowed", .{}); 19775 } else if (elem_ty.zigTypeTag(mod) == .Fn) { 19776 if (ptr_size != .One) { 19777 return sema.fail(block, src, "function pointers must be single pointers", .{}); 19778 } 19779 const fn_align = mod.typeToFunc(elem_ty).?.alignment; 19780 if (abi_align != .none and fn_align != .none and 19781 abi_align != fn_align) 19782 { 19783 return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{}); 19784 } 19785 } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { 19786 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); 19787 } else if (ptr_size == .C) { 19788 if (!try sema.validateExternType(elem_ty, .other)) { 19789 const msg = msg: { 19790 const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)}); 19791 errdefer msg.destroy(gpa); 19792 19793 const src_decl = mod.declPtr(block.src_decl); 19794 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other); 19795 19796 try sema.addDeclaredHereNote(msg, elem_ty); 19797 break :msg msg; 19798 }; 19799 return sema.failWithOwnedErrorMsg(msg); 19800 } 19801 if (elem_ty.zigTypeTag(mod) == .Opaque) { 19802 return sema.fail(block, src, "C pointers cannot point to opaque types", .{}); 19803 } 19804 } 19805 19806 const ty = try mod.ptrType(.{ 19807 .child = elem_ty.toIntern(), 19808 .sentinel = actual_sentinel, 19809 .flags = .{ 19810 .size = ptr_size, 19811 .is_const = is_const_val.toBool(), 19812 .is_volatile = is_volatile_val.toBool(), 19813 .alignment = abi_align, 19814 .address_space = mod.toEnum(std.builtin.AddressSpace, address_space_val), 19815 .is_allowzero = is_allowzero_val.toBool(), 19816 }, 19817 }); 19818 return sema.addType(ty); 19819 }, 19820 .Array => { 19821 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19822 const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19823 try ip.getOrPutString(gpa, "len"), 19824 ).?); 19825 const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19826 try ip.getOrPutString(gpa, "child"), 19827 ).?); 19828 const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19829 try ip.getOrPutString(gpa, "sentinel"), 19830 ).?); 19831 19832 const len = len_val.toUnsignedInt(mod); 19833 const child_ty = child_val.toType(); 19834 const sentinel = if (sentinel_val.optionalValue(mod)) |p| blk: { 19835 const ptr_ty = try mod.singleMutPtrType(child_ty); 19836 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?; 19837 } else null; 19838 19839 const ty = try mod.arrayType(.{ 19840 .len = len, 19841 .sentinel = if (sentinel) |s| s.toIntern() else .none, 19842 .child = child_ty.toIntern(), 19843 }); 19844 return sema.addType(ty); 19845 }, 19846 .Optional => { 19847 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19848 const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19849 try ip.getOrPutString(gpa, "child"), 19850 ).?); 19851 19852 const child_ty = child_val.toType(); 19853 19854 const ty = try mod.optionalType(child_ty.toIntern()); 19855 return sema.addType(ty); 19856 }, 19857 .ErrorUnion => { 19858 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19859 const error_set_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19860 try ip.getOrPutString(gpa, "error_set"), 19861 ).?); 19862 const payload_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19863 try ip.getOrPutString(gpa, "payload"), 19864 ).?); 19865 19866 const error_set_ty = error_set_val.toType(); 19867 const payload_ty = payload_val.toType(); 19868 19869 if (error_set_ty.zigTypeTag(mod) != .ErrorSet) { 19870 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{}); 19871 } 19872 19873 const ty = try mod.errorUnionType(error_set_ty, payload_ty); 19874 return sema.addType(ty); 19875 }, 19876 .ErrorSet => { 19877 const payload_val = union_val.val.toValue().optionalValue(mod) orelse 19878 return sema.addType(Type.anyerror); 19879 19880 const len = try sema.usizeCast(block, src, payload_val.sliceLen(mod)); 19881 var names: Module.Fn.InferredErrorSet.NameMap = .{}; 19882 try names.ensureUnusedCapacity(sema.arena, len); 19883 for (0..len) |i| { 19884 const elem_val = try payload_val.elemValue(mod, i); 19885 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 19886 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 19887 try ip.getOrPutString(gpa, "name"), 19888 ).?); 19889 19890 const name = try name_val.toIpString(Type.slice_const_u8, mod); 19891 _ = try mod.getErrorValue(name); 19892 const gop = names.getOrPutAssumeCapacity(name); 19893 if (gop.found_existing) { 19894 return sema.fail(block, src, "duplicate error '{}'", .{ 19895 name.fmt(ip), 19896 }); 19897 } 19898 } 19899 19900 const ty = try mod.errorSetFromUnsortedNames(names.keys()); 19901 return sema.addType(ty); 19902 }, 19903 .Struct => { 19904 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19905 const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19906 try ip.getOrPutString(gpa, "layout"), 19907 ).?); 19908 const backing_integer_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19909 try ip.getOrPutString(gpa, "backing_integer"), 19910 ).?); 19911 const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19912 try ip.getOrPutString(gpa, "fields"), 19913 ).?); 19914 const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19915 try ip.getOrPutString(gpa, "decls"), 19916 ).?); 19917 const is_tuple_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19918 try ip.getOrPutString(gpa, "is_tuple"), 19919 ).?); 19920 19921 const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); 19922 19923 // Decls 19924 if (decls_val.sliceLen(mod) > 0) { 19925 return sema.fail(block, src, "reified structs must have no decls", .{}); 19926 } 19927 19928 if (layout != .Packed and !backing_integer_val.isNull(mod)) { 19929 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); 19930 } 19931 19932 return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool()); 19933 }, 19934 .Enum => { 19935 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19936 const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19937 try ip.getOrPutString(gpa, "tag_type"), 19938 ).?); 19939 const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19940 try ip.getOrPutString(gpa, "fields"), 19941 ).?); 19942 const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19943 try ip.getOrPutString(gpa, "decls"), 19944 ).?); 19945 const is_exhaustive_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19946 try ip.getOrPutString(gpa, "is_exhaustive"), 19947 ).?); 19948 19949 // Decls 19950 if (decls_val.sliceLen(mod) > 0) { 19951 return sema.fail(block, src, "reified enums must have no decls", .{}); 19952 } 19953 19954 const int_tag_ty = tag_type_val.toType(); 19955 if (int_tag_ty.zigTypeTag(mod) != .Int) { 19956 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); 19957 } 19958 19959 // Because these things each reference each other, `undefined` 19960 // placeholders are used before being set after the enum type gains 19961 // an InternPool index. 19962 19963 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 19964 .ty = Type.noreturn, 19965 .val = Value.@"unreachable", 19966 }, name_strategy, "enum", inst); 19967 const new_decl = mod.declPtr(new_decl_index); 19968 new_decl.owns_tv = true; 19969 errdefer { 19970 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 19971 mod.abortAnonDecl(new_decl_index); 19972 } 19973 19974 // Define our empty enum decl 19975 const fields_len = @as(u32, @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod)))); 19976 const incomplete_enum = try ip.getIncompleteEnum(gpa, .{ 19977 .decl = new_decl_index, 19978 .namespace = .none, 19979 .fields_len = fields_len, 19980 .has_values = true, 19981 .tag_mode = if (!is_exhaustive_val.toBool()) 19982 .nonexhaustive 19983 else 19984 .explicit, 19985 .tag_ty = int_tag_ty.toIntern(), 19986 }); 19987 // TODO: figure out InternPool removals for incremental compilation 19988 //errdefer ip.remove(incomplete_enum.index); 19989 19990 new_decl.ty = Type.type; 19991 new_decl.val = incomplete_enum.index.toValue(); 19992 19993 for (0..fields_len) |field_i| { 19994 const elem_val = try fields_val.elemValue(mod, field_i); 19995 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 19996 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 19997 try ip.getOrPutString(gpa, "name"), 19998 ).?); 19999 const value_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20000 try ip.getOrPutString(gpa, "value"), 20001 ).?); 20002 20003 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 20004 20005 if (!try sema.intFitsInType(value_val, int_tag_ty, null)) { 20006 // TODO: better source location 20007 return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{ 20008 field_name.fmt(ip), 20009 value_val.fmtValue(Type.comptime_int, mod), 20010 int_tag_ty.fmt(mod), 20011 }); 20012 } 20013 20014 if (try incomplete_enum.addFieldName(ip, gpa, field_name)) |other_index| { 20015 const msg = msg: { 20016 const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{ 20017 field_name.fmt(ip), 20018 }); 20019 errdefer msg.destroy(gpa); 20020 _ = other_index; // TODO: this note is incorrect 20021 try sema.errNote(block, src, msg, "other field here", .{}); 20022 break :msg msg; 20023 }; 20024 return sema.failWithOwnedErrorMsg(msg); 20025 } 20026 20027 if (try incomplete_enum.addFieldValue(ip, gpa, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| { 20028 const msg = msg: { 20029 const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)}); 20030 errdefer msg.destroy(gpa); 20031 _ = other; // TODO: this note is incorrect 20032 try sema.errNote(block, src, msg, "other enum tag value here", .{}); 20033 break :msg msg; 20034 }; 20035 return sema.failWithOwnedErrorMsg(msg); 20036 } 20037 } 20038 20039 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20040 try mod.finalizeAnonDecl(new_decl_index); 20041 return decl_val; 20042 }, 20043 .Opaque => { 20044 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 20045 const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20046 try ip.getOrPutString(gpa, "decls"), 20047 ).?); 20048 20049 // Decls 20050 if (decls_val.sliceLen(mod) > 0) { 20051 return sema.fail(block, src, "reified opaque must have no decls", .{}); 20052 } 20053 20054 // Because these three things each reference each other, 20055 // `undefined` placeholders are used in two places before being set 20056 // after the opaque type gains an InternPool index. 20057 20058 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 20059 .ty = Type.noreturn, 20060 .val = Value.@"unreachable", 20061 }, name_strategy, "opaque", inst); 20062 const new_decl = mod.declPtr(new_decl_index); 20063 new_decl.owns_tv = true; 20064 errdefer { 20065 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 20066 mod.abortAnonDecl(new_decl_index); 20067 } 20068 20069 const new_namespace_index = try mod.createNamespace(.{ 20070 .parent = block.namespace.toOptional(), 20071 .ty = undefined, 20072 .file_scope = block.getFileScope(mod), 20073 }); 20074 const new_namespace = mod.namespacePtr(new_namespace_index); 20075 errdefer mod.destroyNamespace(new_namespace_index); 20076 20077 const opaque_ty = try mod.intern(.{ .opaque_type = .{ 20078 .decl = new_decl_index, 20079 .namespace = new_namespace_index, 20080 } }); 20081 // TODO: figure out InternPool removals for incremental compilation 20082 //errdefer ip.remove(opaque_ty); 20083 20084 new_decl.ty = Type.type; 20085 new_decl.val = opaque_ty.toValue(); 20086 new_namespace.ty = opaque_ty.toType(); 20087 20088 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20089 try mod.finalizeAnonDecl(new_decl_index); 20090 return decl_val; 20091 }, 20092 .Union => { 20093 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 20094 const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20095 try ip.getOrPutString(gpa, "layout"), 20096 ).?); 20097 const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20098 try ip.getOrPutString(gpa, "tag_type"), 20099 ).?); 20100 const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20101 try ip.getOrPutString(gpa, "fields"), 20102 ).?); 20103 const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20104 try ip.getOrPutString(gpa, "decls"), 20105 ).?); 20106 20107 // Decls 20108 if (decls_val.sliceLen(mod) > 0) { 20109 return sema.fail(block, src, "reified unions must have no decls", .{}); 20110 } 20111 const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); 20112 20113 // Because these three things each reference each other, `undefined` 20114 // placeholders are used before being set after the union type gains an 20115 // InternPool index. 20116 20117 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 20118 .ty = Type.noreturn, 20119 .val = Value.@"unreachable", 20120 }, name_strategy, "union", inst); 20121 const new_decl = mod.declPtr(new_decl_index); 20122 new_decl.owns_tv = true; 20123 errdefer { 20124 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 20125 mod.abortAnonDecl(new_decl_index); 20126 } 20127 20128 const new_namespace_index = try mod.createNamespace(.{ 20129 .parent = block.namespace.toOptional(), 20130 .ty = undefined, 20131 .file_scope = block.getFileScope(mod), 20132 }); 20133 const new_namespace = mod.namespacePtr(new_namespace_index); 20134 errdefer mod.destroyNamespace(new_namespace_index); 20135 20136 const union_index = try mod.createUnion(.{ 20137 .owner_decl = new_decl_index, 20138 .tag_ty = Type.null, 20139 .fields = .{}, 20140 .zir_index = inst, 20141 .layout = layout, 20142 .status = .have_field_types, 20143 .namespace = new_namespace_index, 20144 }); 20145 const union_obj = mod.unionPtr(union_index); 20146 errdefer mod.destroyUnion(union_index); 20147 20148 const union_ty = try ip.get(gpa, .{ .union_type = .{ 20149 .index = union_index, 20150 .runtime_tag = if (!tag_type_val.isNull(mod)) 20151 .tagged 20152 else if (layout != .Auto) 20153 .none 20154 else switch (mod.optimizeMode()) { 20155 .Debug, .ReleaseSafe => .safety, 20156 .ReleaseFast, .ReleaseSmall => .none, 20157 }, 20158 } }); 20159 // TODO: figure out InternPool removals for incremental compilation 20160 //errdefer ip.remove(union_ty); 20161 20162 new_decl.ty = Type.type; 20163 new_decl.val = union_ty.toValue(); 20164 new_namespace.ty = union_ty.toType(); 20165 20166 // Tag type 20167 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); 20168 var explicit_tags_seen: []bool = &.{}; 20169 var enum_field_names: []InternPool.NullTerminatedString = &.{}; 20170 if (tag_type_val.optionalValue(mod)) |payload_val| { 20171 union_obj.tag_ty = payload_val.toType(); 20172 20173 const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) { 20174 .enum_type => |x| x, 20175 else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), 20176 }; 20177 20178 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); 20179 @memset(explicit_tags_seen, false); 20180 } else { 20181 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 20182 } 20183 20184 // Fields 20185 try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); 20186 20187 for (0..fields_len) |i| { 20188 const elem_val = try fields_val.elemValue(mod, i); 20189 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 20190 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20191 try ip.getOrPutString(gpa, "name"), 20192 ).?); 20193 const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20194 try ip.getOrPutString(gpa, "type"), 20195 ).?); 20196 const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20197 try ip.getOrPutString(gpa, "alignment"), 20198 ).?); 20199 20200 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 20201 20202 if (enum_field_names.len != 0) { 20203 enum_field_names[i] = field_name; 20204 } 20205 20206 if (explicit_tags_seen.len > 0) { 20207 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; 20208 const enum_index = tag_info.nameIndex(ip, field_name) orelse { 20209 const msg = msg: { 20210 const msg = try sema.errMsg(block, src, "no field named '{}' in enum '{}'", .{ 20211 field_name.fmt(ip), 20212 union_obj.tag_ty.fmt(mod), 20213 }); 20214 errdefer msg.destroy(gpa); 20215 try sema.addDeclaredHereNote(msg, union_obj.tag_ty); 20216 break :msg msg; 20217 }; 20218 return sema.failWithOwnedErrorMsg(msg); 20219 }; 20220 // No check for duplicate because the check already happened in order 20221 // to create the enum type in the first place. 20222 assert(!explicit_tags_seen[enum_index]); 20223 explicit_tags_seen[enum_index] = true; 20224 } 20225 20226 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); 20227 if (gop.found_existing) { 20228 // TODO: better source location 20229 return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); 20230 } 20231 20232 const field_ty = type_val.toType(); 20233 gop.value_ptr.* = .{ 20234 .ty = field_ty, 20235 .abi_align = Alignment.fromByteUnits((try alignment_val.getUnsignedIntAdvanced(mod, sema)).?), 20236 }; 20237 20238 if (field_ty.zigTypeTag(mod) == .Opaque) { 20239 const msg = msg: { 20240 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 20241 errdefer msg.destroy(gpa); 20242 20243 try sema.addDeclaredHereNote(msg, field_ty); 20244 break :msg msg; 20245 }; 20246 return sema.failWithOwnedErrorMsg(msg); 20247 } 20248 if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { 20249 const msg = msg: { 20250 const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 20251 errdefer msg.destroy(gpa); 20252 20253 const src_decl = mod.declPtr(block.src_decl); 20254 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field); 20255 20256 try sema.addDeclaredHereNote(msg, field_ty); 20257 break :msg msg; 20258 }; 20259 return sema.failWithOwnedErrorMsg(msg); 20260 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { 20261 const msg = msg: { 20262 const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 20263 errdefer msg.destroy(gpa); 20264 20265 const src_decl = mod.declPtr(block.src_decl); 20266 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); 20267 20268 try sema.addDeclaredHereNote(msg, field_ty); 20269 break :msg msg; 20270 }; 20271 return sema.failWithOwnedErrorMsg(msg); 20272 } 20273 } 20274 20275 if (explicit_tags_seen.len > 0) { 20276 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; 20277 if (tag_info.names.len > fields_len) { 20278 const msg = msg: { 20279 const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); 20280 errdefer msg.destroy(gpa); 20281 20282 const enum_ty = union_obj.tag_ty; 20283 for (tag_info.names, 0..) |field_name, field_index| { 20284 if (explicit_tags_seen[field_index]) continue; 20285 try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{ 20286 field_name.fmt(ip), 20287 }); 20288 } 20289 try sema.addDeclaredHereNote(msg, union_obj.tag_ty); 20290 break :msg msg; 20291 }; 20292 return sema.failWithOwnedErrorMsg(msg); 20293 } 20294 } else { 20295 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, null); 20296 } 20297 20298 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20299 try mod.finalizeAnonDecl(new_decl_index); 20300 return decl_val; 20301 }, 20302 .Fn => { 20303 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 20304 const calling_convention_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20305 try ip.getOrPutString(gpa, "calling_convention"), 20306 ).?); 20307 const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20308 try ip.getOrPutString(gpa, "alignment"), 20309 ).?); 20310 const is_generic_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20311 try ip.getOrPutString(gpa, "is_generic"), 20312 ).?); 20313 const is_var_args_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20314 try ip.getOrPutString(gpa, "is_var_args"), 20315 ).?); 20316 const return_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20317 try ip.getOrPutString(gpa, "return_type"), 20318 ).?); 20319 const params_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20320 try ip.getOrPutString(gpa, "params"), 20321 ).?); 20322 20323 const is_generic = is_generic_val.toBool(); 20324 if (is_generic) { 20325 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{}); 20326 } 20327 20328 const is_var_args = is_var_args_val.toBool(); 20329 const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val); 20330 if (is_var_args and cc != .C) { 20331 return sema.fail(block, src, "varargs functions must have C calling convention", .{}); 20332 } 20333 20334 const alignment = alignment: { 20335 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { 20336 return sema.fail(block, src, "alignment must fit in 'u32'", .{}); 20337 } 20338 const alignment = @as(u29, @intCast(alignment_val.toUnsignedInt(mod))); 20339 if (alignment == target_util.defaultFunctionAlignment(target)) { 20340 break :alignment .none; 20341 } else { 20342 break :alignment Alignment.fromByteUnits(alignment); 20343 } 20344 }; 20345 const return_type = return_type_val.optionalValue(mod) orelse 20346 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); 20347 20348 const args_len = try sema.usizeCast(block, src, params_val.sliceLen(mod)); 20349 const param_types = try sema.arena.alloc(InternPool.Index, args_len); 20350 20351 var noalias_bits: u32 = 0; 20352 for (param_types, 0..) |*param_type, i| { 20353 const elem_val = try params_val.elemValue(mod, i); 20354 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 20355 const param_is_generic_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20356 try ip.getOrPutString(gpa, "is_generic"), 20357 ).?); 20358 const param_is_noalias_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20359 try ip.getOrPutString(gpa, "is_noalias"), 20360 ).?); 20361 const opt_param_type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20362 try ip.getOrPutString(gpa, "type"), 20363 ).?); 20364 20365 if (param_is_generic_val.toBool()) { 20366 return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{}); 20367 } 20368 20369 const param_type_val = opt_param_type_val.optionalValue(mod) orelse 20370 return sema.fail(block, src, "Type.Fn.Param.arg_type must be non-null for @Type", .{}); 20371 param_type.* = param_type_val.toIntern(); 20372 20373 if (param_is_noalias_val.toBool()) { 20374 if (!param_type.toType().isPtrAtRuntime(mod)) { 20375 return sema.fail(block, src, "non-pointer parameter declared noalias", .{}); 20376 } 20377 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse 20378 return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 20379 } 20380 } 20381 20382 const ty = try mod.funcType(.{ 20383 .param_types = param_types, 20384 .comptime_bits = 0, 20385 .noalias_bits = noalias_bits, 20386 .return_type = return_type.toIntern(), 20387 .alignment = alignment, 20388 .cc = cc, 20389 .is_var_args = is_var_args, 20390 .is_generic = false, 20391 .is_noinline = false, 20392 .align_is_generic = false, 20393 .cc_is_generic = false, 20394 .section_is_generic = false, 20395 .addrspace_is_generic = false, 20396 }); 20397 return sema.addType(ty); 20398 }, 20399 .Frame => return sema.failWithUseOfAsync(block, src), 20400 } 20401 } 20402 20403 fn reifyStruct( 20404 sema: *Sema, 20405 block: *Block, 20406 inst: Zir.Inst.Index, 20407 src: LazySrcLoc, 20408 layout: std.builtin.Type.ContainerLayout, 20409 backing_int_val: Value, 20410 fields_val: Value, 20411 name_strategy: Zir.Inst.NameStrategy, 20412 is_tuple: bool, 20413 ) CompileError!Air.Inst.Ref { 20414 const mod = sema.mod; 20415 const gpa = sema.gpa; 20416 const ip = &mod.intern_pool; 20417 20418 // Because these three things each reference each other, `undefined` 20419 // placeholders are used before being set after the struct type gains an 20420 // InternPool index. 20421 20422 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 20423 .ty = Type.noreturn, 20424 .val = Value.@"unreachable", 20425 }, name_strategy, "struct", inst); 20426 const new_decl = mod.declPtr(new_decl_index); 20427 new_decl.owns_tv = true; 20428 errdefer { 20429 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 20430 mod.abortAnonDecl(new_decl_index); 20431 } 20432 20433 const new_namespace_index = try mod.createNamespace(.{ 20434 .parent = block.namespace.toOptional(), 20435 .ty = undefined, 20436 .file_scope = block.getFileScope(mod), 20437 }); 20438 const new_namespace = mod.namespacePtr(new_namespace_index); 20439 errdefer mod.destroyNamespace(new_namespace_index); 20440 20441 const struct_index = try mod.createStruct(.{ 20442 .owner_decl = new_decl_index, 20443 .fields = .{}, 20444 .zir_index = inst, 20445 .layout = layout, 20446 .status = .have_field_types, 20447 .known_non_opv = false, 20448 .is_tuple = is_tuple, 20449 .namespace = new_namespace_index, 20450 }); 20451 const struct_obj = mod.structPtr(struct_index); 20452 errdefer mod.destroyStruct(struct_index); 20453 20454 const struct_ty = try ip.get(gpa, .{ .struct_type = .{ 20455 .index = struct_index.toOptional(), 20456 .namespace = new_namespace_index.toOptional(), 20457 } }); 20458 // TODO: figure out InternPool removals for incremental compilation 20459 //errdefer ip.remove(struct_ty); 20460 20461 new_decl.ty = Type.type; 20462 new_decl.val = struct_ty.toValue(); 20463 new_namespace.ty = struct_ty.toType(); 20464 20465 // Fields 20466 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); 20467 try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); 20468 var i: usize = 0; 20469 while (i < fields_len) : (i += 1) { 20470 const elem_val = try fields_val.elemValue(mod, i); 20471 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 20472 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20473 try ip.getOrPutString(gpa, "name"), 20474 ).?); 20475 const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20476 try ip.getOrPutString(gpa, "type"), 20477 ).?); 20478 const default_value_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20479 try ip.getOrPutString(gpa, "default_value"), 20480 ).?); 20481 const is_comptime_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20482 try ip.getOrPutString(gpa, "is_comptime"), 20483 ).?); 20484 const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20485 try ip.getOrPutString(gpa, "alignment"), 20486 ).?); 20487 20488 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { 20489 return sema.fail(block, src, "alignment must fit in 'u32'", .{}); 20490 } 20491 const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; 20492 20493 if (layout == .Packed) { 20494 if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); 20495 if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}); 20496 } 20497 if (layout == .Extern and is_comptime_val.toBool()) { 20498 return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}); 20499 } 20500 20501 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 20502 20503 if (is_tuple) { 20504 const field_index = field_name.toUnsigned(ip) orelse return sema.fail( 20505 block, 20506 src, 20507 "tuple cannot have non-numeric field '{}'", 20508 .{field_name.fmt(ip)}, 20509 ); 20510 20511 if (field_index >= fields_len) { 20512 return sema.fail( 20513 block, 20514 src, 20515 "tuple field {} exceeds tuple field count", 20516 .{field_index}, 20517 ); 20518 } 20519 } 20520 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); 20521 if (gop.found_existing) { 20522 // TODO: better source location 20523 return sema.fail(block, src, "duplicate struct field {}", .{field_name.fmt(ip)}); 20524 } 20525 20526 const field_ty = type_val.toType(); 20527 const default_val = if (default_value_val.optionalValue(mod)) |opt_val| 20528 (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse 20529 return sema.failWithNeededComptime(block, src, "struct field default value must be comptime-known")).toIntern() 20530 else 20531 .none; 20532 if (is_comptime_val.toBool() and default_val == .none) { 20533 return sema.fail(block, src, "comptime field without default initialization value", .{}); 20534 } 20535 20536 gop.value_ptr.* = .{ 20537 .ty = field_ty, 20538 .abi_align = Alignment.fromByteUnits(abi_align), 20539 .default_val = default_val, 20540 .is_comptime = is_comptime_val.toBool(), 20541 .offset = undefined, 20542 }; 20543 20544 if (field_ty.zigTypeTag(mod) == .Opaque) { 20545 const msg = msg: { 20546 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 20547 errdefer msg.destroy(gpa); 20548 20549 try sema.addDeclaredHereNote(msg, field_ty); 20550 break :msg msg; 20551 }; 20552 return sema.failWithOwnedErrorMsg(msg); 20553 } 20554 if (field_ty.zigTypeTag(mod) == .NoReturn) { 20555 const msg = msg: { 20556 const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{}); 20557 errdefer msg.destroy(gpa); 20558 20559 try sema.addDeclaredHereNote(msg, field_ty); 20560 break :msg msg; 20561 }; 20562 return sema.failWithOwnedErrorMsg(msg); 20563 } 20564 if (struct_obj.layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) { 20565 const msg = msg: { 20566 const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); 20567 errdefer msg.destroy(gpa); 20568 20569 const src_decl = sema.mod.declPtr(block.src_decl); 20570 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field); 20571 20572 try sema.addDeclaredHereNote(msg, field_ty); 20573 break :msg msg; 20574 }; 20575 return sema.failWithOwnedErrorMsg(msg); 20576 } else if (struct_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { 20577 const msg = msg: { 20578 const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); 20579 errdefer msg.destroy(gpa); 20580 20581 const src_decl = sema.mod.declPtr(block.src_decl); 20582 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); 20583 20584 try sema.addDeclaredHereNote(msg, field_ty); 20585 break :msg msg; 20586 }; 20587 return sema.failWithOwnedErrorMsg(msg); 20588 } 20589 } 20590 20591 if (layout == .Packed) { 20592 struct_obj.status = .layout_wip; 20593 20594 for (struct_obj.fields.values(), 0..) |field, index| { 20595 sema.resolveTypeLayout(field.ty) catch |err| switch (err) { 20596 error.AnalysisFail => { 20597 const msg = sema.err orelse return err; 20598 try sema.addFieldErrNote(struct_ty.toType(), index, msg, "while checking this field", .{}); 20599 return err; 20600 }, 20601 else => return err, 20602 }; 20603 } 20604 20605 var fields_bit_sum: u64 = 0; 20606 for (struct_obj.fields.values()) |field| { 20607 fields_bit_sum += field.ty.bitSize(mod); 20608 } 20609 20610 if (backing_int_val.optionalValue(mod)) |payload| { 20611 const backing_int_ty = payload.toType(); 20612 try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); 20613 struct_obj.backing_int_ty = backing_int_ty; 20614 } else { 20615 struct_obj.backing_int_ty = try mod.intType(.unsigned, @as(u16, @intCast(fields_bit_sum))); 20616 } 20617 20618 struct_obj.status = .have_layout; 20619 } 20620 20621 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20622 try mod.finalizeAnonDecl(new_decl_index); 20623 return decl_val; 20624 } 20625 20626 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { 20627 const va_list_ty = try sema.getBuiltinType("VaList"); 20628 const va_list_ptr = try sema.mod.singleMutPtrType(va_list_ty); 20629 20630 const inst = try sema.resolveInst(zir_ref); 20631 return sema.coerce(block, va_list_ptr, inst, src); 20632 } 20633 20634 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20635 const mod = sema.mod; 20636 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 20637 const src = LazySrcLoc.nodeOffset(extra.node); 20638 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20639 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 20640 20641 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs); 20642 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs); 20643 20644 if (!try sema.validateExternType(arg_ty, .param_ty)) { 20645 const msg = msg: { 20646 const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)}); 20647 errdefer msg.destroy(sema.gpa); 20648 20649 const src_decl = sema.mod.declPtr(block.src_decl); 20650 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty); 20651 20652 try sema.addDeclaredHereNote(msg, arg_ty); 20653 break :msg msg; 20654 }; 20655 return sema.failWithOwnedErrorMsg(msg); 20656 } 20657 20658 try sema.requireRuntimeBlock(block, src, null); 20659 return block.addTyOp(.c_va_arg, arg_ty, va_list_ref); 20660 } 20661 20662 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20663 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 20664 const src = LazySrcLoc.nodeOffset(extra.node); 20665 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20666 20667 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 20668 const va_list_ty = try sema.getBuiltinType("VaList"); 20669 20670 try sema.requireRuntimeBlock(block, src, null); 20671 return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref); 20672 } 20673 20674 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20675 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 20676 const src = LazySrcLoc.nodeOffset(extra.node); 20677 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20678 20679 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 20680 20681 try sema.requireRuntimeBlock(block, src, null); 20682 return block.addUnOp(.c_va_end, va_list_ref); 20683 } 20684 20685 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20686 const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand))); 20687 20688 const va_list_ty = try sema.getBuiltinType("VaList"); 20689 try sema.requireRuntimeBlock(block, src, null); 20690 return block.addInst(.{ 20691 .tag = .c_va_start, 20692 .data = .{ .ty = va_list_ty }, 20693 }); 20694 } 20695 20696 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20697 const mod = sema.mod; 20698 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 20699 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20700 const ty = try sema.resolveType(block, ty_src, inst_data.operand); 20701 20702 var anon_decl = try block.startAnonDecl(); 20703 defer anon_decl.deinit(); 20704 20705 var bytes = std.ArrayList(u8).init(sema.arena); 20706 defer bytes.deinit(); 20707 try ty.print(bytes.writer(), mod); 20708 20709 const decl_ty = try mod.arrayType(.{ 20710 .len = bytes.items.len, 20711 .sentinel = .zero_u8, 20712 .child = .u8_type, 20713 }); 20714 const new_decl = try anon_decl.finish( 20715 decl_ty, 20716 (try mod.intern(.{ .aggregate = .{ 20717 .ty = decl_ty.toIntern(), 20718 .storage = .{ .bytes = bytes.items }, 20719 } })).toValue(), 20720 .none, // default alignment 20721 ); 20722 20723 return sema.analyzeDeclRef(new_decl); 20724 } 20725 20726 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20727 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 20728 const src = inst_data.src(); 20729 return sema.failWithUseOfAsync(block, src); 20730 } 20731 20732 fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20733 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 20734 const src = inst_data.src(); 20735 return sema.failWithUseOfAsync(block, src); 20736 } 20737 20738 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20739 const mod = sema.mod; 20740 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 20741 const src = inst_data.src(); 20742 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 20743 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20744 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat"); 20745 const operand = try sema.resolveInst(extra.rhs); 20746 const operand_ty = sema.typeOf(operand); 20747 20748 _ = try sema.checkIntType(block, src, dest_ty); 20749 try sema.checkFloatType(block, operand_src, operand_ty); 20750 20751 if (try sema.resolveMaybeUndefVal(operand)) |val| { 20752 const result_val = try sema.intFromFloat(block, operand_src, val, operand_ty, dest_ty); 20753 return sema.addConstant(result_val); 20754 } else if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { 20755 return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime-known"); 20756 } 20757 20758 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 20759 if (dest_ty.intInfo(mod).bits == 0) { 20760 if (block.wantSafety()) { 20761 const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, try sema.addConstant(try mod.floatValue(operand_ty, 0.0))); 20762 try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds); 20763 } 20764 return sema.addConstant(try mod.intValue(dest_ty, 0)); 20765 } 20766 const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_ty, operand); 20767 if (block.wantSafety()) { 20768 const back = try block.addTyOp(.float_from_int, operand_ty, result); 20769 const diff = try block.addBinOp(.sub, operand, back); 20770 const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, try sema.addConstant(try mod.floatValue(operand_ty, 1.0))); 20771 const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, try sema.addConstant(try mod.floatValue(operand_ty, -1.0))); 20772 const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg); 20773 try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds); 20774 } 20775 return result; 20776 } 20777 20778 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20779 const mod = sema.mod; 20780 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 20781 const src = inst_data.src(); 20782 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 20783 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20784 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt"); 20785 const operand = try sema.resolveInst(extra.rhs); 20786 const operand_ty = sema.typeOf(operand); 20787 20788 try sema.checkFloatType(block, src, dest_ty); 20789 _ = try sema.checkIntType(block, operand_src, operand_ty); 20790 20791 if (try sema.resolveMaybeUndefVal(operand)) |val| { 20792 const result_val = try val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, sema.mod, sema); 20793 return sema.addConstant(result_val); 20794 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { 20795 return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime-known"); 20796 } 20797 20798 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 20799 return block.addTyOp(.float_from_int, dest_ty, operand); 20800 } 20801 20802 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20803 const mod = sema.mod; 20804 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 20805 const src = inst_data.src(); 20806 20807 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 20808 20809 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20810 const operand_res = try sema.resolveInst(extra.rhs); 20811 const operand_coerced = try sema.coerce(block, Type.usize, operand_res, operand_src); 20812 20813 const ptr_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt"); 20814 try sema.checkPtrType(block, src, ptr_ty); 20815 const elem_ty = ptr_ty.elemType2(mod); 20816 const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema); 20817 20818 if (ptr_ty.isSlice(mod)) { 20819 const msg = msg: { 20820 const msg = try sema.errMsg(block, src, "integer cannot be converted to slice type '{}'", .{ptr_ty.fmt(sema.mod)}); 20821 errdefer msg.destroy(sema.gpa); 20822 try sema.errNote(block, src, msg, "slice length cannot be inferred from address", .{}); 20823 break :msg msg; 20824 }; 20825 return sema.failWithOwnedErrorMsg(msg); 20826 } 20827 20828 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { 20829 const addr = val.toUnsignedInt(mod); 20830 if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0) 20831 return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)}); 20832 if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0) 20833 return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)}); 20834 20835 const ptr_val = switch (ptr_ty.zigTypeTag(mod)) { 20836 .Optional => (try mod.intern(.{ .opt = .{ 20837 .ty = ptr_ty.toIntern(), 20838 .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(), 20839 } })).toValue(), 20840 .Pointer => try mod.ptrIntValue(ptr_ty, addr), 20841 else => unreachable, 20842 }; 20843 return sema.addConstant(ptr_val); 20844 } 20845 20846 try sema.requireRuntimeBlock(block, src, operand_src); 20847 if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) { 20848 if (!ptr_ty.isAllowzeroPtr(mod)) { 20849 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); 20850 try sema.addSafetyCheck(block, is_non_zero, .cast_to_null); 20851 } 20852 20853 if (ptr_align > 1) { 20854 const align_minus_1 = try sema.addConstant( 20855 try mod.intValue(Type.usize, ptr_align - 1), 20856 ); 20857 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1); 20858 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 20859 try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment); 20860 } 20861 } 20862 return block.addBitCast(ptr_ty, operand_coerced); 20863 } 20864 20865 fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20866 const mod = sema.mod; 20867 const ip = &mod.intern_pool; 20868 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 20869 const src = LazySrcLoc.nodeOffset(extra.node); 20870 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20871 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@errSetCast"); 20872 const operand = try sema.resolveInst(extra.rhs); 20873 const operand_ty = sema.typeOf(operand); 20874 try sema.checkErrorSetType(block, src, dest_ty); 20875 try sema.checkErrorSetType(block, operand_src, operand_ty); 20876 20877 // operand must be defined since it can be an invalid error value 20878 const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand); 20879 20880 if (disjoint: { 20881 // Try avoiding resolving inferred error sets if we can 20882 if (!dest_ty.isAnyError(mod) and dest_ty.errorSetNames(mod).len == 0) break :disjoint true; 20883 if (!operand_ty.isAnyError(mod) and operand_ty.errorSetNames(mod).len == 0) break :disjoint true; 20884 if (dest_ty.isAnyError(mod)) break :disjoint false; 20885 if (operand_ty.isAnyError(mod)) break :disjoint false; 20886 for (dest_ty.errorSetNames(mod)) |dest_err_name| { 20887 if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) 20888 break :disjoint false; 20889 } 20890 20891 if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and 20892 !ip.isInferredErrorSetType(operand_ty.toIntern())) 20893 { 20894 break :disjoint true; 20895 } 20896 20897 try sema.resolveInferredErrorSetTy(block, src, dest_ty); 20898 try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty); 20899 for (dest_ty.errorSetNames(mod)) |dest_err_name| { 20900 if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) 20901 break :disjoint false; 20902 } 20903 20904 break :disjoint true; 20905 }) { 20906 const msg = msg: { 20907 const msg = try sema.errMsg( 20908 block, 20909 src, 20910 "error sets '{}' and '{}' have no common errors", 20911 .{ operand_ty.fmt(sema.mod), dest_ty.fmt(sema.mod) }, 20912 ); 20913 errdefer msg.destroy(sema.gpa); 20914 try sema.addDeclaredHereNote(msg, operand_ty); 20915 try sema.addDeclaredHereNote(msg, dest_ty); 20916 break :msg msg; 20917 }; 20918 return sema.failWithOwnedErrorMsg(msg); 20919 } 20920 20921 if (maybe_operand_val) |val| { 20922 if (!dest_ty.isAnyError(mod)) { 20923 const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; 20924 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) { 20925 const msg = msg: { 20926 const msg = try sema.errMsg( 20927 block, 20928 src, 20929 "'error.{}' not a member of error set '{}'", 20930 .{ error_name.fmt(ip), dest_ty.fmt(sema.mod) }, 20931 ); 20932 errdefer msg.destroy(sema.gpa); 20933 try sema.addDeclaredHereNote(msg, dest_ty); 20934 break :msg msg; 20935 }; 20936 return sema.failWithOwnedErrorMsg(msg); 20937 } 20938 } 20939 20940 return sema.addConstant(try mod.getCoerced(val, dest_ty)); 20941 } 20942 20943 try sema.requireRuntimeBlock(block, src, operand_src); 20944 if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) { 20945 const err_int_inst = try block.addBitCast(Type.err_int, operand); 20946 const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); 20947 try sema.addSafetyCheck(block, ok, .invalid_error_code); 20948 } 20949 return block.addBitCast(dest_ty, operand); 20950 } 20951 20952 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20953 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(u5, @truncate(extended.small))); 20954 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 20955 const src = LazySrcLoc.nodeOffset(extra.node); 20956 const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node }; 20957 const operand = try sema.resolveInst(extra.rhs); 20958 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName()); 20959 return sema.ptrCastFull( 20960 block, 20961 flags, 20962 src, 20963 operand, 20964 operand_src, 20965 dest_ty, 20966 ); 20967 } 20968 20969 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20970 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 20971 const src = inst_data.src(); 20972 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20973 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 20974 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, "@ptrCast"); 20975 const operand = try sema.resolveInst(extra.rhs); 20976 20977 return sema.ptrCastFull( 20978 block, 20979 .{ .ptr_cast = true }, 20980 src, 20981 operand, 20982 operand_src, 20983 dest_ty, 20984 ); 20985 } 20986 20987 fn ptrCastFull( 20988 sema: *Sema, 20989 block: *Block, 20990 flags: Zir.Inst.FullPtrCastFlags, 20991 src: LazySrcLoc, 20992 operand: Air.Inst.Ref, 20993 operand_src: LazySrcLoc, 20994 dest_ty: Type, 20995 ) CompileError!Air.Inst.Ref { 20996 const mod = sema.mod; 20997 const operand_ty = sema.typeOf(operand); 20998 20999 try sema.checkPtrType(block, src, dest_ty); 21000 try sema.checkPtrOperand(block, operand_src, operand_ty); 21001 21002 const src_info = operand_ty.ptrInfo(mod); 21003 const dest_info = dest_ty.ptrInfo(mod); 21004 21005 try sema.resolveTypeLayout(src_info.child.toType()); 21006 try sema.resolveTypeLayout(dest_info.child.toType()); 21007 21008 const src_slice_like = src_info.flags.size == .Slice or 21009 (src_info.flags.size == .One and src_info.child.toType().zigTypeTag(mod) == .Array); 21010 21011 const dest_slice_like = dest_info.flags.size == .Slice or 21012 (dest_info.flags.size == .One and dest_info.child.toType().zigTypeTag(mod) == .Array); 21013 21014 if (dest_info.flags.size == .Slice and !src_slice_like) { 21015 return sema.fail(block, src, "illegal pointer cast to slice", .{}); 21016 } 21017 21018 if (dest_info.flags.size == .Slice) { 21019 const src_elem_size = switch (src_info.flags.size) { 21020 .Slice => src_info.child.toType().abiSize(mod), 21021 // pointer to array 21022 .One => src_info.child.toType().childType(mod).abiSize(mod), 21023 else => unreachable, 21024 }; 21025 const dest_elem_size = dest_info.child.toType().abiSize(mod); 21026 if (src_elem_size != dest_elem_size) { 21027 return sema.fail(block, src, "TODO: implement @ptrCast between slices changing the length", .{}); 21028 } 21029 } 21030 21031 // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs 21032 21033 if (!flags.ptr_cast) { 21034 check_size: { 21035 if (src_info.flags.size == dest_info.flags.size) break :check_size; 21036 if (src_slice_like and dest_slice_like) break :check_size; 21037 if (src_info.flags.size == .C) break :check_size; 21038 if (dest_info.flags.size == .C) break :check_size; 21039 return sema.failWithOwnedErrorMsg(msg: { 21040 const msg = try sema.errMsg(block, src, "cannot implicitly convert {s} pointer to {s} pointer", .{ 21041 pointerSizeString(src_info.flags.size), 21042 pointerSizeString(dest_info.flags.size), 21043 }); 21044 errdefer msg.destroy(sema.gpa); 21045 if (dest_info.flags.size == .Many and 21046 (src_info.flags.size == .Slice or 21047 (src_info.flags.size == .One and src_info.child.toType().zigTypeTag(mod) == .Array))) 21048 { 21049 try sema.errNote(block, src, msg, "use 'ptr' field to convert slice to many pointer", .{}); 21050 } else { 21051 try sema.errNote(block, src, msg, "use @ptrCast to change pointer size", .{}); 21052 } 21053 break :msg msg; 21054 }); 21055 } 21056 21057 check_child: { 21058 const src_child = if (dest_info.flags.size == .Slice and src_info.flags.size == .One) blk: { 21059 // *[n]T -> []T 21060 break :blk src_info.child.toType().childType(mod); 21061 } else src_info.child.toType(); 21062 21063 const dest_child = dest_info.child.toType(); 21064 21065 const imc_res = try sema.coerceInMemoryAllowed( 21066 block, 21067 dest_child, 21068 src_child, 21069 !dest_info.flags.is_const, 21070 mod.getTarget(), 21071 src, 21072 operand_src, 21073 ); 21074 if (imc_res == .ok) break :check_child; 21075 return sema.failWithOwnedErrorMsg(msg: { 21076 const msg = try sema.errMsg(block, src, "pointer element type '{}' cannot coerce into element type '{}'", .{ 21077 src_child.fmt(mod), 21078 dest_child.fmt(mod), 21079 }); 21080 errdefer msg.destroy(sema.gpa); 21081 try imc_res.report(sema, block, src, msg); 21082 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer element type", .{}); 21083 break :msg msg; 21084 }); 21085 } 21086 21087 check_sent: { 21088 if (dest_info.sentinel == .none) break :check_sent; 21089 if (src_info.flags.size == .C) break :check_sent; 21090 if (src_info.sentinel != .none) { 21091 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child); 21092 if (dest_info.sentinel == coerced_sent) break :check_sent; 21093 } 21094 if (src_slice_like and src_info.flags.size == .One and dest_info.flags.size == .Slice) { 21095 // [*]nT -> []T 21096 const arr_ty = src_info.child.toType(); 21097 if (arr_ty.sentinel(mod)) |src_sentinel| { 21098 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_sentinel.toIntern(), dest_info.child); 21099 if (dest_info.sentinel == coerced_sent) break :check_sent; 21100 } 21101 } 21102 return sema.failWithOwnedErrorMsg(msg: { 21103 const msg = if (src_info.sentinel == .none) blk: { 21104 break :blk try sema.errMsg(block, src, "destination pointer requires '{}' sentinel", .{ 21105 dest_info.sentinel.toValue().fmtValue(dest_info.child.toType(), mod), 21106 }); 21107 } else blk: { 21108 break :blk try sema.errMsg(block, src, "pointer sentinel '{}' cannot coerce into pointer sentinel '{}'", .{ 21109 src_info.sentinel.toValue().fmtValue(src_info.child.toType(), mod), 21110 dest_info.sentinel.toValue().fmtValue(dest_info.child.toType(), mod), 21111 }); 21112 }; 21113 errdefer msg.destroy(sema.gpa); 21114 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer sentinel", .{}); 21115 break :msg msg; 21116 }); 21117 } 21118 21119 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) { 21120 return sema.failWithOwnedErrorMsg(msg: { 21121 const msg = try sema.errMsg(block, src, "pointer host size '{}' cannot coerce into pointer host size '{}'", .{ 21122 src_info.packed_offset.host_size, 21123 dest_info.packed_offset.host_size, 21124 }); 21125 errdefer msg.destroy(sema.gpa); 21126 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer host size", .{}); 21127 break :msg msg; 21128 }); 21129 } 21130 21131 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) { 21132 return sema.failWithOwnedErrorMsg(msg: { 21133 const msg = try sema.errMsg(block, src, "pointer bit offset '{}' cannot coerce into pointer bit offset '{}'", .{ 21134 src_info.packed_offset.bit_offset, 21135 dest_info.packed_offset.bit_offset, 21136 }); 21137 errdefer msg.destroy(sema.gpa); 21138 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer bit offset", .{}); 21139 break :msg msg; 21140 }); 21141 } 21142 21143 check_allowzero: { 21144 const src_allows_zero = operand_ty.ptrAllowsZero(mod); 21145 const dest_allows_zero = dest_ty.ptrAllowsZero(mod); 21146 if (!src_allows_zero) break :check_allowzero; 21147 if (dest_allows_zero) break :check_allowzero; 21148 21149 return sema.failWithOwnedErrorMsg(msg: { 21150 const msg = try sema.errMsg(block, src, "'{}' could have null values which are illegal in type '{}'", .{ 21151 operand_ty.fmt(mod), 21152 dest_ty.fmt(mod), 21153 }); 21154 errdefer msg.destroy(sema.gpa); 21155 try sema.errNote(block, src, msg, "use @ptrCast to assert the pointer is not null", .{}); 21156 break :msg msg; 21157 }); 21158 } 21159 21160 // TODO: vector index? 21161 } 21162 21163 const src_align = src_info.flags.alignment.toByteUnitsOptional() orelse src_info.child.toType().abiAlignment(mod); 21164 const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse dest_info.child.toType().abiAlignment(mod); 21165 if (!flags.align_cast) { 21166 if (dest_align > src_align) { 21167 return sema.failWithOwnedErrorMsg(msg: { 21168 const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{}); 21169 errdefer msg.destroy(sema.gpa); 21170 try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{ 21171 operand_ty.fmt(mod), src_align, 21172 }); 21173 try sema.errNote(block, src, msg, "'{}' has alignment '{d}'", .{ 21174 dest_ty.fmt(mod), dest_align, 21175 }); 21176 try sema.errNote(block, src, msg, "use @alignCast to assert pointer alignment", .{}); 21177 break :msg msg; 21178 }); 21179 } 21180 } 21181 21182 if (!flags.addrspace_cast) { 21183 if (src_info.flags.address_space != dest_info.flags.address_space) { 21184 return sema.failWithOwnedErrorMsg(msg: { 21185 const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{}); 21186 errdefer msg.destroy(sema.gpa); 21187 try sema.errNote(block, operand_src, msg, "'{}' has address space '{s}'", .{ 21188 operand_ty.fmt(mod), @tagName(src_info.flags.address_space), 21189 }); 21190 try sema.errNote(block, src, msg, "'{}' has address space '{s}'", .{ 21191 dest_ty.fmt(mod), @tagName(dest_info.flags.address_space), 21192 }); 21193 try sema.errNote(block, src, msg, "use @addrSpaceCast to cast pointer address space", .{}); 21194 break :msg msg; 21195 }); 21196 } 21197 } else { 21198 // Some address space casts are always disallowed 21199 if (!target_util.addrSpaceCastIsValid(mod.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) { 21200 return sema.failWithOwnedErrorMsg(msg: { 21201 const msg = try sema.errMsg(block, src, "invalid address space cast", .{}); 21202 errdefer msg.destroy(sema.gpa); 21203 try sema.errNote(block, operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{ 21204 @tagName(src_info.flags.address_space), 21205 @tagName(dest_info.flags.address_space), 21206 }); 21207 break :msg msg; 21208 }); 21209 } 21210 } 21211 21212 if (!flags.const_cast) { 21213 if (src_info.flags.is_const and !dest_info.flags.is_const) { 21214 return sema.failWithOwnedErrorMsg(msg: { 21215 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{}); 21216 errdefer msg.destroy(sema.gpa); 21217 try sema.errNote(block, src, msg, "use @constCast to discard const qualifier", .{}); 21218 break :msg msg; 21219 }); 21220 } 21221 } 21222 21223 if (!flags.volatile_cast) { 21224 if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) { 21225 return sema.failWithOwnedErrorMsg(msg: { 21226 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{}); 21227 errdefer msg.destroy(sema.gpa); 21228 try sema.errNote(block, src, msg, "use @volatileCast to discard volatile qualifier", .{}); 21229 break :msg msg; 21230 }); 21231 } 21232 } 21233 21234 const ptr = if (src_info.flags.size == .Slice and dest_info.flags.size != .Slice) ptr: { 21235 break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); 21236 } else operand; 21237 21238 const dest_ptr_ty = if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) blk: { 21239 // Only convert to a many-pointer at first 21240 var info = dest_info; 21241 info.flags.size = .Many; 21242 const ty = try mod.ptrType(info); 21243 if (dest_ty.zigTypeTag(mod) == .Optional) { 21244 break :blk try mod.optionalType(ty.toIntern()); 21245 } else { 21246 break :blk ty; 21247 } 21248 } else dest_ty; 21249 21250 // Cannot do @addrSpaceCast at comptime 21251 if (!flags.addrspace_cast) { 21252 if (try sema.resolveMaybeUndefVal(ptr)) |ptr_val| { 21253 if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isUndef(mod)) { 21254 return sema.failWithUseOfUndef(block, operand_src); 21255 } 21256 if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isNull(mod)) { 21257 return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)}); 21258 } 21259 if (dest_align > src_align) { 21260 if (try ptr_val.getUnsignedIntAdvanced(mod, null)) |addr| { 21261 if (addr % dest_align != 0) { 21262 return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ addr, dest_align }); 21263 } 21264 } 21265 } 21266 if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) { 21267 if (ptr_val.isUndef(mod)) return sema.addConstUndef(dest_ty); 21268 const arr_len = try mod.intValue(Type.usize, src_info.child.toType().arrayLen(mod)); 21269 return sema.addConstant((try mod.intern(.{ .ptr = .{ 21270 .ty = dest_ty.toIntern(), 21271 .addr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr, 21272 .len = arr_len.toIntern(), 21273 } })).toValue()); 21274 } else { 21275 assert(dest_ptr_ty.eql(dest_ty, mod)); 21276 return sema.addConstant(try mod.getCoerced(ptr_val, dest_ty)); 21277 } 21278 } 21279 } 21280 21281 try sema.requireRuntimeBlock(block, src, null); 21282 21283 if (block.wantSafety() and operand_ty.ptrAllowsZero(mod) and !dest_ty.ptrAllowsZero(mod) and 21284 (try sema.typeHasRuntimeBits(dest_info.child.toType()) or dest_info.child.toType().zigTypeTag(mod) == .Fn)) 21285 { 21286 const ptr_int = try block.addUnOp(.int_from_ptr, ptr); 21287 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); 21288 const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: { 21289 const len = try sema.analyzeSliceLen(block, operand_src, ptr); 21290 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 21291 break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero); 21292 } else is_non_zero; 21293 try sema.addSafetyCheck(block, ok, .cast_to_null); 21294 } 21295 21296 if (block.wantSafety() and dest_align > src_align and try sema.typeHasRuntimeBits(dest_info.child.toType())) { 21297 const align_minus_1 = try sema.addConstant( 21298 try mod.intValue(Type.usize, dest_align - 1), 21299 ); 21300 const ptr_int = try block.addUnOp(.int_from_ptr, ptr); 21301 const remainder = try block.addBinOp(.bit_and, ptr_int, align_minus_1); 21302 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 21303 const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: { 21304 const len = try sema.analyzeSliceLen(block, operand_src, ptr); 21305 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 21306 break :ok try block.addBinOp(.bit_or, len_zero, is_aligned); 21307 } else is_aligned; 21308 try sema.addSafetyCheck(block, ok, .incorrect_alignment); 21309 } 21310 21311 // If we're going from an array pointer to a slice, this will only be the pointer part! 21312 const result_ptr = if (flags.addrspace_cast) ptr: { 21313 // We can't change address spaces with a bitcast, so this requires two instructions 21314 var intermediate_info = src_info; 21315 intermediate_info.flags.address_space = dest_info.flags.address_space; 21316 const intermediate_ptr_ty = try mod.ptrType(intermediate_info); 21317 const intermediate_ty = if (dest_ptr_ty.zigTypeTag(mod) == .Optional) blk: { 21318 break :blk try mod.optionalType(intermediate_ptr_ty.toIntern()); 21319 } else intermediate_ptr_ty; 21320 const intermediate = try block.addInst(.{ 21321 .tag = .addrspace_cast, 21322 .data = .{ .ty_op = .{ 21323 .ty = try sema.addType(intermediate_ty), 21324 .operand = ptr, 21325 } }, 21326 }); 21327 if (intermediate_ty.eql(dest_ptr_ty, mod)) { 21328 // We only changed the address space, so no need for a bitcast 21329 break :ptr intermediate; 21330 } 21331 break :ptr try block.addBitCast(dest_ptr_ty, intermediate); 21332 } else ptr: { 21333 break :ptr try block.addBitCast(dest_ptr_ty, ptr); 21334 }; 21335 21336 if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) { 21337 // We have to construct a slice using the operand's child's array length 21338 // Note that we know from the check at the start of the function that operand_ty is slice-like 21339 const arr_len = try sema.addConstant( 21340 try mod.intValue(Type.usize, src_info.child.toType().arrayLen(mod)), 21341 ); 21342 return block.addInst(.{ 21343 .tag = .slice, 21344 .data = .{ .ty_pl = .{ 21345 .ty = try sema.addType(dest_ty), 21346 .payload = try sema.addExtra(Air.Bin{ 21347 .lhs = result_ptr, 21348 .rhs = arr_len, 21349 }), 21350 } }, 21351 }); 21352 } else { 21353 assert(dest_ptr_ty.eql(dest_ty, mod)); 21354 return result_ptr; 21355 } 21356 } 21357 21358 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21359 const mod = sema.mod; 21360 const flags = @as(Zir.Inst.FullPtrCastFlags, @bitCast(@as(u5, @truncate(extended.small)))); 21361 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 21362 const src = LazySrcLoc.nodeOffset(extra.node); 21363 const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node }; 21364 const operand = try sema.resolveInst(extra.operand); 21365 const operand_ty = sema.typeOf(operand); 21366 try sema.checkPtrOperand(block, operand_src, operand_ty); 21367 21368 var ptr_info = operand_ty.ptrInfo(mod); 21369 if (flags.const_cast) ptr_info.flags.is_const = false; 21370 if (flags.volatile_cast) ptr_info.flags.is_volatile = false; 21371 const dest_ty = try mod.ptrType(ptr_info); 21372 21373 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { 21374 return sema.addConstant(try mod.getCoerced(operand_val, dest_ty)); 21375 } 21376 21377 try sema.requireRuntimeBlock(block, src, null); 21378 return block.addBitCast(dest_ty, operand); 21379 } 21380 21381 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21382 const mod = sema.mod; 21383 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 21384 const src = inst_data.src(); 21385 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21386 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21387 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate"); 21388 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src); 21389 const operand = try sema.resolveInst(extra.rhs); 21390 const operand_ty = sema.typeOf(operand); 21391 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 21392 21393 const operand_is_vector = operand_ty.zigTypeTag(mod) == .Vector; 21394 const dest_is_vector = dest_ty.zigTypeTag(mod) == .Vector; 21395 if (operand_is_vector != dest_is_vector) { 21396 return sema.fail(block, operand_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), operand_ty.fmt(mod) }); 21397 } 21398 21399 if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 21400 return sema.coerce(block, dest_ty, operand, operand_src); 21401 } 21402 21403 const dest_info = dest_scalar_ty.intInfo(mod); 21404 21405 if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { 21406 return sema.addConstant(val); 21407 } 21408 21409 if (operand_scalar_ty.zigTypeTag(mod) != .ComptimeInt) { 21410 const operand_info = operand_ty.intInfo(mod); 21411 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 21412 return sema.addConstant(val); 21413 } 21414 21415 if (operand_info.signedness != dest_info.signedness) { 21416 return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{ 21417 @tagName(dest_info.signedness), operand_ty.fmt(mod), 21418 }); 21419 } 21420 if (operand_info.bits < dest_info.bits) { 21421 const msg = msg: { 21422 const msg = try sema.errMsg( 21423 block, 21424 src, 21425 "destination type '{}' has more bits than source type '{}'", 21426 .{ dest_ty.fmt(mod), operand_ty.fmt(mod) }, 21427 ); 21428 errdefer msg.destroy(sema.gpa); 21429 try sema.errNote(block, src, msg, "destination type has {d} bits", .{ 21430 dest_info.bits, 21431 }); 21432 try sema.errNote(block, operand_src, msg, "operand type has {d} bits", .{ 21433 operand_info.bits, 21434 }); 21435 break :msg msg; 21436 }; 21437 return sema.failWithOwnedErrorMsg(msg); 21438 } 21439 } 21440 21441 if (try sema.resolveMaybeUndefValIntable(operand)) |val| { 21442 if (val.isUndef(mod)) return sema.addConstUndef(dest_ty); 21443 if (!dest_is_vector) { 21444 return sema.addConstant(try mod.getCoerced( 21445 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, mod), 21446 dest_ty, 21447 )); 21448 } 21449 const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(mod)); 21450 for (elems, 0..) |*elem, i| { 21451 const elem_val = try val.elemValue(mod, i); 21452 elem.* = try (try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, mod)).intern(dest_scalar_ty, mod); 21453 } 21454 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 21455 .ty = dest_ty.toIntern(), 21456 .storage = .{ .elems = elems }, 21457 } })).toValue()); 21458 } 21459 21460 try sema.requireRuntimeBlock(block, src, operand_src); 21461 return block.addTyOp(.trunc, dest_ty, operand); 21462 } 21463 21464 fn zirBitCount( 21465 sema: *Sema, 21466 block: *Block, 21467 inst: Zir.Inst.Index, 21468 air_tag: Air.Inst.Tag, 21469 comptime comptimeOp: fn (val: Value, ty: Type, mod: *Module) u64, 21470 ) CompileError!Air.Inst.Ref { 21471 const mod = sema.mod; 21472 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 21473 const src = inst_data.src(); 21474 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21475 const operand = try sema.resolveInst(inst_data.operand); 21476 const operand_ty = sema.typeOf(operand); 21477 _ = try sema.checkIntOrVector(block, operand, operand_src); 21478 const bits = operand_ty.intInfo(mod).bits; 21479 21480 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 21481 return sema.addConstant(val); 21482 } 21483 21484 const result_scalar_ty = try mod.smallestUnsignedInt(bits); 21485 switch (operand_ty.zigTypeTag(mod)) { 21486 .Vector => { 21487 const vec_len = operand_ty.vectorLen(mod); 21488 const result_ty = try mod.vectorType(.{ 21489 .len = vec_len, 21490 .child = result_scalar_ty.toIntern(), 21491 }); 21492 if (try sema.resolveMaybeUndefVal(operand)) |val| { 21493 if (val.isUndef(mod)) return sema.addConstUndef(result_ty); 21494 21495 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 21496 const scalar_ty = operand_ty.scalarType(mod); 21497 for (elems, 0..) |*elem, i| { 21498 const elem_val = try val.elemValue(mod, i); 21499 const count = comptimeOp(elem_val, scalar_ty, mod); 21500 elem.* = (try mod.intValue(result_scalar_ty, count)).toIntern(); 21501 } 21502 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 21503 .ty = result_ty.toIntern(), 21504 .storage = .{ .elems = elems }, 21505 } })).toValue()); 21506 } else { 21507 try sema.requireRuntimeBlock(block, src, operand_src); 21508 return block.addTyOp(air_tag, result_ty, operand); 21509 } 21510 }, 21511 .Int => { 21512 if (try sema.resolveMaybeUndefLazyVal(operand)) |val| { 21513 if (val.isUndef(mod)) return sema.addConstUndef(result_scalar_ty); 21514 return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, mod)); 21515 } else { 21516 try sema.requireRuntimeBlock(block, src, operand_src); 21517 return block.addTyOp(air_tag, result_scalar_ty, operand); 21518 } 21519 }, 21520 else => unreachable, 21521 } 21522 } 21523 21524 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21525 const mod = sema.mod; 21526 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 21527 const src = inst_data.src(); 21528 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21529 const operand = try sema.resolveInst(inst_data.operand); 21530 const operand_ty = sema.typeOf(operand); 21531 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 21532 const bits = scalar_ty.intInfo(mod).bits; 21533 if (bits % 8 != 0) { 21534 return sema.fail( 21535 block, 21536 operand_src, 21537 "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", 21538 .{ scalar_ty.fmt(mod), bits }, 21539 ); 21540 } 21541 21542 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 21543 return sema.addConstant(val); 21544 } 21545 21546 switch (operand_ty.zigTypeTag(mod)) { 21547 .Int => { 21548 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { 21549 if (val.isUndef(mod)) return sema.addConstUndef(operand_ty); 21550 const result_val = try val.byteSwap(operand_ty, mod, sema.arena); 21551 return sema.addConstant(result_val); 21552 } else operand_src; 21553 21554 try sema.requireRuntimeBlock(block, src, runtime_src); 21555 return block.addTyOp(.byte_swap, operand_ty, operand); 21556 }, 21557 .Vector => { 21558 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { 21559 if (val.isUndef(mod)) 21560 return sema.addConstUndef(operand_ty); 21561 21562 const vec_len = operand_ty.vectorLen(mod); 21563 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 21564 for (elems, 0..) |*elem, i| { 21565 const elem_val = try val.elemValue(mod, i); 21566 elem.* = try (try elem_val.byteSwap(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod); 21567 } 21568 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 21569 .ty = operand_ty.toIntern(), 21570 .storage = .{ .elems = elems }, 21571 } })).toValue()); 21572 } else operand_src; 21573 21574 try sema.requireRuntimeBlock(block, src, runtime_src); 21575 return block.addTyOp(.byte_swap, operand_ty, operand); 21576 }, 21577 else => unreachable, 21578 } 21579 } 21580 21581 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21582 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 21583 const src = inst_data.src(); 21584 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21585 const operand = try sema.resolveInst(inst_data.operand); 21586 const operand_ty = sema.typeOf(operand); 21587 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 21588 21589 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 21590 return sema.addConstant(val); 21591 } 21592 21593 const mod = sema.mod; 21594 switch (operand_ty.zigTypeTag(mod)) { 21595 .Int => { 21596 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { 21597 if (val.isUndef(mod)) return sema.addConstUndef(operand_ty); 21598 const result_val = try val.bitReverse(operand_ty, mod, sema.arena); 21599 return sema.addConstant(result_val); 21600 } else operand_src; 21601 21602 try sema.requireRuntimeBlock(block, src, runtime_src); 21603 return block.addTyOp(.bit_reverse, operand_ty, operand); 21604 }, 21605 .Vector => { 21606 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { 21607 if (val.isUndef(mod)) 21608 return sema.addConstUndef(operand_ty); 21609 21610 const vec_len = operand_ty.vectorLen(mod); 21611 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 21612 for (elems, 0..) |*elem, i| { 21613 const elem_val = try val.elemValue(mod, i); 21614 elem.* = try (try elem_val.bitReverse(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod); 21615 } 21616 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 21617 .ty = operand_ty.toIntern(), 21618 .storage = .{ .elems = elems }, 21619 } })).toValue()); 21620 } else operand_src; 21621 21622 try sema.requireRuntimeBlock(block, src, runtime_src); 21623 return block.addTyOp(.bit_reverse, operand_ty, operand); 21624 }, 21625 else => unreachable, 21626 } 21627 } 21628 21629 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21630 const offset = try sema.bitOffsetOf(block, inst); 21631 return sema.addIntUnsigned(Type.comptime_int, offset); 21632 } 21633 21634 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21635 const offset = try sema.bitOffsetOf(block, inst); 21636 // TODO reminder to make this a compile error for packed structs 21637 return sema.addIntUnsigned(Type.comptime_int, offset / 8); 21638 } 21639 21640 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 { 21641 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 21642 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 21643 sema.src = src; 21644 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 21645 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 21646 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21647 21648 const ty = try sema.resolveType(block, lhs_src, extra.lhs); 21649 const field_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, "name of field must be comptime-known"); 21650 21651 const mod = sema.mod; 21652 try sema.resolveTypeLayout(ty); 21653 switch (ty.zigTypeTag(mod)) { 21654 .Struct => {}, 21655 else => { 21656 const msg = msg: { 21657 const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(mod)}); 21658 errdefer msg.destroy(sema.gpa); 21659 try sema.addDeclaredHereNote(msg, ty); 21660 break :msg msg; 21661 }; 21662 return sema.failWithOwnedErrorMsg(msg); 21663 }, 21664 } 21665 21666 const field_index = if (ty.isTuple(mod)) blk: { 21667 if (mod.intern_pool.stringEqlSlice(field_name, "len")) { 21668 return sema.fail(block, src, "no offset available for 'len' field of tuple", .{}); 21669 } 21670 break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src); 21671 } else try sema.structFieldIndex(block, ty, field_name, rhs_src); 21672 21673 if (ty.structFieldIsComptime(field_index, mod)) { 21674 return sema.fail(block, src, "no offset available for comptime field", .{}); 21675 } 21676 21677 switch (ty.containerLayout(mod)) { 21678 .Packed => { 21679 var bit_sum: u64 = 0; 21680 const fields = ty.structFields(mod); 21681 for (fields.values(), 0..) |field, i| { 21682 if (i == field_index) { 21683 return bit_sum; 21684 } 21685 bit_sum += field.ty.bitSize(mod); 21686 } else unreachable; 21687 }, 21688 else => return ty.structFieldOffset(field_index, mod) * 8, 21689 } 21690 } 21691 21692 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 21693 const mod = sema.mod; 21694 switch (ty.zigTypeTag(mod)) { 21695 .Struct, .Enum, .Union, .Opaque => return, 21696 else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(mod)}), 21697 } 21698 } 21699 21700 /// Returns `true` if the type was a comptime_int. 21701 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { 21702 const mod = sema.mod; 21703 switch (try ty.zigTypeTagOrPoison(mod)) { 21704 .ComptimeInt => return true, 21705 .Int => return false, 21706 else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(mod)}), 21707 } 21708 } 21709 21710 fn checkInvalidPtrArithmetic( 21711 sema: *Sema, 21712 block: *Block, 21713 src: LazySrcLoc, 21714 ty: Type, 21715 ) CompileError!void { 21716 const mod = sema.mod; 21717 switch (try ty.zigTypeTagOrPoison(mod)) { 21718 .Pointer => switch (ty.ptrSize(mod)) { 21719 .One, .Slice => return, 21720 .Many, .C => return sema.fail( 21721 block, 21722 src, 21723 "invalid pointer arithmetic operator", 21724 .{}, 21725 ), 21726 }, 21727 else => return, 21728 } 21729 } 21730 21731 fn checkArithmeticOp( 21732 sema: *Sema, 21733 block: *Block, 21734 src: LazySrcLoc, 21735 scalar_tag: std.builtin.TypeId, 21736 lhs_zig_ty_tag: std.builtin.TypeId, 21737 rhs_zig_ty_tag: std.builtin.TypeId, 21738 zir_tag: Zir.Inst.Tag, 21739 ) CompileError!void { 21740 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 21741 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; 21742 21743 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { 21744 return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ 21745 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), 21746 }); 21747 } 21748 } 21749 21750 fn checkPtrOperand( 21751 sema: *Sema, 21752 block: *Block, 21753 ty_src: LazySrcLoc, 21754 ty: Type, 21755 ) CompileError!void { 21756 const mod = sema.mod; 21757 switch (ty.zigTypeTag(mod)) { 21758 .Pointer => return, 21759 .Fn => { 21760 const msg = msg: { 21761 const msg = try sema.errMsg( 21762 block, 21763 ty_src, 21764 "expected pointer, found '{}'", 21765 .{ty.fmt(mod)}, 21766 ); 21767 errdefer msg.destroy(sema.gpa); 21768 21769 try sema.errNote(block, ty_src, msg, "use '&' to obtain a function pointer", .{}); 21770 21771 break :msg msg; 21772 }; 21773 return sema.failWithOwnedErrorMsg(msg); 21774 }, 21775 .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return, 21776 else => {}, 21777 } 21778 return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)}); 21779 } 21780 21781 fn checkPtrType( 21782 sema: *Sema, 21783 block: *Block, 21784 ty_src: LazySrcLoc, 21785 ty: Type, 21786 ) CompileError!void { 21787 const mod = sema.mod; 21788 switch (ty.zigTypeTag(mod)) { 21789 .Pointer => return, 21790 .Fn => { 21791 const msg = msg: { 21792 const msg = try sema.errMsg( 21793 block, 21794 ty_src, 21795 "expected pointer type, found '{}'", 21796 .{ty.fmt(mod)}, 21797 ); 21798 errdefer msg.destroy(sema.gpa); 21799 21800 try sema.errNote(block, ty_src, msg, "use '*const ' to make a function pointer type", .{}); 21801 21802 break :msg msg; 21803 }; 21804 return sema.failWithOwnedErrorMsg(msg); 21805 }, 21806 .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return, 21807 else => {}, 21808 } 21809 return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)}); 21810 } 21811 21812 fn checkVectorElemType( 21813 sema: *Sema, 21814 block: *Block, 21815 ty_src: LazySrcLoc, 21816 ty: Type, 21817 ) CompileError!void { 21818 const mod = sema.mod; 21819 switch (ty.zigTypeTag(mod)) { 21820 .Int, .Float, .Bool => return, 21821 else => if (ty.isPtrAtRuntime(mod)) return, 21822 } 21823 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)}); 21824 } 21825 21826 fn checkFloatType( 21827 sema: *Sema, 21828 block: *Block, 21829 ty_src: LazySrcLoc, 21830 ty: Type, 21831 ) CompileError!void { 21832 const mod = sema.mod; 21833 switch (ty.zigTypeTag(mod)) { 21834 .ComptimeInt, .ComptimeFloat, .Float => {}, 21835 else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(mod)}), 21836 } 21837 } 21838 21839 fn checkNumericType( 21840 sema: *Sema, 21841 block: *Block, 21842 ty_src: LazySrcLoc, 21843 ty: Type, 21844 ) CompileError!void { 21845 const mod = sema.mod; 21846 switch (ty.zigTypeTag(mod)) { 21847 .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, 21848 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) { 21849 .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, 21850 else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}), 21851 }, 21852 else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(mod)}), 21853 } 21854 } 21855 21856 /// Returns the casted pointer. 21857 fn checkAtomicPtrOperand( 21858 sema: *Sema, 21859 block: *Block, 21860 elem_ty: Type, 21861 elem_ty_src: LazySrcLoc, 21862 ptr: Air.Inst.Ref, 21863 ptr_src: LazySrcLoc, 21864 ptr_const: bool, 21865 ) CompileError!Air.Inst.Ref { 21866 const mod = sema.mod; 21867 var diag: Module.AtomicPtrAlignmentDiagnostics = .{}; 21868 const alignment = mod.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) { 21869 error.OutOfMemory => return error.OutOfMemory, 21870 error.FloatTooBig => return sema.fail( 21871 block, 21872 elem_ty_src, 21873 "expected {d}-bit float type or smaller; found {d}-bit float type", 21874 .{ diag.max_bits, diag.bits }, 21875 ), 21876 error.IntTooBig => return sema.fail( 21877 block, 21878 elem_ty_src, 21879 "expected {d}-bit integer type or smaller; found {d}-bit integer type", 21880 .{ diag.max_bits, diag.bits }, 21881 ), 21882 error.BadType => return sema.fail( 21883 block, 21884 elem_ty_src, 21885 "expected bool, integer, float, enum, or pointer type; found '{}'", 21886 .{elem_ty.fmt(mod)}, 21887 ), 21888 }; 21889 21890 var wanted_ptr_data: InternPool.Key.PtrType = .{ 21891 .child = elem_ty.toIntern(), 21892 .flags = .{ 21893 .alignment = alignment, 21894 .is_const = ptr_const, 21895 }, 21896 }; 21897 21898 const ptr_ty = sema.typeOf(ptr); 21899 const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison(mod)) { 21900 .Pointer => ptr_ty.ptrInfo(mod), 21901 else => { 21902 const wanted_ptr_ty = try mod.ptrType(wanted_ptr_data); 21903 _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 21904 unreachable; 21905 }, 21906 }; 21907 21908 wanted_ptr_data.flags.address_space = ptr_data.flags.address_space; 21909 wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero; 21910 wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile; 21911 21912 const wanted_ptr_ty = try mod.ptrType(wanted_ptr_data); 21913 const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 21914 21915 return casted_ptr; 21916 } 21917 21918 fn checkPtrIsNotComptimeMutable( 21919 sema: *Sema, 21920 block: *Block, 21921 ptr_val: Value, 21922 ptr_src: LazySrcLoc, 21923 operand_src: LazySrcLoc, 21924 ) CompileError!void { 21925 _ = operand_src; 21926 if (ptr_val.isComptimeMutablePtr(sema.mod)) { 21927 return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); 21928 } 21929 } 21930 21931 fn checkComptimeVarStore( 21932 sema: *Sema, 21933 block: *Block, 21934 src: LazySrcLoc, 21935 decl_ref_mut: InternPool.Key.Ptr.Addr.MutDecl, 21936 ) CompileError!void { 21937 if (@intFromEnum(decl_ref_mut.runtime_index) < @intFromEnum(block.runtime_index)) { 21938 if (block.runtime_cond) |cond_src| { 21939 const msg = msg: { 21940 const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{}); 21941 errdefer msg.destroy(sema.gpa); 21942 try sema.errNote(block, cond_src, msg, "runtime condition here", .{}); 21943 break :msg msg; 21944 }; 21945 return sema.failWithOwnedErrorMsg(msg); 21946 } 21947 if (block.runtime_loop) |loop_src| { 21948 const msg = msg: { 21949 const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{}); 21950 errdefer msg.destroy(sema.gpa); 21951 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{}); 21952 break :msg msg; 21953 }; 21954 return sema.failWithOwnedErrorMsg(msg); 21955 } 21956 unreachable; 21957 } 21958 } 21959 21960 fn checkIntOrVector( 21961 sema: *Sema, 21962 block: *Block, 21963 operand: Air.Inst.Ref, 21964 operand_src: LazySrcLoc, 21965 ) CompileError!Type { 21966 const mod = sema.mod; 21967 const operand_ty = sema.typeOf(operand); 21968 switch (try operand_ty.zigTypeTagOrPoison(mod)) { 21969 .Int => return operand_ty, 21970 .Vector => { 21971 const elem_ty = operand_ty.childType(mod); 21972 switch (try elem_ty.zigTypeTagOrPoison(mod)) { 21973 .Int => return elem_ty, 21974 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ 21975 elem_ty.fmt(mod), 21976 }), 21977 } 21978 }, 21979 else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ 21980 operand_ty.fmt(mod), 21981 }), 21982 } 21983 } 21984 21985 fn checkIntOrVectorAllowComptime( 21986 sema: *Sema, 21987 block: *Block, 21988 operand_ty: Type, 21989 operand_src: LazySrcLoc, 21990 ) CompileError!Type { 21991 const mod = sema.mod; 21992 switch (try operand_ty.zigTypeTagOrPoison(mod)) { 21993 .Int, .ComptimeInt => return operand_ty, 21994 .Vector => { 21995 const elem_ty = operand_ty.childType(mod); 21996 switch (try elem_ty.zigTypeTagOrPoison(mod)) { 21997 .Int, .ComptimeInt => return elem_ty, 21998 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ 21999 elem_ty.fmt(mod), 22000 }), 22001 } 22002 }, 22003 else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ 22004 operand_ty.fmt(mod), 22005 }), 22006 } 22007 } 22008 22009 fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 22010 const mod = sema.mod; 22011 switch (ty.zigTypeTag(mod)) { 22012 .ErrorSet => return, 22013 else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}), 22014 } 22015 } 22016 22017 const SimdBinOp = struct { 22018 len: ?usize, 22019 /// Coerced to `result_ty`. 22020 lhs: Air.Inst.Ref, 22021 /// Coerced to `result_ty`. 22022 rhs: Air.Inst.Ref, 22023 lhs_val: ?Value, 22024 rhs_val: ?Value, 22025 /// Only different than `scalar_ty` when it is a vector operation. 22026 result_ty: Type, 22027 scalar_ty: Type, 22028 }; 22029 22030 fn checkSimdBinOp( 22031 sema: *Sema, 22032 block: *Block, 22033 src: LazySrcLoc, 22034 uncasted_lhs: Air.Inst.Ref, 22035 uncasted_rhs: Air.Inst.Ref, 22036 lhs_src: LazySrcLoc, 22037 rhs_src: LazySrcLoc, 22038 ) CompileError!SimdBinOp { 22039 const mod = sema.mod; 22040 const lhs_ty = sema.typeOf(uncasted_lhs); 22041 const rhs_ty = sema.typeOf(uncasted_rhs); 22042 22043 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 22044 var vec_len: ?usize = if (lhs_ty.zigTypeTag(mod) == .Vector) lhs_ty.vectorLen(mod) else null; 22045 const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ 22046 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 22047 }); 22048 const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); 22049 const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); 22050 22051 return SimdBinOp{ 22052 .len = vec_len, 22053 .lhs = lhs, 22054 .rhs = rhs, 22055 .lhs_val = try sema.resolveMaybeUndefVal(lhs), 22056 .rhs_val = try sema.resolveMaybeUndefVal(rhs), 22057 .result_ty = result_ty, 22058 .scalar_ty = result_ty.scalarType(mod), 22059 }; 22060 } 22061 22062 fn checkVectorizableBinaryOperands( 22063 sema: *Sema, 22064 block: *Block, 22065 src: LazySrcLoc, 22066 lhs_ty: Type, 22067 rhs_ty: Type, 22068 lhs_src: LazySrcLoc, 22069 rhs_src: LazySrcLoc, 22070 ) CompileError!void { 22071 const mod = sema.mod; 22072 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 22073 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 22074 if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return; 22075 22076 const lhs_is_vector = switch (lhs_zig_ty_tag) { 22077 .Vector, .Array => true, 22078 else => false, 22079 }; 22080 const rhs_is_vector = switch (rhs_zig_ty_tag) { 22081 .Vector, .Array => true, 22082 else => false, 22083 }; 22084 22085 if (lhs_is_vector and rhs_is_vector) { 22086 const lhs_len = lhs_ty.arrayLen(mod); 22087 const rhs_len = rhs_ty.arrayLen(mod); 22088 if (lhs_len != rhs_len) { 22089 const msg = msg: { 22090 const msg = try sema.errMsg(block, src, "vector length mismatch", .{}); 22091 errdefer msg.destroy(sema.gpa); 22092 try sema.errNote(block, lhs_src, msg, "length {d} here", .{lhs_len}); 22093 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len}); 22094 break :msg msg; 22095 }; 22096 return sema.failWithOwnedErrorMsg(msg); 22097 } 22098 } else { 22099 const msg = msg: { 22100 const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{ 22101 lhs_ty.fmt(mod), rhs_ty.fmt(mod), 22102 }); 22103 errdefer msg.destroy(sema.gpa); 22104 if (lhs_is_vector) { 22105 try sema.errNote(block, lhs_src, msg, "vector here", .{}); 22106 try sema.errNote(block, rhs_src, msg, "scalar here", .{}); 22107 } else { 22108 try sema.errNote(block, lhs_src, msg, "scalar here", .{}); 22109 try sema.errNote(block, rhs_src, msg, "vector here", .{}); 22110 } 22111 break :msg msg; 22112 }; 22113 return sema.failWithOwnedErrorMsg(msg); 22114 } 22115 } 22116 22117 fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc { 22118 if (base_src == .unneeded) return .unneeded; 22119 const mod = sema.mod; 22120 return mod.optionsSrc(mod.declPtr(block.src_decl), base_src, wanted); 22121 } 22122 22123 fn resolveExportOptions( 22124 sema: *Sema, 22125 block: *Block, 22126 src: LazySrcLoc, 22127 zir_ref: Zir.Inst.Ref, 22128 ) CompileError!Module.Export.Options { 22129 const mod = sema.mod; 22130 const gpa = sema.gpa; 22131 const ip = &mod.intern_pool; 22132 const export_options_ty = try sema.getBuiltinType("ExportOptions"); 22133 const air_ref = try sema.resolveInst(zir_ref); 22134 const options = try sema.coerce(block, export_options_ty, air_ref, src); 22135 22136 const name_src = sema.maybeOptionsSrc(block, src, "name"); 22137 const linkage_src = sema.maybeOptionsSrc(block, src, "linkage"); 22138 const section_src = sema.maybeOptionsSrc(block, src, "section"); 22139 const visibility_src = sema.maybeOptionsSrc(block, src, "visibility"); 22140 22141 const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); 22142 const name_val = try sema.resolveConstValue(block, name_src, name_operand, "name of exported value must be comptime-known"); 22143 const name_ty = Type.slice_const_u8; 22144 const name = try name_val.toAllocatedBytes(name_ty, sema.arena, mod); 22145 22146 const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); 22147 const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_operand, "linkage of exported value must be comptime-known"); 22148 const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); 22149 22150 const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section"), section_src); 22151 const section_opt_val = try sema.resolveConstValue(block, section_src, section_operand, "linksection of exported value must be comptime-known"); 22152 const section_ty = Type.slice_const_u8; 22153 const section = if (section_opt_val.optionalValue(mod)) |section_val| 22154 try section_val.toAllocatedBytes(section_ty, sema.arena, mod) 22155 else 22156 null; 22157 22158 const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility"), visibility_src); 22159 const visibility_val = try sema.resolveConstValue(block, visibility_src, visibility_operand, "visibility of exported value must be comptime-known"); 22160 const visibility = mod.toEnum(std.builtin.SymbolVisibility, visibility_val); 22161 22162 if (name.len < 1) { 22163 return sema.fail(block, name_src, "exported symbol name cannot be empty", .{}); 22164 } 22165 22166 if (visibility != .default and linkage == .Internal) { 22167 return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{ 22168 name, @tagName(visibility), 22169 }); 22170 } 22171 22172 return .{ 22173 .name = try ip.getOrPutString(gpa, name), 22174 .linkage = linkage, 22175 .section = try ip.getOrPutStringOpt(gpa, section), 22176 .visibility = visibility, 22177 }; 22178 } 22179 22180 fn resolveBuiltinEnum( 22181 sema: *Sema, 22182 block: *Block, 22183 src: LazySrcLoc, 22184 zir_ref: Zir.Inst.Ref, 22185 comptime name: []const u8, 22186 reason: []const u8, 22187 ) CompileError!@field(std.builtin, name) { 22188 const mod = sema.mod; 22189 const ty = try sema.getBuiltinType(name); 22190 const air_ref = try sema.resolveInst(zir_ref); 22191 const coerced = try sema.coerce(block, ty, air_ref, src); 22192 const val = try sema.resolveConstValue(block, src, coerced, reason); 22193 return mod.toEnum(@field(std.builtin, name), val); 22194 } 22195 22196 fn resolveAtomicOrder( 22197 sema: *Sema, 22198 block: *Block, 22199 src: LazySrcLoc, 22200 zir_ref: Zir.Inst.Ref, 22201 reason: []const u8, 22202 ) CompileError!std.builtin.AtomicOrder { 22203 return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicOrder", reason); 22204 } 22205 22206 fn resolveAtomicRmwOp( 22207 sema: *Sema, 22208 block: *Block, 22209 src: LazySrcLoc, 22210 zir_ref: Zir.Inst.Ref, 22211 ) CompileError!std.builtin.AtomicRmwOp { 22212 return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicRmwOp", "@atomicRmW operation must be comptime-known"); 22213 } 22214 22215 fn zirCmpxchg( 22216 sema: *Sema, 22217 block: *Block, 22218 extended: Zir.Inst.Extended.InstData, 22219 ) CompileError!Air.Inst.Ref { 22220 const mod = sema.mod; 22221 const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data; 22222 const air_tag: Air.Inst.Tag = switch (extended.small) { 22223 0 => .cmpxchg_weak, 22224 1 => .cmpxchg_strong, 22225 else => unreachable, 22226 }; 22227 const src = LazySrcLoc.nodeOffset(extra.node); 22228 // zig fmt: off 22229 const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 22230 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 22231 const expected_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; 22232 const new_value_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node }; 22233 const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = extra.node }; 22234 const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = extra.node }; 22235 // zig fmt: on 22236 const expected_value = try sema.resolveInst(extra.expected_value); 22237 const elem_ty = sema.typeOf(expected_value); 22238 if (elem_ty.zigTypeTag(mod) == .Float) { 22239 return sema.fail( 22240 block, 22241 elem_ty_src, 22242 "expected bool, integer, enum, or pointer type; found '{}'", 22243 .{elem_ty.fmt(mod)}, 22244 ); 22245 } 22246 const uncasted_ptr = try sema.resolveInst(extra.ptr); 22247 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 22248 const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); 22249 const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, "atomic order of cmpxchg success must be comptime-known"); 22250 const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, "atomic order of cmpxchg failure must be comptime-known"); 22251 22252 if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) { 22253 return sema.fail(block, success_order_src, "success atomic ordering must be Monotonic or stricter", .{}); 22254 } 22255 if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) { 22256 return sema.fail(block, failure_order_src, "failure atomic ordering must be Monotonic or stricter", .{}); 22257 } 22258 if (@intFromEnum(failure_order) > @intFromEnum(success_order)) { 22259 return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{}); 22260 } 22261 if (failure_order == .Release or failure_order == .AcqRel) { 22262 return sema.fail(block, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{}); 22263 } 22264 22265 const result_ty = try mod.optionalType(elem_ty.toIntern()); 22266 22267 // special case zero bit types 22268 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { 22269 return sema.addConstant((try mod.intern(.{ .opt = .{ 22270 .ty = result_ty.toIntern(), 22271 .val = .none, 22272 } })).toValue()); 22273 } 22274 22275 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 22276 if (try sema.resolveMaybeUndefVal(expected_value)) |expected_val| { 22277 if (try sema.resolveMaybeUndefVal(new_value)) |new_val| { 22278 if (expected_val.isUndef(mod) or new_val.isUndef(mod)) { 22279 // TODO: this should probably cause the memory stored at the pointer 22280 // to become undef as well 22281 return sema.addConstUndef(result_ty); 22282 } 22283 const ptr_ty = sema.typeOf(ptr); 22284 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 22285 const result_val = try mod.intern(.{ .opt = .{ 22286 .ty = result_ty.toIntern(), 22287 .val = if (stored_val.eql(expected_val, elem_ty, mod)) blk: { 22288 try sema.storePtr(block, src, ptr, new_value); 22289 break :blk .none; 22290 } else stored_val.toIntern(), 22291 } }); 22292 return sema.addConstant(result_val.toValue()); 22293 } else break :rs new_value_src; 22294 } else break :rs expected_src; 22295 } else ptr_src; 22296 22297 const flags: u32 = @as(u32, @intFromEnum(success_order)) | 22298 (@as(u32, @intFromEnum(failure_order)) << 3); 22299 22300 try sema.requireRuntimeBlock(block, src, runtime_src); 22301 return block.addInst(.{ 22302 .tag = air_tag, 22303 .data = .{ .ty_pl = .{ 22304 .ty = try sema.addType(result_ty), 22305 .payload = try sema.addExtra(Air.Cmpxchg{ 22306 .ptr = ptr, 22307 .expected_value = expected_value, 22308 .new_value = new_value, 22309 .flags = flags, 22310 }), 22311 } }, 22312 }); 22313 } 22314 22315 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22316 const mod = sema.mod; 22317 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22318 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22319 const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 22320 const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 22321 const len = @as(u32, @intCast(try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector splat destination length must be comptime-known"))); 22322 const scalar = try sema.resolveInst(extra.rhs); 22323 const scalar_ty = sema.typeOf(scalar); 22324 try sema.checkVectorElemType(block, scalar_src, scalar_ty); 22325 const vector_ty = try mod.vectorType(.{ 22326 .len = len, 22327 .child = scalar_ty.toIntern(), 22328 }); 22329 if (try sema.resolveMaybeUndefVal(scalar)) |scalar_val| { 22330 if (scalar_val.isUndef(mod)) return sema.addConstUndef(vector_ty); 22331 return sema.addConstant(try sema.splat(vector_ty, scalar_val)); 22332 } 22333 22334 try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src); 22335 return block.addTyOp(.splat, vector_ty, scalar); 22336 } 22337 22338 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22339 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22340 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22341 const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22342 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22343 const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", "@reduce operation must be comptime-known"); 22344 const operand = try sema.resolveInst(extra.rhs); 22345 const operand_ty = sema.typeOf(operand); 22346 const mod = sema.mod; 22347 22348 if (operand_ty.zigTypeTag(mod) != .Vector) { 22349 return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(mod)}); 22350 } 22351 22352 const scalar_ty = operand_ty.childType(mod); 22353 22354 // Type-check depending on operation. 22355 switch (operation) { 22356 .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(mod)) { 22357 .Int, .Bool => {}, 22358 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{ 22359 @tagName(operation), operand_ty.fmt(mod), 22360 }), 22361 }, 22362 .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(mod)) { 22363 .Int, .Float => {}, 22364 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{ 22365 @tagName(operation), operand_ty.fmt(mod), 22366 }), 22367 }, 22368 } 22369 22370 const vec_len = operand_ty.vectorLen(mod); 22371 if (vec_len == 0) { 22372 // TODO re-evaluate if we should introduce a "neutral value" for some operations, 22373 // e.g. zero for add and one for mul. 22374 return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{}); 22375 } 22376 22377 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { 22378 if (operand_val.isUndef(mod)) return sema.addConstUndef(scalar_ty); 22379 22380 var accum: Value = try operand_val.elemValue(mod, 0); 22381 var i: u32 = 1; 22382 while (i < vec_len) : (i += 1) { 22383 const elem_val = try operand_val.elemValue(mod, i); 22384 switch (operation) { 22385 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, mod), 22386 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, mod), 22387 .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, mod), 22388 .Min => accum = accum.numberMin(elem_val, mod), 22389 .Max => accum = accum.numberMax(elem_val, mod), 22390 .Add => accum = try sema.numberAddWrapScalar(accum, elem_val, scalar_ty), 22391 .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, mod), 22392 } 22393 } 22394 return sema.addConstant(accum); 22395 } 22396 22397 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 22398 return block.addInst(.{ 22399 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 22400 .data = .{ .reduce = .{ 22401 .operand = operand, 22402 .operation = operation, 22403 } }, 22404 }); 22405 } 22406 22407 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22408 const mod = sema.mod; 22409 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22410 const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; 22411 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22412 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 22413 22414 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 22415 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 22416 var a = try sema.resolveInst(extra.a); 22417 var b = try sema.resolveInst(extra.b); 22418 var mask = try sema.resolveInst(extra.mask); 22419 var mask_ty = sema.typeOf(mask); 22420 22421 const mask_len = switch (sema.typeOf(mask).zigTypeTag(mod)) { 22422 .Array, .Vector => sema.typeOf(mask).arrayLen(mod), 22423 else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}), 22424 }; 22425 mask_ty = try mod.vectorType(.{ 22426 .len = @as(u32, @intCast(mask_len)), 22427 .child = .i32_type, 22428 }); 22429 mask = try sema.coerce(block, mask_ty, mask, mask_src); 22430 const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask, "shuffle mask must be comptime-known"); 22431 return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @as(u32, @intCast(mask_len))); 22432 } 22433 22434 fn analyzeShuffle( 22435 sema: *Sema, 22436 block: *Block, 22437 src_node: i32, 22438 elem_ty: Type, 22439 a_arg: Air.Inst.Ref, 22440 b_arg: Air.Inst.Ref, 22441 mask: Value, 22442 mask_len: u32, 22443 ) CompileError!Air.Inst.Ref { 22444 const mod = sema.mod; 22445 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node }; 22446 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node }; 22447 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node }; 22448 var a = a_arg; 22449 var b = b_arg; 22450 22451 const res_ty = try mod.vectorType(.{ 22452 .len = mask_len, 22453 .child = elem_ty.toIntern(), 22454 }); 22455 22456 var maybe_a_len = switch (sema.typeOf(a).zigTypeTag(mod)) { 22457 .Array, .Vector => sema.typeOf(a).arrayLen(mod), 22458 .Undefined => null, 22459 else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{ 22460 elem_ty.fmt(sema.mod), 22461 sema.typeOf(a).fmt(sema.mod), 22462 }), 22463 }; 22464 var maybe_b_len = switch (sema.typeOf(b).zigTypeTag(mod)) { 22465 .Array, .Vector => sema.typeOf(b).arrayLen(mod), 22466 .Undefined => null, 22467 else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{ 22468 elem_ty.fmt(sema.mod), 22469 sema.typeOf(b).fmt(sema.mod), 22470 }), 22471 }; 22472 if (maybe_a_len == null and maybe_b_len == null) { 22473 return sema.addConstUndef(res_ty); 22474 } 22475 const a_len = @as(u32, @intCast(maybe_a_len orelse maybe_b_len.?)); 22476 const b_len = @as(u32, @intCast(maybe_b_len orelse a_len)); 22477 22478 const a_ty = try mod.vectorType(.{ 22479 .len = a_len, 22480 .child = elem_ty.toIntern(), 22481 }); 22482 const b_ty = try mod.vectorType(.{ 22483 .len = b_len, 22484 .child = elem_ty.toIntern(), 22485 }); 22486 22487 if (maybe_a_len == null) a = try sema.addConstUndef(a_ty) else a = try sema.coerce(block, a_ty, a, a_src); 22488 if (maybe_b_len == null) b = try sema.addConstUndef(b_ty) else b = try sema.coerce(block, b_ty, b, b_src); 22489 22490 const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){ 22491 .{ a_len, a_src, a_ty }, 22492 .{ b_len, b_src, b_ty }, 22493 }; 22494 22495 for (0..@as(usize, @intCast(mask_len))) |i| { 22496 const elem = try mask.elemValue(sema.mod, i); 22497 if (elem.isUndef(mod)) continue; 22498 const int = elem.toSignedInt(mod); 22499 var unsigned: u32 = undefined; 22500 var chosen: u32 = undefined; 22501 if (int >= 0) { 22502 unsigned = @as(u32, @intCast(int)); 22503 chosen = 0; 22504 } else { 22505 unsigned = @as(u32, @intCast(~int)); 22506 chosen = 1; 22507 } 22508 if (unsigned >= operand_info[chosen][0]) { 22509 const msg = msg: { 22510 const msg = try sema.errMsg(block, mask_src, "mask index '{d}' has out-of-bounds selection", .{i}); 22511 errdefer msg.destroy(sema.gpa); 22512 22513 try sema.errNote(block, operand_info[chosen][1], msg, "selected index '{d}' out of bounds of '{}'", .{ 22514 unsigned, 22515 operand_info[chosen][2].fmt(sema.mod), 22516 }); 22517 22518 if (chosen == 0) { 22519 try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{}); 22520 } 22521 22522 break :msg msg; 22523 }; 22524 return sema.failWithOwnedErrorMsg(msg); 22525 } 22526 } 22527 22528 if (try sema.resolveMaybeUndefVal(a)) |a_val| { 22529 if (try sema.resolveMaybeUndefVal(b)) |b_val| { 22530 const values = try sema.arena.alloc(InternPool.Index, mask_len); 22531 for (values, 0..) |*value, i| { 22532 const mask_elem_val = try mask.elemValue(sema.mod, i); 22533 if (mask_elem_val.isUndef(mod)) { 22534 value.* = try mod.intern(.{ .undef = elem_ty.toIntern() }); 22535 continue; 22536 } 22537 const int = mask_elem_val.toSignedInt(mod); 22538 const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int)); 22539 values[i] = try (try (if (int >= 0) a_val else b_val).elemValue(mod, unsigned)).intern(elem_ty, mod); 22540 } 22541 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 22542 .ty = res_ty.toIntern(), 22543 .storage = .{ .elems = values }, 22544 } })).toValue()); 22545 } 22546 } 22547 22548 // All static analysis passed, and not comptime. 22549 // For runtime codegen, vectors a and b must be the same length. Here we 22550 // recursively @shuffle the smaller vector to append undefined elements 22551 // to it up to the length of the longer vector. This recursion terminates 22552 // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len. 22553 if (a_len != b_len) { 22554 const min_len = @min(a_len, b_len); 22555 const max_src = if (a_len > b_len) a_src else b_src; 22556 const max_len = try sema.usizeCast(block, max_src, @max(a_len, b_len)); 22557 22558 const expand_mask_values = try sema.arena.alloc(InternPool.Index, max_len); 22559 for (@as(usize, @intCast(0))..@as(usize, @intCast(min_len))) |i| { 22560 expand_mask_values[i] = (try mod.intValue(Type.comptime_int, i)).toIntern(); 22561 } 22562 for (@as(usize, @intCast(min_len))..@as(usize, @intCast(max_len))) |i| { 22563 expand_mask_values[i] = (try mod.intValue(Type.comptime_int, -1)).toIntern(); 22564 } 22565 const expand_mask = try mod.intern(.{ .aggregate = .{ 22566 .ty = (try mod.vectorType(.{ .len = @as(u32, @intCast(max_len)), .child = .comptime_int_type })).toIntern(), 22567 .storage = .{ .elems = expand_mask_values }, 22568 } }); 22569 22570 if (a_len < b_len) { 22571 const undef = try sema.addConstUndef(a_ty); 22572 a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask.toValue(), @as(u32, @intCast(max_len))); 22573 } else { 22574 const undef = try sema.addConstUndef(b_ty); 22575 b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask.toValue(), @as(u32, @intCast(max_len))); 22576 } 22577 } 22578 22579 return block.addInst(.{ 22580 .tag = .shuffle, 22581 .data = .{ .ty_pl = .{ 22582 .ty = try sema.addType(res_ty), 22583 .payload = try block.sema.addExtra(Air.Shuffle{ 22584 .a = a, 22585 .b = b, 22586 .mask = mask.toIntern(), 22587 .mask_len = mask_len, 22588 }), 22589 } }, 22590 }); 22591 } 22592 22593 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 22594 const mod = sema.mod; 22595 const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; 22596 22597 const src = LazySrcLoc.nodeOffset(extra.node); 22598 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 22599 const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 22600 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; 22601 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node }; 22602 22603 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 22604 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 22605 const pred_uncoerced = try sema.resolveInst(extra.pred); 22606 const pred_ty = sema.typeOf(pred_uncoerced); 22607 22608 const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison(mod)) { 22609 .Vector, .Array => pred_ty.arrayLen(mod), 22610 else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(mod)}), 22611 }; 22612 const vec_len = @as(u32, @intCast(try sema.usizeCast(block, pred_src, vec_len_u64))); 22613 22614 const bool_vec_ty = try mod.vectorType(.{ 22615 .len = vec_len, 22616 .child = .bool_type, 22617 }); 22618 const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); 22619 22620 const vec_ty = try mod.vectorType(.{ 22621 .len = vec_len, 22622 .child = elem_ty.toIntern(), 22623 }); 22624 const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src); 22625 const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src); 22626 22627 const maybe_pred = try sema.resolveMaybeUndefVal(pred); 22628 const maybe_a = try sema.resolveMaybeUndefVal(a); 22629 const maybe_b = try sema.resolveMaybeUndefVal(b); 22630 22631 const runtime_src = if (maybe_pred) |pred_val| rs: { 22632 if (pred_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22633 22634 if (maybe_a) |a_val| { 22635 if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22636 22637 if (maybe_b) |b_val| { 22638 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22639 22640 const elems = try sema.gpa.alloc(InternPool.Index, vec_len); 22641 for (elems, 0..) |*elem, i| { 22642 const pred_elem_val = try pred_val.elemValue(mod, i); 22643 const should_choose_a = pred_elem_val.toBool(); 22644 elem.* = try (try (if (should_choose_a) a_val else b_val).elemValue(mod, i)).intern(elem_ty, mod); 22645 } 22646 22647 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 22648 .ty = vec_ty.toIntern(), 22649 .storage = .{ .elems = elems }, 22650 } })).toValue()); 22651 } else { 22652 break :rs b_src; 22653 } 22654 } else { 22655 if (maybe_b) |b_val| { 22656 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22657 } 22658 break :rs a_src; 22659 } 22660 } else rs: { 22661 if (maybe_a) |a_val| { 22662 if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22663 } 22664 if (maybe_b) |b_val| { 22665 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22666 } 22667 break :rs pred_src; 22668 }; 22669 22670 try sema.requireRuntimeBlock(block, src, runtime_src); 22671 return block.addInst(.{ 22672 .tag = .select, 22673 .data = .{ .pl_op = .{ 22674 .operand = pred, 22675 .payload = try block.sema.addExtra(Air.Bin{ 22676 .lhs = a, 22677 .rhs = b, 22678 }), 22679 } }, 22680 }); 22681 } 22682 22683 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22684 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22685 const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; 22686 // zig fmt: off 22687 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22688 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22689 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22690 // zig fmt: on 22691 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 22692 const uncasted_ptr = try sema.resolveInst(extra.ptr); 22693 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); 22694 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicLoad must be comptime-known"); 22695 22696 switch (order) { 22697 .Release, .AcqRel => { 22698 return sema.fail( 22699 block, 22700 order_src, 22701 "@atomicLoad atomic ordering must not be Release or AcqRel", 22702 .{}, 22703 ); 22704 }, 22705 else => {}, 22706 } 22707 22708 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 22709 return sema.addConstant(val); 22710 } 22711 22712 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 22713 if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { 22714 return sema.addConstant(elem_val); 22715 } 22716 } 22717 22718 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); 22719 return block.addInst(.{ 22720 .tag = .atomic_load, 22721 .data = .{ .atomic_load = .{ 22722 .ptr = ptr, 22723 .order = order, 22724 } }, 22725 }); 22726 } 22727 22728 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22729 const mod = sema.mod; 22730 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22731 const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; 22732 const src = inst_data.src(); 22733 // zig fmt: off 22734 const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22735 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22736 const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22737 const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 22738 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; 22739 // zig fmt: on 22740 const operand = try sema.resolveInst(extra.operand); 22741 const elem_ty = sema.typeOf(operand); 22742 const uncasted_ptr = try sema.resolveInst(extra.ptr); 22743 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 22744 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); 22745 22746 switch (elem_ty.zigTypeTag(mod)) { 22747 .Enum => if (op != .Xchg) { 22748 return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); 22749 }, 22750 .Bool => if (op != .Xchg) { 22751 return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{}); 22752 }, 22753 .Float => switch (op) { 22754 .Xchg, .Add, .Sub, .Max, .Min => {}, 22755 else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}), 22756 }, 22757 else => {}, 22758 } 22759 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicRmW must be comptime-known"); 22760 22761 if (order == .Unordered) { 22762 return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be Unordered", .{}); 22763 } 22764 22765 // special case zero bit types 22766 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 22767 return sema.addConstant(val); 22768 } 22769 22770 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 22771 const maybe_operand_val = try sema.resolveMaybeUndefVal(operand); 22772 const operand_val = maybe_operand_val orelse { 22773 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); 22774 break :rs operand_src; 22775 }; 22776 if (ptr_val.isComptimeMutablePtr(mod)) { 22777 const ptr_ty = sema.typeOf(ptr); 22778 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 22779 const new_val = switch (op) { 22780 // zig fmt: off 22781 .Xchg => operand_val, 22782 .Add => try sema.numberAddWrapScalar(stored_val, operand_val, elem_ty), 22783 .Sub => try sema.numberSubWrapScalar(stored_val, operand_val, elem_ty), 22784 .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, mod), 22785 .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, mod), 22786 .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, mod), 22787 .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, mod), 22788 .Max => stored_val.numberMax (operand_val, mod), 22789 .Min => stored_val.numberMin (operand_val, mod), 22790 // zig fmt: on 22791 }; 22792 try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); 22793 return sema.addConstant(stored_val); 22794 } else break :rs ptr_src; 22795 } else ptr_src; 22796 22797 const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3); 22798 22799 try sema.requireRuntimeBlock(block, src, runtime_src); 22800 return block.addInst(.{ 22801 .tag = .atomic_rmw, 22802 .data = .{ .pl_op = .{ 22803 .operand = ptr, 22804 .payload = try sema.addExtra(Air.AtomicRmw{ 22805 .operand = operand, 22806 .flags = flags, 22807 }), 22808 } }, 22809 }); 22810 } 22811 22812 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 22813 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22814 const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; 22815 const src = inst_data.src(); 22816 // zig fmt: off 22817 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22818 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22819 const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22820 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 22821 // zig fmt: on 22822 const operand = try sema.resolveInst(extra.operand); 22823 const elem_ty = sema.typeOf(operand); 22824 const uncasted_ptr = try sema.resolveInst(extra.ptr); 22825 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 22826 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicStore must be comptime-known"); 22827 22828 const air_tag: Air.Inst.Tag = switch (order) { 22829 .Acquire, .AcqRel => { 22830 return sema.fail( 22831 block, 22832 order_src, 22833 "@atomicStore atomic ordering must not be Acquire or AcqRel", 22834 .{}, 22835 ); 22836 }, 22837 .Unordered => .atomic_store_unordered, 22838 .Monotonic => .atomic_store_monotonic, 22839 .Release => .atomic_store_release, 22840 .SeqCst => .atomic_store_seq_cst, 22841 }; 22842 22843 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 22844 } 22845 22846 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22847 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22848 const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data; 22849 const src = inst_data.src(); 22850 22851 const mulend1_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22852 const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22853 const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 22854 22855 const addend = try sema.resolveInst(extra.addend); 22856 const ty = sema.typeOf(addend); 22857 const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src); 22858 const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src); 22859 22860 const maybe_mulend1 = try sema.resolveMaybeUndefVal(mulend1); 22861 const maybe_mulend2 = try sema.resolveMaybeUndefVal(mulend2); 22862 const maybe_addend = try sema.resolveMaybeUndefVal(addend); 22863 const mod = sema.mod; 22864 22865 switch (ty.scalarType(mod).zigTypeTag(mod)) { 22866 .ComptimeFloat, .Float => {}, 22867 else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}), 22868 } 22869 22870 const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { 22871 if (maybe_mulend2) |mulend2_val| { 22872 if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty); 22873 22874 if (maybe_addend) |addend_val| { 22875 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); 22876 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, sema.mod); 22877 return sema.addConstant(result_val); 22878 } else { 22879 break :rs addend_src; 22880 } 22881 } else { 22882 if (maybe_addend) |addend_val| { 22883 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); 22884 } 22885 break :rs mulend2_src; 22886 } 22887 } else rs: { 22888 if (maybe_mulend2) |mulend2_val| { 22889 if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty); 22890 } 22891 if (maybe_addend) |addend_val| { 22892 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); 22893 } 22894 break :rs mulend1_src; 22895 }; 22896 22897 try sema.requireRuntimeBlock(block, src, runtime_src); 22898 return block.addInst(.{ 22899 .tag = .mul_add, 22900 .data = .{ .pl_op = .{ 22901 .operand = addend, 22902 .payload = try sema.addExtra(Air.Bin{ 22903 .lhs = mulend1, 22904 .rhs = mulend2, 22905 }), 22906 } }, 22907 }); 22908 } 22909 22910 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22911 const tracy = trace(@src()); 22912 defer tracy.end(); 22913 22914 const mod = sema.mod; 22915 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22916 const modifier_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22917 const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22918 const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22919 const call_src = inst_data.src(); 22920 22921 const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; 22922 var func = try sema.resolveInst(extra.callee); 22923 22924 const modifier_ty = try sema.getBuiltinType("CallModifier"); 22925 const air_ref = try sema.resolveInst(extra.modifier); 22926 const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); 22927 const modifier_val = try sema.resolveConstValue(block, modifier_src, modifier_ref, "call modifier must be comptime-known"); 22928 var modifier = mod.toEnum(std.builtin.CallModifier, modifier_val); 22929 switch (modifier) { 22930 // These can be upgraded to comptime or nosuspend calls. 22931 .auto, .never_tail, .no_async => { 22932 if (block.is_comptime) { 22933 if (modifier == .never_tail) { 22934 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{}); 22935 } 22936 modifier = .compile_time; 22937 } else if (extra.flags.is_nosuspend) { 22938 modifier = .no_async; 22939 } 22940 }, 22941 // These can be upgraded to comptime. nosuspend bit can be safely ignored. 22942 .always_inline, .compile_time => { 22943 _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { 22944 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)}); 22945 }; 22946 22947 if (block.is_comptime) { 22948 modifier = .compile_time; 22949 } 22950 }, 22951 .always_tail => { 22952 if (block.is_comptime) { 22953 modifier = .compile_time; 22954 } 22955 }, 22956 .async_kw => { 22957 if (extra.flags.is_nosuspend) { 22958 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{}); 22959 } 22960 if (block.is_comptime) { 22961 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{}); 22962 } 22963 }, 22964 .never_inline => { 22965 if (block.is_comptime) { 22966 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{}); 22967 } 22968 }, 22969 } 22970 22971 const args = try sema.resolveInst(extra.args); 22972 22973 const args_ty = sema.typeOf(args); 22974 if (!args_ty.isTuple(mod) and args_ty.toIntern() != .empty_struct_type) { 22975 return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)}); 22976 } 22977 22978 var resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(mod)); 22979 for (resolved_args, 0..) |*resolved, i| { 22980 resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @as(u32, @intCast(i)), args_ty); 22981 } 22982 22983 const callee_ty = sema.typeOf(func); 22984 const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false); 22985 const ensure_result_used = extra.flags.ensure_result_used; 22986 return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null); 22987 } 22988 22989 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22990 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22991 const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data; 22992 const src = inst_data.src(); 22993 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22994 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22995 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22996 22997 const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type); 22998 const field_name = try sema.resolveConstStringIntern(block, name_src, extra.field_name, "field name must be comptime-known"); 22999 const field_ptr = try sema.resolveInst(extra.field_ptr); 23000 const field_ptr_ty = sema.typeOf(field_ptr); 23001 const mod = sema.mod; 23002 const ip = &mod.intern_pool; 23003 23004 if (parent_ty.zigTypeTag(mod) != .Struct and parent_ty.zigTypeTag(mod) != .Union) { 23005 return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)}); 23006 } 23007 try sema.resolveTypeLayout(parent_ty); 23008 23009 const field_index = switch (parent_ty.zigTypeTag(mod)) { 23010 .Struct => blk: { 23011 if (parent_ty.isTuple(mod)) { 23012 if (ip.stringEqlSlice(field_name, "len")) { 23013 return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); 23014 } 23015 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src); 23016 } else { 23017 break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src); 23018 } 23019 }, 23020 .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src), 23021 else => unreachable, 23022 }; 23023 23024 if (parent_ty.zigTypeTag(mod) == .Struct and parent_ty.structFieldIsComptime(field_index, mod)) { 23025 return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{}); 23026 } 23027 23028 try sema.checkPtrOperand(block, ptr_src, field_ptr_ty); 23029 const field_ptr_ty_info = field_ptr_ty.ptrInfo(mod); 23030 23031 var ptr_ty_data: InternPool.Key.PtrType = .{ 23032 .child = parent_ty.structFieldType(field_index, mod).toIntern(), 23033 .flags = .{ 23034 .address_space = field_ptr_ty_info.flags.address_space, 23035 .is_const = field_ptr_ty_info.flags.is_const, 23036 }, 23037 }; 23038 23039 if (parent_ty.containerLayout(mod) == .Packed) { 23040 return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{}); 23041 } else { 23042 ptr_ty_data.flags.alignment = blk: { 23043 if (mod.typeToStruct(parent_ty)) |struct_obj| { 23044 break :blk struct_obj.fields.values()[field_index].abi_align; 23045 } else if (mod.typeToUnion(parent_ty)) |union_obj| { 23046 break :blk union_obj.fields.values()[field_index].abi_align; 23047 } else { 23048 break :blk .none; 23049 } 23050 }; 23051 } 23052 23053 const actual_field_ptr_ty = try mod.ptrType(ptr_ty_data); 23054 const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src); 23055 23056 ptr_ty_data.child = parent_ty.toIntern(); 23057 const result_ptr = try mod.ptrType(ptr_ty_data); 23058 23059 if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { 23060 const field = switch (ip.indexToKey(field_ptr_val.toIntern())) { 23061 .ptr => |ptr| switch (ptr.addr) { 23062 .field => |field| field, 23063 else => null, 23064 }, 23065 else => null, 23066 } orelse return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{}); 23067 23068 if (field.index != field_index) { 23069 const msg = msg: { 23070 const msg = try sema.errMsg( 23071 block, 23072 src, 23073 "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'", 23074 .{ 23075 field_name.fmt(ip), 23076 field_index, 23077 field.index, 23078 parent_ty.fmt(sema.mod), 23079 }, 23080 ); 23081 errdefer msg.destroy(sema.gpa); 23082 try sema.addDeclaredHereNote(msg, parent_ty); 23083 break :msg msg; 23084 }; 23085 return sema.failWithOwnedErrorMsg(msg); 23086 } 23087 return sema.addConstant(field.base.toValue()); 23088 } 23089 23090 try sema.requireRuntimeBlock(block, src, ptr_src); 23091 try sema.queueFullTypeResolution(result_ptr); 23092 return block.addInst(.{ 23093 .tag = .field_parent_ptr, 23094 .data = .{ .ty_pl = .{ 23095 .ty = try sema.addType(result_ptr), 23096 .payload = try block.sema.addExtra(Air.FieldParentPtr{ 23097 .field_ptr = casted_field_ptr, 23098 .field_index = @as(u32, @intCast(field_index)), 23099 }), 23100 } }, 23101 }); 23102 } 23103 23104 fn zirMinMax( 23105 sema: *Sema, 23106 block: *Block, 23107 inst: Zir.Inst.Index, 23108 comptime air_tag: Air.Inst.Tag, 23109 ) CompileError!Air.Inst.Ref { 23110 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 23111 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23112 const src = inst_data.src(); 23113 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23114 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23115 const lhs = try sema.resolveInst(extra.lhs); 23116 const rhs = try sema.resolveInst(extra.rhs); 23117 try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs)); 23118 try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs)); 23119 return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src }); 23120 } 23121 23122 fn zirMinMaxMulti( 23123 sema: *Sema, 23124 block: *Block, 23125 extended: Zir.Inst.Extended.InstData, 23126 comptime air_tag: Air.Inst.Tag, 23127 ) CompileError!Air.Inst.Ref { 23128 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 23129 const src_node = extra.data.src_node; 23130 const src = LazySrcLoc.nodeOffset(src_node); 23131 const operands = sema.code.refSlice(extra.end, extended.small); 23132 23133 const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 23134 const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len); 23135 23136 for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| { 23137 op_src.* = switch (i) { 23138 0 => .{ .node_offset_builtin_call_arg0 = src_node }, 23139 1 => .{ .node_offset_builtin_call_arg1 = src_node }, 23140 2 => .{ .node_offset_builtin_call_arg2 = src_node }, 23141 3 => .{ .node_offset_builtin_call_arg3 = src_node }, 23142 4 => .{ .node_offset_builtin_call_arg4 = src_node }, 23143 5 => .{ .node_offset_builtin_call_arg5 = src_node }, 23144 else => src, // TODO: better source location 23145 }; 23146 air_ref.* = try sema.resolveInst(zir_ref); 23147 try sema.checkNumericType(block, op_src.*, sema.typeOf(air_ref.*)); 23148 } 23149 23150 return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs); 23151 } 23152 23153 fn analyzeMinMax( 23154 sema: *Sema, 23155 block: *Block, 23156 src: LazySrcLoc, 23157 comptime air_tag: Air.Inst.Tag, 23158 operands: []const Air.Inst.Ref, 23159 operand_srcs: []const LazySrcLoc, 23160 ) CompileError!Air.Inst.Ref { 23161 assert(operands.len == operand_srcs.len); 23162 assert(operands.len > 0); 23163 const mod = sema.mod; 23164 23165 if (operands.len == 1) return operands[0]; 23166 23167 const opFunc = switch (air_tag) { 23168 .min => Value.numberMin, 23169 .max => Value.numberMax, 23170 else => @compileError("unreachable"), 23171 }; 23172 23173 // The set of runtime-known operands. Set up in the loop below. 23174 var runtime_known = try std.DynamicBitSet.initFull(sema.arena, operands.len); 23175 // The current minmax value - initially this will always be comptime-known, then we'll add 23176 // runtime values into the mix later. 23177 var cur_minmax: ?Air.Inst.Ref = null; 23178 var cur_minmax_src: LazySrcLoc = undefined; // defined if cur_minmax not null 23179 // The current known scalar bounds of the value. 23180 var bounds_status: enum { 23181 unknown, // We've only seen undef comptime_ints so far, so do not know the bounds. 23182 defined, // We've seen only integers, so the bounds are defined. 23183 non_integral, // There are floats in the mix, so the bounds aren't defined. 23184 } = .unknown; 23185 var cur_min_scalar: Value = undefined; 23186 var cur_max_scalar: Value = undefined; 23187 23188 // First, find all comptime-known arguments, and get their min/max 23189 23190 for (operands, operand_srcs, 0..) |operand, operand_src, operand_idx| { 23191 // Resolve the value now to avoid redundant calls to `checkSimdBinOp` - we'll have to call 23192 // it in the runtime path anyway since the result type may have been refined 23193 const unresolved_uncoerced_val = try sema.resolveMaybeUndefVal(operand) orelse continue; 23194 const uncoerced_val = try sema.resolveLazyValue(unresolved_uncoerced_val); 23195 23196 runtime_known.unset(operand_idx); 23197 23198 switch (bounds_status) { 23199 .unknown, .defined => refine_bounds: { 23200 const ty = sema.typeOf(operand); 23201 if (!ty.scalarType(mod).isInt(mod) and !ty.scalarType(mod).eql(Type.comptime_int, mod)) { 23202 bounds_status = .non_integral; 23203 break :refine_bounds; 23204 } 23205 const scalar_bounds: ?[2]Value = bounds: { 23206 if (!ty.isVector(mod)) break :bounds try uncoerced_val.intValueBounds(mod); 23207 var cur_bounds: [2]Value = try Value.intValueBounds(try uncoerced_val.elemValue(mod, 0), mod) orelse break :bounds null; 23208 const len = try sema.usizeCast(block, src, ty.vectorLen(mod)); 23209 for (1..len) |i| { 23210 const elem = try uncoerced_val.elemValue(mod, i); 23211 const elem_bounds = try elem.intValueBounds(mod) orelse break :bounds null; 23212 cur_bounds = .{ 23213 Value.numberMin(elem_bounds[0], cur_bounds[0], mod), 23214 Value.numberMax(elem_bounds[1], cur_bounds[1], mod), 23215 }; 23216 } 23217 break :bounds cur_bounds; 23218 }; 23219 if (scalar_bounds) |bounds| { 23220 if (bounds_status == .unknown) { 23221 cur_min_scalar = bounds[0]; 23222 cur_max_scalar = bounds[1]; 23223 bounds_status = .defined; 23224 } else { 23225 cur_min_scalar = opFunc(cur_min_scalar, bounds[0], mod); 23226 cur_max_scalar = opFunc(cur_max_scalar, bounds[1], mod); 23227 } 23228 } 23229 }, 23230 .non_integral => {}, 23231 } 23232 23233 const cur = cur_minmax orelse { 23234 cur_minmax = operand; 23235 cur_minmax_src = operand_src; 23236 continue; 23237 }; 23238 23239 const simd_op = try sema.checkSimdBinOp(block, src, cur, operand, cur_minmax_src, operand_src); 23240 const cur_val = try sema.resolveLazyValue(simd_op.lhs_val.?); // cur_minmax is comptime-known 23241 const operand_val = try sema.resolveLazyValue(simd_op.rhs_val.?); // we checked the operand was resolvable above 23242 23243 const vec_len = simd_op.len orelse { 23244 const result_val = opFunc(cur_val, operand_val, mod); 23245 cur_minmax = try sema.addConstant(result_val); 23246 continue; 23247 }; 23248 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 23249 for (elems, 0..) |*elem, i| { 23250 const lhs_elem_val = try cur_val.elemValue(mod, i); 23251 const rhs_elem_val = try operand_val.elemValue(mod, i); 23252 const uncoerced_elem = opFunc(lhs_elem_val, rhs_elem_val, mod); 23253 elem.* = (try mod.getCoerced(uncoerced_elem, simd_op.scalar_ty)).toIntern(); 23254 } 23255 cur_minmax = try sema.addConstant((try mod.intern(.{ .aggregate = .{ 23256 .ty = simd_op.result_ty.toIntern(), 23257 .storage = .{ .elems = elems }, 23258 } })).toValue()); 23259 } 23260 23261 const opt_runtime_idx = runtime_known.findFirstSet(); 23262 23263 if (cur_minmax) |ct_minmax_ref| refine: { 23264 // Refine the comptime-known result type based on the bounds. This isn't strictly necessary 23265 // in the runtime case, since we'll refine the type again later, but keeping things as small 23266 // as possible will allow us to emit more optimal AIR (if all the runtime operands have 23267 // smaller types than the non-refined comptime type). 23268 23269 const val = (try sema.resolveMaybeUndefVal(ct_minmax_ref)).?; 23270 const orig_ty = sema.typeOf(ct_minmax_ref); 23271 23272 if (opt_runtime_idx == null and orig_ty.scalarType(mod).eql(Type.comptime_int, mod)) { 23273 // If all arguments were `comptime_int`, and there are no runtime args, we'll preserve that type 23274 break :refine; 23275 } 23276 23277 // We can't refine float types 23278 if (orig_ty.scalarType(mod).isAnyFloat()) break :refine; 23279 23280 assert(bounds_status == .defined); // there was a non-comptime-int integral comptime-known arg 23281 23282 const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar); 23283 const refined_ty = if (orig_ty.isVector(mod)) try mod.vectorType(.{ 23284 .len = orig_ty.vectorLen(mod), 23285 .child = refined_scalar_ty.toIntern(), 23286 }) else refined_scalar_ty; 23287 23288 // Apply the refined type to the current value 23289 if (std.debug.runtime_safety) { 23290 assert(try sema.intFitsInType(val, refined_ty, null)); 23291 } 23292 cur_minmax = try sema.coerceInMemory(val, refined_ty); 23293 } 23294 23295 const runtime_idx = opt_runtime_idx orelse return cur_minmax.?; 23296 const runtime_src = operand_srcs[runtime_idx]; 23297 try sema.requireRuntimeBlock(block, src, runtime_src); 23298 23299 // Now, iterate over runtime operands, emitting a min/max instruction for each. We'll refine the 23300 // type again at the end, based on the comptime-known bound. 23301 23302 // If the comptime-known part is undef we can avoid emitting actual instructions later 23303 const known_undef = if (cur_minmax) |operand| blk: { 23304 const val = (try sema.resolveMaybeUndefVal(operand)).?; 23305 break :blk val.isUndef(mod); 23306 } else false; 23307 23308 if (cur_minmax == null) { 23309 // No comptime operands - use the first operand as the starting value 23310 assert(bounds_status == .unknown); 23311 assert(runtime_idx == 0); 23312 cur_minmax = operands[0]; 23313 cur_minmax_src = runtime_src; 23314 runtime_known.unset(0); // don't look at this operand in the loop below 23315 const scalar_ty = sema.typeOf(cur_minmax.?).scalarType(mod); 23316 if (scalar_ty.isInt(mod)) { 23317 cur_min_scalar = try scalar_ty.minInt(mod, scalar_ty); 23318 cur_max_scalar = try scalar_ty.maxInt(mod, scalar_ty); 23319 bounds_status = .defined; 23320 } else { 23321 bounds_status = .non_integral; 23322 } 23323 } 23324 23325 var it = runtime_known.iterator(.{}); 23326 while (it.next()) |idx| { 23327 const lhs = cur_minmax.?; 23328 const lhs_src = cur_minmax_src; 23329 const rhs = operands[idx]; 23330 const rhs_src = operand_srcs[idx]; 23331 const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src); 23332 if (known_undef) { 23333 cur_minmax = try sema.addConstUndef(simd_op.result_ty); 23334 } else { 23335 cur_minmax = try block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); 23336 } 23337 // Compute the bounds of this type 23338 switch (bounds_status) { 23339 .unknown, .defined => refine_bounds: { 23340 const scalar_ty = sema.typeOf(rhs).scalarType(mod); 23341 if (scalar_ty.isAnyFloat()) { 23342 bounds_status = .non_integral; 23343 break :refine_bounds; 23344 } 23345 const scalar_min = try scalar_ty.minInt(mod, scalar_ty); 23346 const scalar_max = try scalar_ty.maxInt(mod, scalar_ty); 23347 if (bounds_status == .unknown) { 23348 cur_min_scalar = scalar_min; 23349 cur_max_scalar = scalar_max; 23350 bounds_status = .defined; 23351 } else { 23352 cur_min_scalar = opFunc(cur_min_scalar, scalar_min, mod); 23353 cur_max_scalar = opFunc(cur_max_scalar, scalar_max, mod); 23354 } 23355 }, 23356 .non_integral => {}, 23357 } 23358 } 23359 23360 // Finally, refine the type based on the known bounds. 23361 const unrefined_ty = sema.typeOf(cur_minmax.?); 23362 if (unrefined_ty.scalarType(mod).isAnyFloat()) { 23363 // We can't refine floats, so we're done. 23364 return cur_minmax.?; 23365 } 23366 assert(bounds_status == .defined); // there were integral runtime operands 23367 const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar); 23368 const refined_ty = if (unrefined_ty.isVector(mod)) try mod.vectorType(.{ 23369 .len = unrefined_ty.vectorLen(mod), 23370 .child = refined_scalar_ty.toIntern(), 23371 }) else refined_scalar_ty; 23372 23373 if (!refined_ty.eql(unrefined_ty, mod)) { 23374 // We've reduced the type - cast the result down 23375 return block.addTyOp(.intcast, refined_ty, cur_minmax.?); 23376 } 23377 23378 return cur_minmax.?; 23379 } 23380 23381 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { 23382 const mod = sema.mod; 23383 const info = sema.typeOf(ptr).ptrInfo(mod); 23384 if (info.flags.size == .One) { 23385 // Already an array pointer. 23386 return ptr; 23387 } 23388 const new_ty = try mod.ptrType(.{ 23389 .child = (try mod.arrayType(.{ 23390 .len = len, 23391 .sentinel = info.sentinel, 23392 .child = info.child, 23393 })).toIntern(), 23394 .flags = .{ 23395 .alignment = info.flags.alignment, 23396 .is_const = info.flags.is_const, 23397 .is_volatile = info.flags.is_volatile, 23398 .is_allowzero = info.flags.is_allowzero, 23399 .address_space = info.flags.address_space, 23400 }, 23401 }); 23402 if (info.flags.size == .Slice) { 23403 return block.addTyOp(.slice_ptr, new_ty, ptr); 23404 } 23405 return block.addBitCast(new_ty, ptr); 23406 } 23407 23408 fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 23409 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 23410 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23411 const src = inst_data.src(); 23412 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23413 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23414 const dest_ptr = try sema.resolveInst(extra.lhs); 23415 const src_ptr = try sema.resolveInst(extra.rhs); 23416 const dest_ty = sema.typeOf(dest_ptr); 23417 const src_ty = sema.typeOf(src_ptr); 23418 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); 23419 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); 23420 const target = sema.mod.getTarget(); 23421 const mod = sema.mod; 23422 23423 if (dest_ty.isConstPtr(mod)) { 23424 return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{}); 23425 } 23426 23427 if (dest_len == .none and src_len == .none) { 23428 const msg = msg: { 23429 const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{}); 23430 errdefer msg.destroy(sema.gpa); 23431 try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{ 23432 dest_ty.fmt(sema.mod), 23433 }); 23434 try sema.errNote(block, src_src, msg, "source type '{}' provides no length", .{ 23435 src_ty.fmt(sema.mod), 23436 }); 23437 break :msg msg; 23438 }; 23439 return sema.failWithOwnedErrorMsg(msg); 23440 } 23441 23442 var len_val: ?Value = null; 23443 23444 if (dest_len != .none and src_len != .none) check: { 23445 // If we can check at compile-time, no need for runtime safety. 23446 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 23447 len_val = dest_len_val; 23448 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 23449 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { 23450 const msg = msg: { 23451 const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{}); 23452 errdefer msg.destroy(sema.gpa); 23453 try sema.errNote(block, dest_src, msg, "length {} here", .{ 23454 dest_len_val.fmtValue(Type.usize, sema.mod), 23455 }); 23456 try sema.errNote(block, src_src, msg, "length {} here", .{ 23457 src_len_val.fmtValue(Type.usize, sema.mod), 23458 }); 23459 break :msg msg; 23460 }; 23461 return sema.failWithOwnedErrorMsg(msg); 23462 } 23463 break :check; 23464 } 23465 } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 23466 len_val = src_len_val; 23467 } 23468 23469 if (block.wantSafety()) { 23470 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); 23471 try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch); 23472 } 23473 } else if (dest_len != .none) { 23474 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 23475 len_val = dest_len_val; 23476 } 23477 } else if (src_len != .none) { 23478 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 23479 len_val = src_len_val; 23480 } 23481 } 23482 23483 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { 23484 if (!dest_ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src; 23485 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| { 23486 const len_u64 = (try len_val.?.getUnsignedIntAdvanced(mod, sema)).?; 23487 const len = try sema.usizeCast(block, dest_src, len_u64); 23488 for (0..len) |i| { 23489 const elem_index = try sema.addIntUnsigned(Type.usize, i); 23490 const dest_elem_ptr = try sema.elemPtrOneLayerOnly( 23491 block, 23492 src, 23493 dest_ptr, 23494 elem_index, 23495 src, 23496 true, // init 23497 false, // oob_safety 23498 ); 23499 const src_elem_ptr = try sema.elemPtrOneLayerOnly( 23500 block, 23501 src, 23502 src_ptr, 23503 elem_index, 23504 src, 23505 false, // init 23506 false, // oob_safety 23507 ); 23508 const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src); 23509 try sema.storePtr2( 23510 block, 23511 src, 23512 dest_elem_ptr, 23513 dest_src, 23514 uncoerced_elem, 23515 src_src, 23516 .store, 23517 ); 23518 } 23519 return; 23520 } else break :rs src_src; 23521 } else dest_src; 23522 23523 // If in-memory coercion is not allowed, explode this memcpy call into a 23524 // for loop that copies element-wise. 23525 // Likewise if this is an iterable rather than a pointer, do the same 23526 // lowering. The AIR instruction requires pointers with element types of 23527 // equal ABI size. 23528 23529 if (dest_ty.zigTypeTag(mod) != .Pointer or src_ty.zigTypeTag(mod) != .Pointer) { 23530 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{}); 23531 } 23532 23533 const dest_elem_ty = dest_ty.elemType2(mod); 23534 const src_elem_ty = src_ty.elemType2(mod); 23535 if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) { 23536 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{}); 23537 } 23538 23539 // If the length is comptime-known, then upgrade src and destination types 23540 // into pointer-to-array. At this point we know they are both pointers 23541 // already. 23542 var new_dest_ptr = dest_ptr; 23543 var new_src_ptr = src_ptr; 23544 if (len_val) |val| { 23545 const len = val.toUnsignedInt(mod); 23546 if (len == 0) { 23547 // This AIR instruction guarantees length > 0 if it is comptime-known. 23548 return; 23549 } 23550 new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len); 23551 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len); 23552 } 23553 23554 if (dest_len != .none) { 23555 // Change the src from slice to a many pointer, to avoid multiple ptr 23556 // slice extractions in AIR instructions. 23557 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 23558 if (new_src_ptr_ty.isSlice(mod)) { 23559 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 23560 } 23561 } else if (dest_len == .none and len_val == null) { 23562 // Change the dest to a slice, since its type must have the length. 23563 const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr); 23564 new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, .unneeded, dest_src, dest_src, dest_src, false); 23565 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 23566 if (new_src_ptr_ty.isSlice(mod)) { 23567 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 23568 } 23569 } 23570 23571 try sema.requireRuntimeBlock(block, src, runtime_src); 23572 23573 // Aliasing safety check. 23574 if (block.wantSafety()) { 23575 const len = if (len_val) |v| 23576 try sema.addConstant(v) 23577 else if (dest_len != .none) 23578 dest_len 23579 else 23580 src_len; 23581 23582 // Extract raw pointer from dest slice. The AIR instructions could support them, but 23583 // it would cause redundant machine code instructions. 23584 const new_dest_ptr_ty = sema.typeOf(new_dest_ptr); 23585 const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(mod)) 23586 try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty) 23587 else if (new_dest_ptr_ty.ptrSize(mod) == .One) ptr: { 23588 var dest_manyptr_ty_key = mod.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type; 23589 assert(dest_manyptr_ty_key.flags.size == .One); 23590 dest_manyptr_ty_key.child = dest_elem_ty.toIntern(); 23591 dest_manyptr_ty_key.flags.size = .Many; 23592 break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src); 23593 } else new_dest_ptr; 23594 23595 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 23596 const raw_src_ptr = if (new_src_ptr_ty.isSlice(mod)) 23597 try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty) 23598 else if (new_src_ptr_ty.ptrSize(mod) == .One) ptr: { 23599 var src_manyptr_ty_key = mod.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type; 23600 assert(src_manyptr_ty_key.flags.size == .One); 23601 src_manyptr_ty_key.child = src_elem_ty.toIntern(); 23602 src_manyptr_ty_key.flags.size = .Many; 23603 break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(src_manyptr_ty_key), new_src_ptr, src_src); 23604 } else new_src_ptr; 23605 23606 // ok1: dest >= src + len 23607 // ok2: src >= dest + len 23608 const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src); 23609 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src); 23610 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len); 23611 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len); 23612 const ok = try block.addBinOp(.bit_or, ok1, ok2); 23613 try sema.addSafetyCheck(block, ok, .memcpy_alias); 23614 } 23615 23616 _ = try block.addInst(.{ 23617 .tag = .memcpy, 23618 .data = .{ .bin_op = .{ 23619 .lhs = new_dest_ptr, 23620 .rhs = new_src_ptr, 23621 } }, 23622 }); 23623 } 23624 23625 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 23626 const mod = sema.mod; 23627 const gpa = sema.gpa; 23628 const ip = &mod.intern_pool; 23629 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 23630 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23631 const src = inst_data.src(); 23632 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23633 const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23634 const dest_ptr = try sema.resolveInst(extra.lhs); 23635 const uncoerced_elem = try sema.resolveInst(extra.rhs); 23636 const dest_ptr_ty = sema.typeOf(dest_ptr); 23637 try checkMemOperand(sema, block, dest_src, dest_ptr_ty); 23638 23639 if (dest_ptr_ty.isConstPtr(mod)) { 23640 return sema.fail(block, dest_src, "cannot memset constant pointer", .{}); 23641 } 23642 23643 const dest_elem_ty = dest_ptr_ty.elemType2(mod); 23644 23645 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { 23646 const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len"), dest_src); 23647 const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse 23648 break :rs dest_src; 23649 const len_u64 = (try len_val.getUnsignedIntAdvanced(mod, sema)).?; 23650 const len = try sema.usizeCast(block, dest_src, len_u64); 23651 if (len == 0) { 23652 // This AIR instruction guarantees length > 0 if it is comptime-known. 23653 return; 23654 } 23655 23656 if (!ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src; 23657 if (try sema.resolveMaybeUndefVal(uncoerced_elem)) |_| { 23658 for (0..len) |i| { 23659 const elem_index = try sema.addIntUnsigned(Type.usize, i); 23660 const elem_ptr = try sema.elemPtrOneLayerOnly( 23661 block, 23662 src, 23663 dest_ptr, 23664 elem_index, 23665 src, 23666 true, // init 23667 false, // oob_safety 23668 ); 23669 try sema.storePtr2( 23670 block, 23671 src, 23672 elem_ptr, 23673 dest_src, 23674 uncoerced_elem, 23675 value_src, 23676 .store, 23677 ); 23678 } 23679 return; 23680 } else break :rs value_src; 23681 } else dest_src; 23682 23683 const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); 23684 23685 try sema.requireRuntimeBlock(block, src, runtime_src); 23686 _ = try block.addInst(.{ 23687 .tag = if (block.wantSafety()) .memset_safe else .memset, 23688 .data = .{ .bin_op = .{ 23689 .lhs = dest_ptr, 23690 .rhs = elem, 23691 } }, 23692 }); 23693 } 23694 23695 fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 23696 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 23697 const src = LazySrcLoc.nodeOffset(extra.node); 23698 return sema.failWithUseOfAsync(block, src); 23699 } 23700 23701 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23702 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 23703 const src = inst_data.src(); 23704 return sema.failWithUseOfAsync(block, src); 23705 } 23706 23707 fn zirAwait( 23708 sema: *Sema, 23709 block: *Block, 23710 inst: Zir.Inst.Index, 23711 ) CompileError!Air.Inst.Ref { 23712 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 23713 const src = inst_data.src(); 23714 23715 return sema.failWithUseOfAsync(block, src); 23716 } 23717 23718 fn zirAwaitNosuspend( 23719 sema: *Sema, 23720 block: *Block, 23721 extended: Zir.Inst.Extended.InstData, 23722 ) CompileError!Air.Inst.Ref { 23723 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 23724 const src = LazySrcLoc.nodeOffset(extra.node); 23725 23726 return sema.failWithUseOfAsync(block, src); 23727 } 23728 23729 fn zirVarExtended( 23730 sema: *Sema, 23731 block: *Block, 23732 extended: Zir.Inst.Extended.InstData, 23733 ) CompileError!Air.Inst.Ref { 23734 const mod = sema.mod; 23735 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand); 23736 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 }; 23737 const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 }; 23738 const small = @as(Zir.Inst.ExtendedVar.Small, @bitCast(extended.small)); 23739 23740 var extra_index: usize = extra.end; 23741 23742 const lib_name: ?[]const u8 = if (small.has_lib_name) blk: { 23743 const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]); 23744 extra_index += 1; 23745 break :blk lib_name; 23746 } else null; 23747 23748 // ZIR supports encoding this information but it is not used; the information 23749 // is encoded via the Decl entry. 23750 assert(!small.has_align); 23751 23752 const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: { 23753 const init_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23754 extra_index += 1; 23755 break :blk try sema.resolveInst(init_ref); 23756 } else .none; 23757 23758 const have_ty = extra.data.var_type != .none; 23759 const var_ty = if (have_ty) 23760 try sema.resolveType(block, ty_src, extra.data.var_type) 23761 else 23762 sema.typeOf(uncasted_init); 23763 23764 const init_val = if (uncasted_init != .none) blk: { 23765 const init = if (have_ty) 23766 try sema.coerce(block, var_ty, uncasted_init, init_src) 23767 else 23768 uncasted_init; 23769 23770 break :blk ((try sema.resolveMaybeUndefVal(init)) orelse 23771 return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known")).toIntern(); 23772 } else .none; 23773 23774 try sema.validateVarType(block, ty_src, var_ty, small.is_extern); 23775 23776 return sema.addConstant((try mod.intern(.{ .variable = .{ 23777 .ty = var_ty.toIntern(), 23778 .init = init_val, 23779 .decl = sema.owner_decl_index, 23780 .lib_name = if (lib_name) |lname| (try mod.intern_pool.getOrPutString( 23781 sema.gpa, 23782 try sema.handleExternLibName(block, ty_src, lname), 23783 )).toOptional() else .none, 23784 .is_extern = small.is_extern, 23785 .is_threadlocal = small.is_threadlocal, 23786 } })).toValue()); 23787 } 23788 23789 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23790 const tracy = trace(@src()); 23791 defer tracy.end(); 23792 23793 const mod = sema.mod; 23794 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 23795 const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); 23796 const target = mod.getTarget(); 23797 23798 const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node }; 23799 const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node }; 23800 const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node }; 23801 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; 23802 const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node }; 23803 const has_body = extra.data.body_len != 0; 23804 23805 var extra_index: usize = extra.end; 23806 23807 const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: { 23808 const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]); 23809 extra_index += 1; 23810 break :blk lib_name; 23811 } else null; 23812 23813 if (has_body and 23814 (extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and 23815 !target_util.supportsFunctionAlignment(target)) 23816 { 23817 return sema.fail(block, align_src, "target does not support function alignment", .{}); 23818 } 23819 23820 const @"align": ?Alignment = if (extra.data.bits.has_align_body) blk: { 23821 const body_len = sema.code.extra[extra_index]; 23822 extra_index += 1; 23823 const body = sema.code.extra[extra_index..][0..body_len]; 23824 extra_index += body.len; 23825 23826 const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, "alignment must be comptime-known"); 23827 if (val.isGenericPoison()) { 23828 break :blk null; 23829 } 23830 const alignment = @as(u32, @intCast(val.toUnsignedInt(mod))); 23831 try sema.validateAlign(block, align_src, alignment); 23832 if (alignment == target_util.defaultFunctionAlignment(target)) { 23833 break :blk .none; 23834 } else { 23835 break :blk Alignment.fromNonzeroByteUnits(alignment); 23836 } 23837 } else if (extra.data.bits.has_align_ref) blk: { 23838 const align_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23839 extra_index += 1; 23840 const align_tv = sema.resolveInstConst(block, align_src, align_ref, "alignment must be comptime-known") catch |err| switch (err) { 23841 error.GenericPoison => { 23842 break :blk null; 23843 }, 23844 else => |e| return e, 23845 }; 23846 const alignment = @as(u32, @intCast(align_tv.val.toUnsignedInt(mod))); 23847 try sema.validateAlign(block, align_src, alignment); 23848 if (alignment == target_util.defaultFunctionAlignment(target)) { 23849 break :blk .none; 23850 } else { 23851 break :blk Alignment.fromNonzeroByteUnits(alignment); 23852 } 23853 } else .none; 23854 23855 const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: { 23856 const body_len = sema.code.extra[extra_index]; 23857 extra_index += 1; 23858 const body = sema.code.extra[extra_index..][0..body_len]; 23859 extra_index += body.len; 23860 23861 const addrspace_ty = try sema.getBuiltinType("AddressSpace"); 23862 const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, "addrespace must be comptime-known"); 23863 if (val.isGenericPoison()) { 23864 break :blk null; 23865 } 23866 break :blk mod.toEnum(std.builtin.AddressSpace, val); 23867 } else if (extra.data.bits.has_addrspace_ref) blk: { 23868 const addrspace_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23869 extra_index += 1; 23870 const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref, "addrespace must be comptime-known") catch |err| switch (err) { 23871 error.GenericPoison => { 23872 break :blk null; 23873 }, 23874 else => |e| return e, 23875 }; 23876 break :blk mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); 23877 } else target_util.defaultAddressSpace(target, .function); 23878 23879 const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: { 23880 const body_len = sema.code.extra[extra_index]; 23881 extra_index += 1; 23882 const body = sema.code.extra[extra_index..][0..body_len]; 23883 extra_index += body.len; 23884 23885 const ty = Type.slice_const_u8; 23886 const val = try sema.resolveGenericBody(block, section_src, body, inst, ty, "linksection must be comptime-known"); 23887 if (val.isGenericPoison()) { 23888 break :blk FuncLinkSection{ .generic = {} }; 23889 } 23890 break :blk FuncLinkSection{ .explicit = try val.toIpString(ty, mod) }; 23891 } else if (extra.data.bits.has_section_ref) blk: { 23892 const section_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23893 extra_index += 1; 23894 const section_name = sema.resolveConstStringIntern(block, section_src, section_ref, "linksection must be comptime-known") catch |err| switch (err) { 23895 error.GenericPoison => { 23896 break :blk FuncLinkSection{ .generic = {} }; 23897 }, 23898 else => |e| return e, 23899 }; 23900 break :blk FuncLinkSection{ .explicit = section_name }; 23901 } else FuncLinkSection{ .default = {} }; 23902 23903 const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: { 23904 const body_len = sema.code.extra[extra_index]; 23905 extra_index += 1; 23906 const body = sema.code.extra[extra_index..][0..body_len]; 23907 extra_index += body.len; 23908 23909 const cc_ty = try sema.getBuiltinType("CallingConvention"); 23910 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, "calling convention must be comptime-known"); 23911 if (val.isGenericPoison()) { 23912 break :blk null; 23913 } 23914 break :blk mod.toEnum(std.builtin.CallingConvention, val); 23915 } else if (extra.data.bits.has_cc_ref) blk: { 23916 const cc_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23917 extra_index += 1; 23918 const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref, "calling convention must be comptime-known") catch |err| switch (err) { 23919 error.GenericPoison => { 23920 break :blk null; 23921 }, 23922 else => |e| return e, 23923 }; 23924 break :blk mod.toEnum(std.builtin.CallingConvention, cc_tv.val); 23925 } else if (sema.owner_decl.is_exported and has_body) 23926 .C 23927 else 23928 .Unspecified; 23929 23930 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { 23931 const body_len = sema.code.extra[extra_index]; 23932 extra_index += 1; 23933 const body = sema.code.extra[extra_index..][0..body_len]; 23934 extra_index += body.len; 23935 23936 const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, "return type must be comptime-known"); 23937 const ty = val.toType(); 23938 break :blk ty; 23939 } else if (extra.data.bits.has_ret_ty_ref) blk: { 23940 const ret_ty_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23941 extra_index += 1; 23942 const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, "return type must be comptime-known") catch |err| switch (err) { 23943 error.GenericPoison => { 23944 break :blk Type.generic_poison; 23945 }, 23946 else => |e| return e, 23947 }; 23948 const ty = ret_ty_tv.val.toType(); 23949 break :blk ty; 23950 } else Type.void; 23951 23952 const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { 23953 const x = sema.code.extra[extra_index]; 23954 extra_index += 1; 23955 break :blk x; 23956 } else 0; 23957 23958 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 23959 if (has_body) { 23960 extra_index += extra.data.body_len; 23961 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 23962 } 23963 23964 const is_var_args = extra.data.bits.is_var_args; 23965 const is_inferred_error = extra.data.bits.is_inferred_error; 23966 const is_extern = extra.data.bits.is_extern; 23967 const is_noinline = extra.data.bits.is_noinline; 23968 23969 return sema.funcCommon( 23970 block, 23971 inst_data.src_node, 23972 inst, 23973 @"align", 23974 @"addrspace", 23975 @"linksection", 23976 cc, 23977 ret_ty, 23978 is_var_args, 23979 is_inferred_error, 23980 is_extern, 23981 has_body, 23982 src_locs, 23983 lib_name, 23984 noalias_bits, 23985 is_noinline, 23986 ); 23987 } 23988 23989 fn zirCUndef( 23990 sema: *Sema, 23991 block: *Block, 23992 extended: Zir.Inst.Extended.InstData, 23993 ) CompileError!Air.Inst.Ref { 23994 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 23995 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 23996 23997 const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known"); 23998 try block.c_import_buf.?.writer().print("#undef {s}\n", .{name}); 23999 return Air.Inst.Ref.void_value; 24000 } 24001 24002 fn zirCInclude( 24003 sema: *Sema, 24004 block: *Block, 24005 extended: Zir.Inst.Extended.InstData, 24006 ) CompileError!Air.Inst.Ref { 24007 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24008 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24009 24010 const name = try sema.resolveConstString(block, src, extra.operand, "path being included must be comptime-known"); 24011 try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name}); 24012 return Air.Inst.Ref.void_value; 24013 } 24014 24015 fn zirCDefine( 24016 sema: *Sema, 24017 block: *Block, 24018 extended: Zir.Inst.Extended.InstData, 24019 ) CompileError!Air.Inst.Ref { 24020 const mod = sema.mod; 24021 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 24022 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24023 const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 24024 24025 const name = try sema.resolveConstString(block, name_src, extra.lhs, "name of macro being undefined must be comptime-known"); 24026 const rhs = try sema.resolveInst(extra.rhs); 24027 if (sema.typeOf(rhs).zigTypeTag(mod) != .Void) { 24028 const value = try sema.resolveConstString(block, val_src, extra.rhs, "value of macro being undefined must be comptime-known"); 24029 try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); 24030 } else { 24031 try block.c_import_buf.?.writer().print("#define {s}\n", .{name}); 24032 } 24033 return Air.Inst.Ref.void_value; 24034 } 24035 24036 fn zirWasmMemorySize( 24037 sema: *Sema, 24038 block: *Block, 24039 extended: Zir.Inst.Extended.InstData, 24040 ) CompileError!Air.Inst.Ref { 24041 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24042 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24043 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 24044 const target = sema.mod.getTarget(); 24045 if (!target.isWasm()) { 24046 return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 24047 } 24048 24049 const index = @as(u32, @intCast(try sema.resolveInt(block, index_src, extra.operand, Type.u32, "wasm memory size index must be comptime-known"))); 24050 try sema.requireRuntimeBlock(block, builtin_src, null); 24051 return block.addInst(.{ 24052 .tag = .wasm_memory_size, 24053 .data = .{ .pl_op = .{ 24054 .operand = .none, 24055 .payload = index, 24056 } }, 24057 }); 24058 } 24059 24060 fn zirWasmMemoryGrow( 24061 sema: *Sema, 24062 block: *Block, 24063 extended: Zir.Inst.Extended.InstData, 24064 ) CompileError!Air.Inst.Ref { 24065 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 24066 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 24067 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24068 const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 24069 const target = sema.mod.getTarget(); 24070 if (!target.isWasm()) { 24071 return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 24072 } 24073 24074 const index = @as(u32, @intCast(try sema.resolveInt(block, index_src, extra.lhs, Type.u32, "wasm memory size index must be comptime-known"))); 24075 const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src); 24076 24077 try sema.requireRuntimeBlock(block, builtin_src, null); 24078 return block.addInst(.{ 24079 .tag = .wasm_memory_grow, 24080 .data = .{ .pl_op = .{ 24081 .operand = delta, 24082 .payload = index, 24083 } }, 24084 }); 24085 } 24086 24087 fn resolvePrefetchOptions( 24088 sema: *Sema, 24089 block: *Block, 24090 src: LazySrcLoc, 24091 zir_ref: Zir.Inst.Ref, 24092 ) CompileError!std.builtin.PrefetchOptions { 24093 const mod = sema.mod; 24094 const gpa = sema.gpa; 24095 const ip = &mod.intern_pool; 24096 const options_ty = try sema.getBuiltinType("PrefetchOptions"); 24097 const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src); 24098 24099 const rw_src = sema.maybeOptionsSrc(block, src, "rw"); 24100 const locality_src = sema.maybeOptionsSrc(block, src, "locality"); 24101 const cache_src = sema.maybeOptionsSrc(block, src, "cache"); 24102 24103 const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw"), rw_src); 24104 const rw_val = try sema.resolveConstValue(block, rw_src, rw, "prefetch read/write must be comptime-known"); 24105 24106 const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality"), locality_src); 24107 const locality_val = try sema.resolveConstValue(block, locality_src, locality, "prefetch locality must be comptime-known"); 24108 24109 const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache"), cache_src); 24110 const cache_val = try sema.resolveConstValue(block, cache_src, cache, "prefetch cache must be comptime-known"); 24111 24112 return std.builtin.PrefetchOptions{ 24113 .rw = mod.toEnum(std.builtin.PrefetchOptions.Rw, rw_val), 24114 .locality = @as(u2, @intCast(locality_val.toUnsignedInt(mod))), 24115 .cache = mod.toEnum(std.builtin.PrefetchOptions.Cache, cache_val), 24116 }; 24117 } 24118 24119 fn zirPrefetch( 24120 sema: *Sema, 24121 block: *Block, 24122 extended: Zir.Inst.Extended.InstData, 24123 ) CompileError!Air.Inst.Ref { 24124 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 24125 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24126 const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 24127 const ptr = try sema.resolveInst(extra.lhs); 24128 try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); 24129 24130 const options = sema.resolvePrefetchOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { 24131 error.NeededSourceLocation => { 24132 _ = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs); 24133 unreachable; 24134 }, 24135 else => |e| return e, 24136 }; 24137 24138 if (!block.is_comptime) { 24139 _ = try block.addInst(.{ 24140 .tag = .prefetch, 24141 .data = .{ .prefetch = .{ 24142 .ptr = ptr, 24143 .rw = options.rw, 24144 .locality = options.locality, 24145 .cache = options.cache, 24146 } }, 24147 }); 24148 } 24149 24150 return Air.Inst.Ref.void_value; 24151 } 24152 24153 fn resolveExternOptions( 24154 sema: *Sema, 24155 block: *Block, 24156 src: LazySrcLoc, 24157 zir_ref: Zir.Inst.Ref, 24158 ) CompileError!struct { 24159 name: InternPool.NullTerminatedString, 24160 library_name: InternPool.OptionalNullTerminatedString = .none, 24161 linkage: std.builtin.GlobalLinkage = .Strong, 24162 is_thread_local: bool = false, 24163 } { 24164 const mod = sema.mod; 24165 const gpa = sema.gpa; 24166 const ip = &mod.intern_pool; 24167 const options_inst = try sema.resolveInst(zir_ref); 24168 const extern_options_ty = try sema.getBuiltinType("ExternOptions"); 24169 const options = try sema.coerce(block, extern_options_ty, options_inst, src); 24170 24171 const name_src = sema.maybeOptionsSrc(block, src, "name"); 24172 const library_src = sema.maybeOptionsSrc(block, src, "library"); 24173 const linkage_src = sema.maybeOptionsSrc(block, src, "linkage"); 24174 const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local"); 24175 24176 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); 24177 const name_val = try sema.resolveConstValue(block, name_src, name_ref, "name of the extern symbol must be comptime-known"); 24178 const name = try name_val.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); 24179 24180 const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src); 24181 const library_name_val = try sema.resolveConstValue(block, library_src, library_name_inst, "library in which extern symbol is must be comptime-known"); 24182 24183 const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); 24184 const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_ref, "linkage of the extern symbol must be comptime-known"); 24185 const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); 24186 24187 const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local"), thread_local_src); 24188 const is_thread_local_val = try sema.resolveConstValue(block, thread_local_src, is_thread_local, "threadlocality of the extern symbol must be comptime-known"); 24189 24190 const library_name = if (library_name_val.optionalValue(mod)) |payload| blk: { 24191 const library_name = try payload.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); 24192 if (library_name.len == 0) { 24193 return sema.fail(block, library_src, "library name cannot be empty", .{}); 24194 } 24195 break :blk try sema.handleExternLibName(block, library_src, library_name); 24196 } else null; 24197 24198 if (name.len == 0) { 24199 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{}); 24200 } 24201 24202 if (linkage != .Weak and linkage != .Strong) { 24203 return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{}); 24204 } 24205 24206 return .{ 24207 .name = try ip.getOrPutString(gpa, name), 24208 .library_name = try ip.getOrPutStringOpt(gpa, library_name), 24209 .linkage = linkage, 24210 .is_thread_local = is_thread_local_val.toBool(), 24211 }; 24212 } 24213 24214 fn zirBuiltinExtern( 24215 sema: *Sema, 24216 block: *Block, 24217 extended: Zir.Inst.Extended.InstData, 24218 ) CompileError!Air.Inst.Ref { 24219 const mod = sema.mod; 24220 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 24221 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24222 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 24223 24224 var ty = try sema.resolveType(block, ty_src, extra.lhs); 24225 if (!ty.isPtrAtRuntime(mod)) { 24226 return sema.fail(block, ty_src, "expected (optional) pointer", .{}); 24227 } 24228 if (!try sema.validateExternType(ty.childType(mod), .other)) { 24229 const msg = msg: { 24230 const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)}); 24231 errdefer msg.destroy(sema.gpa); 24232 const src_decl = sema.mod.declPtr(block.src_decl); 24233 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other); 24234 break :msg msg; 24235 }; 24236 return sema.failWithOwnedErrorMsg(msg); 24237 } 24238 24239 const options = sema.resolveExternOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { 24240 error.NeededSourceLocation => { 24241 _ = try sema.resolveExternOptions(block, options_src, extra.rhs); 24242 unreachable; 24243 }, 24244 else => |e| return e, 24245 }; 24246 24247 if (options.linkage == .Weak and !ty.ptrAllowsZero(mod)) { 24248 ty = try mod.optionalType(ty.toIntern()); 24249 } 24250 24251 // TODO check duplicate extern 24252 24253 const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null); 24254 errdefer mod.destroyDecl(new_decl_index); 24255 const new_decl = mod.declPtr(new_decl_index); 24256 new_decl.name = options.name; 24257 24258 { 24259 const new_var = try mod.intern(.{ .variable = .{ 24260 .ty = ty.toIntern(), 24261 .init = .none, 24262 .decl = sema.owner_decl_index, 24263 .is_extern = true, 24264 .is_const = true, 24265 .is_threadlocal = options.is_thread_local, 24266 .is_weak_linkage = options.linkage == .Weak, 24267 } }); 24268 24269 new_decl.src_line = sema.owner_decl.src_line; 24270 // We only access this decl through the decl_ref with the correct type created 24271 // below, so this type doesn't matter 24272 new_decl.ty = ty; 24273 new_decl.val = new_var.toValue(); 24274 new_decl.alignment = .none; 24275 new_decl.@"linksection" = .none; 24276 new_decl.has_tv = true; 24277 new_decl.analysis = .complete; 24278 new_decl.generation = mod.generation; 24279 } 24280 24281 try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); 24282 try sema.ensureDeclAnalyzed(new_decl_index); 24283 24284 return sema.addConstant(try mod.getCoerced((try mod.intern(.{ .ptr = .{ 24285 .ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) { 24286 .ptr_type => ty.toIntern(), 24287 .opt_type => |child_type| child_type, 24288 else => unreachable, 24289 }, 24290 .addr = .{ .decl = new_decl_index }, 24291 } })).toValue(), ty)); 24292 } 24293 24294 fn zirWorkItem( 24295 sema: *Sema, 24296 block: *Block, 24297 extended: Zir.Inst.Extended.InstData, 24298 zir_tag: Zir.Inst.Extended, 24299 ) CompileError!Air.Inst.Ref { 24300 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24301 const dimension_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24302 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 24303 const target = sema.mod.getTarget(); 24304 24305 switch (target.cpu.arch) { 24306 // TODO: Allow for other GPU targets. 24307 .amdgcn => {}, 24308 else => { 24309 return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)}); 24310 }, 24311 } 24312 24313 const dimension = @as(u32, @intCast(try sema.resolveInt(block, dimension_src, extra.operand, Type.u32, "dimension must be comptime-known"))); 24314 try sema.requireRuntimeBlock(block, builtin_src, null); 24315 24316 return block.addInst(.{ 24317 .tag = switch (zir_tag) { 24318 .work_item_id => .work_item_id, 24319 .work_group_size => .work_group_size, 24320 .work_group_id => .work_group_id, 24321 else => unreachable, 24322 }, 24323 .data = .{ .pl_op = .{ 24324 .operand = .none, 24325 .payload = dimension, 24326 } }, 24327 }); 24328 } 24329 24330 fn zirInComptime( 24331 sema: *Sema, 24332 block: *Block, 24333 ) CompileError!Air.Inst.Ref { 24334 _ = sema; 24335 if (block.is_comptime) { 24336 return Air.Inst.Ref.bool_true; 24337 } else { 24338 return Air.Inst.Ref.bool_false; 24339 } 24340 } 24341 24342 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { 24343 if (block.is_comptime) { 24344 const msg = msg: { 24345 const msg = try sema.errMsg(block, src, "unable to evaluate comptime expression", .{}); 24346 errdefer msg.destroy(sema.gpa); 24347 24348 if (runtime_src) |some| { 24349 try sema.errNote(block, some, msg, "operation is runtime due to this operand", .{}); 24350 } 24351 if (block.comptime_reason) |some| { 24352 try some.explain(sema, msg); 24353 } 24354 break :msg msg; 24355 }; 24356 return sema.failWithOwnedErrorMsg(msg); 24357 } 24358 } 24359 24360 /// Emit a compile error if type cannot be used for a runtime variable. 24361 fn validateVarType( 24362 sema: *Sema, 24363 block: *Block, 24364 src: LazySrcLoc, 24365 var_ty: Type, 24366 is_extern: bool, 24367 ) CompileError!void { 24368 const mod = sema.mod; 24369 if (is_extern and !try sema.validateExternType(var_ty, .other)) { 24370 const msg = msg: { 24371 const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)}); 24372 errdefer msg.destroy(sema.gpa); 24373 const src_decl = mod.declPtr(block.src_decl); 24374 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other); 24375 break :msg msg; 24376 }; 24377 return sema.failWithOwnedErrorMsg(msg); 24378 } 24379 24380 if (try sema.validateRunTimeType(var_ty, is_extern)) return; 24381 24382 const msg = msg: { 24383 const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)}); 24384 errdefer msg.destroy(sema.gpa); 24385 24386 const src_decl = mod.declPtr(block.src_decl); 24387 try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty); 24388 if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) { 24389 try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{}); 24390 } 24391 24392 break :msg msg; 24393 }; 24394 return sema.failWithOwnedErrorMsg(msg); 24395 } 24396 24397 fn validateRunTimeType( 24398 sema: *Sema, 24399 var_ty: Type, 24400 is_extern: bool, 24401 ) CompileError!bool { 24402 const mod = sema.mod; 24403 var ty = var_ty; 24404 while (true) switch (ty.zigTypeTag(mod)) { 24405 .Bool, 24406 .Int, 24407 .Float, 24408 .ErrorSet, 24409 .Frame, 24410 .AnyFrame, 24411 .Void, 24412 => return true, 24413 24414 .Enum => return !(try sema.typeRequiresComptime(ty)), 24415 24416 .ComptimeFloat, 24417 .ComptimeInt, 24418 .EnumLiteral, 24419 .NoReturn, 24420 .Type, 24421 .Undefined, 24422 .Null, 24423 .Fn, 24424 => return false, 24425 24426 .Pointer => { 24427 const elem_ty = ty.childType(mod); 24428 switch (elem_ty.zigTypeTag(mod)) { 24429 .Opaque => return true, 24430 .Fn => return elem_ty.isFnOrHasRuntimeBits(mod), 24431 else => ty = elem_ty, 24432 } 24433 }, 24434 .Opaque => return is_extern, 24435 24436 .Optional => { 24437 const child_ty = ty.optionalChild(mod); 24438 return sema.validateRunTimeType(child_ty, is_extern); 24439 }, 24440 .Array, .Vector => ty = ty.childType(mod), 24441 24442 .ErrorUnion => ty = ty.errorUnionPayload(mod), 24443 24444 .Struct, .Union => { 24445 const resolved_ty = try sema.resolveTypeFields(ty); 24446 const needs_comptime = try sema.typeRequiresComptime(resolved_ty); 24447 return !needs_comptime; 24448 }, 24449 }; 24450 } 24451 24452 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void); 24453 24454 fn explainWhyTypeIsComptime( 24455 sema: *Sema, 24456 msg: *Module.ErrorMsg, 24457 src_loc: Module.SrcLoc, 24458 ty: Type, 24459 ) CompileError!void { 24460 var type_set = TypeSet{}; 24461 defer type_set.deinit(sema.gpa); 24462 24463 try sema.resolveTypeFully(ty); 24464 return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set); 24465 } 24466 24467 fn explainWhyTypeIsComptimeInner( 24468 sema: *Sema, 24469 msg: *Module.ErrorMsg, 24470 src_loc: Module.SrcLoc, 24471 ty: Type, 24472 type_set: *TypeSet, 24473 ) CompileError!void { 24474 const mod = sema.mod; 24475 switch (ty.zigTypeTag(mod)) { 24476 .Bool, 24477 .Int, 24478 .Float, 24479 .ErrorSet, 24480 .Enum, 24481 .Frame, 24482 .AnyFrame, 24483 .Void, 24484 => return, 24485 24486 .Fn => { 24487 try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{ 24488 ty.fmt(sema.mod), 24489 }); 24490 }, 24491 24492 .Type => { 24493 try mod.errNoteNonLazy(src_loc, msg, "types are not available at runtime", .{}); 24494 }, 24495 24496 .ComptimeFloat, 24497 .ComptimeInt, 24498 .EnumLiteral, 24499 .NoReturn, 24500 .Undefined, 24501 .Null, 24502 => return, 24503 24504 .Opaque => { 24505 try mod.errNoteNonLazy(src_loc, msg, "opaque type '{}' has undefined size", .{ty.fmt(sema.mod)}); 24506 }, 24507 24508 .Array, .Vector => { 24509 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set); 24510 }, 24511 .Pointer => { 24512 const elem_ty = ty.elemType2(mod); 24513 if (elem_ty.zigTypeTag(mod) == .Fn) { 24514 const fn_info = mod.typeToFunc(elem_ty).?; 24515 if (fn_info.is_generic) { 24516 try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{}); 24517 } 24518 switch (fn_info.cc) { 24519 .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}), 24520 else => {}, 24521 } 24522 if (fn_info.return_type.toType().comptimeOnly(mod)) { 24523 try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{}); 24524 } 24525 return; 24526 } 24527 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set); 24528 }, 24529 24530 .Optional => { 24531 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(mod), type_set); 24532 }, 24533 .ErrorUnion => { 24534 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(mod), type_set); 24535 }, 24536 24537 .Struct => { 24538 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 24539 24540 if (mod.typeToStruct(ty)) |struct_obj| { 24541 for (struct_obj.fields.values(), 0..) |field, i| { 24542 const field_src_loc = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 24543 .index = i, 24544 .range = .type, 24545 }); 24546 24547 if (try sema.typeRequiresComptime(field.ty)) { 24548 try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{}); 24549 try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set); 24550 } 24551 } 24552 } 24553 // TODO tuples 24554 }, 24555 24556 .Union => { 24557 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 24558 24559 if (mod.typeToUnion(ty)) |union_obj| { 24560 for (union_obj.fields.values(), 0..) |field, i| { 24561 const field_src_loc = mod.fieldSrcLoc(union_obj.owner_decl, .{ 24562 .index = i, 24563 .range = .type, 24564 }); 24565 24566 if (try sema.typeRequiresComptime(field.ty)) { 24567 try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{}); 24568 try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set); 24569 } 24570 } 24571 } 24572 }, 24573 } 24574 } 24575 24576 const ExternPosition = enum { 24577 ret_ty, 24578 param_ty, 24579 union_field, 24580 struct_field, 24581 element, 24582 other, 24583 }; 24584 24585 /// Returns true if `ty` is allowed in extern types. 24586 /// Does *NOT* require `ty` to be resolved in any way. 24587 /// Calls `resolveTypeLayout` for packed containers. 24588 fn validateExternType( 24589 sema: *Sema, 24590 ty: Type, 24591 position: ExternPosition, 24592 ) !bool { 24593 const mod = sema.mod; 24594 switch (ty.zigTypeTag(mod)) { 24595 .Type, 24596 .ComptimeFloat, 24597 .ComptimeInt, 24598 .EnumLiteral, 24599 .Undefined, 24600 .Null, 24601 .ErrorUnion, 24602 .ErrorSet, 24603 .Frame, 24604 => return false, 24605 .Void => return position == .union_field or position == .ret_ty, 24606 .NoReturn => return position == .ret_ty, 24607 .Opaque, 24608 .Bool, 24609 .Float, 24610 .AnyFrame, 24611 => return true, 24612 .Pointer => return !(ty.isSlice(mod) or try sema.typeRequiresComptime(ty)), 24613 .Int => switch (ty.intInfo(mod).bits) { 24614 8, 16, 32, 64, 128 => return true, 24615 else => return false, 24616 }, 24617 .Fn => { 24618 if (position != .other) return false; 24619 const target = sema.mod.getTarget(); 24620 // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI. 24621 // The goal is to experiment with more integrated CPU/GPU code. 24622 if (ty.fnCallingConvention(mod) == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) { 24623 return true; 24624 } 24625 return !target_util.fnCallConvAllowsZigTypes(target, ty.fnCallingConvention(mod)); 24626 }, 24627 .Enum => { 24628 return sema.validateExternType(ty.intTagType(mod), position); 24629 }, 24630 .Struct, .Union => switch (ty.containerLayout(mod)) { 24631 .Extern => return true, 24632 .Packed => { 24633 const bit_size = try ty.bitSizeAdvanced(mod, sema); 24634 switch (bit_size) { 24635 8, 16, 32, 64, 128 => return true, 24636 else => return false, 24637 } 24638 }, 24639 .Auto => return false, 24640 }, 24641 .Array => { 24642 if (position == .ret_ty or position == .param_ty) return false; 24643 return sema.validateExternType(ty.elemType2(mod), .element); 24644 }, 24645 .Vector => return sema.validateExternType(ty.elemType2(mod), .element), 24646 .Optional => return ty.isPtrLikeOptional(mod), 24647 } 24648 } 24649 24650 fn explainWhyTypeIsNotExtern( 24651 sema: *Sema, 24652 msg: *Module.ErrorMsg, 24653 src_loc: Module.SrcLoc, 24654 ty: Type, 24655 position: ExternPosition, 24656 ) CompileError!void { 24657 const mod = sema.mod; 24658 switch (ty.zigTypeTag(mod)) { 24659 .Opaque, 24660 .Bool, 24661 .Float, 24662 .AnyFrame, 24663 => return, 24664 24665 .Type, 24666 .ComptimeFloat, 24667 .ComptimeInt, 24668 .EnumLiteral, 24669 .Undefined, 24670 .Null, 24671 .ErrorUnion, 24672 .ErrorSet, 24673 .Frame, 24674 => return, 24675 24676 .Pointer => { 24677 if (ty.isSlice(mod)) { 24678 try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); 24679 } else { 24680 const pointee_ty = ty.childType(mod); 24681 try mod.errNoteNonLazy(src_loc, msg, "pointer to comptime-only type '{}'", .{pointee_ty.fmt(sema.mod)}); 24682 try sema.explainWhyTypeIsComptime(msg, src_loc, pointee_ty); 24683 } 24684 }, 24685 .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}), 24686 .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), 24687 .Int => if (!std.math.isPowerOfTwo(ty.intInfo(mod).bits)) { 24688 try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{}); 24689 } else { 24690 try mod.errNoteNonLazy(src_loc, msg, "only integers with 8, 16, 32, 64 and 128 bits are extern compatible", .{}); 24691 }, 24692 .Fn => { 24693 if (position != .other) { 24694 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 24695 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 24696 return; 24697 } 24698 switch (ty.fnCallingConvention(mod)) { 24699 .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}), 24700 .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}), 24701 .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}), 24702 else => return, 24703 } 24704 }, 24705 .Enum => { 24706 const tag_ty = ty.intTagType(mod); 24707 try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)}); 24708 try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position); 24709 }, 24710 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}), 24711 .Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}), 24712 .Array => { 24713 if (position == .ret_ty) { 24714 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); 24715 } else if (position == .param_ty) { 24716 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); 24717 } 24718 try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element); 24719 }, 24720 .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element), 24721 .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}), 24722 } 24723 } 24724 24725 /// Returns true if `ty` is allowed in packed types. 24726 /// Does *NOT* require `ty` to be resolved in any way. 24727 fn validatePackedType(ty: Type, mod: *Module) bool { 24728 switch (ty.zigTypeTag(mod)) { 24729 .Type, 24730 .ComptimeFloat, 24731 .ComptimeInt, 24732 .EnumLiteral, 24733 .Undefined, 24734 .Null, 24735 .ErrorUnion, 24736 .ErrorSet, 24737 .Frame, 24738 .NoReturn, 24739 .Opaque, 24740 .AnyFrame, 24741 .Fn, 24742 .Array, 24743 => return false, 24744 .Optional => return ty.isPtrLikeOptional(mod), 24745 .Void, 24746 .Bool, 24747 .Float, 24748 .Int, 24749 .Vector, 24750 .Enum, 24751 => return true, 24752 .Pointer => return !ty.isSlice(mod), 24753 .Struct, .Union => return ty.containerLayout(mod) == .Packed, 24754 } 24755 } 24756 24757 fn explainWhyTypeIsNotPacked( 24758 sema: *Sema, 24759 msg: *Module.ErrorMsg, 24760 src_loc: Module.SrcLoc, 24761 ty: Type, 24762 ) CompileError!void { 24763 const mod = sema.mod; 24764 switch (ty.zigTypeTag(mod)) { 24765 .Void, 24766 .Bool, 24767 .Float, 24768 .Int, 24769 .Vector, 24770 .Enum, 24771 => return, 24772 .Type, 24773 .ComptimeFloat, 24774 .ComptimeInt, 24775 .EnumLiteral, 24776 .Undefined, 24777 .Null, 24778 .Frame, 24779 .NoReturn, 24780 .Opaque, 24781 .ErrorUnion, 24782 .ErrorSet, 24783 .AnyFrame, 24784 .Optional, 24785 .Array, 24786 => try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}), 24787 .Pointer => try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}), 24788 .Fn => { 24789 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 24790 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 24791 }, 24792 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}), 24793 .Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}), 24794 } 24795 } 24796 24797 fn prepareSimplePanic(sema: *Sema, block: *Block) !void { 24798 const mod = sema.mod; 24799 24800 if (mod.panic_func_index == .none) { 24801 const decl_index = (try sema.getBuiltinDecl(block, "panic")); 24802 // decl_index may be an alias; we must find the decl that actually 24803 // owns the function. 24804 try sema.ensureDeclAnalyzed(decl_index); 24805 const tv = try mod.declPtr(decl_index).typedValue(); 24806 assert(tv.ty.zigTypeTag(mod) == .Fn); 24807 assert(try sema.fnHasRuntimeBits(tv.ty)); 24808 const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap().?; 24809 try mod.ensureFuncBodyAnalysisQueued(func_index); 24810 mod.panic_func_index = func_index.toOptional(); 24811 } 24812 24813 if (mod.null_stack_trace == .none) { 24814 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 24815 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 24816 const target = mod.getTarget(); 24817 const ptr_stack_trace_ty = try mod.ptrType(.{ 24818 .child = stack_trace_ty.toIntern(), 24819 .flags = .{ 24820 .address_space = target_util.defaultAddressSpace(target, .global_constant), 24821 }, 24822 }); 24823 const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern()); 24824 mod.null_stack_trace = try mod.intern(.{ .opt = .{ 24825 .ty = opt_ptr_stack_trace_ty.toIntern(), 24826 .val = .none, 24827 } }); 24828 } 24829 } 24830 24831 /// Backends depend on panic decls being available when lowering safety-checked 24832 /// instructions. This function ensures the panic function will be available to 24833 /// be called during that time. 24834 fn preparePanicId(sema: *Sema, block: *Block, panic_id: Module.PanicId) !Module.Decl.Index { 24835 const mod = sema.mod; 24836 const gpa = sema.gpa; 24837 if (mod.panic_messages[@intFromEnum(panic_id)].unwrap()) |x| return x; 24838 24839 try sema.prepareSimplePanic(block); 24840 24841 const panic_messages_ty = try sema.getBuiltinType("panic_messages"); 24842 const msg_decl_index = (try sema.namespaceLookup( 24843 block, 24844 sema.src, 24845 panic_messages_ty.getNamespaceIndex(mod).unwrap().?, 24846 try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id)), 24847 )).?; 24848 try sema.ensureDeclAnalyzed(msg_decl_index); 24849 mod.panic_messages[@intFromEnum(panic_id)] = msg_decl_index.toOptional(); 24850 return msg_decl_index; 24851 } 24852 24853 fn addSafetyCheck( 24854 sema: *Sema, 24855 parent_block: *Block, 24856 ok: Air.Inst.Ref, 24857 panic_id: Module.PanicId, 24858 ) !void { 24859 const gpa = sema.gpa; 24860 assert(!parent_block.is_comptime); 24861 24862 var fail_block: Block = .{ 24863 .parent = parent_block, 24864 .sema = sema, 24865 .src_decl = parent_block.src_decl, 24866 .namespace = parent_block.namespace, 24867 .wip_capture_scope = parent_block.wip_capture_scope, 24868 .instructions = .{}, 24869 .inlining = parent_block.inlining, 24870 .is_comptime = false, 24871 }; 24872 24873 defer fail_block.instructions.deinit(gpa); 24874 24875 try sema.safetyPanic(&fail_block, panic_id); 24876 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 24877 } 24878 24879 fn addSafetyCheckExtra( 24880 sema: *Sema, 24881 parent_block: *Block, 24882 ok: Air.Inst.Ref, 24883 fail_block: *Block, 24884 ) !void { 24885 const gpa = sema.gpa; 24886 24887 try parent_block.instructions.ensureUnusedCapacity(gpa, 1); 24888 24889 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + 24890 1 + // The main block only needs space for the cond_br. 24891 @typeInfo(Air.CondBr).Struct.fields.len + 24892 1 + // The ok branch of the cond_br only needs space for the br. 24893 fail_block.instructions.items.len); 24894 24895 try sema.air_instructions.ensureUnusedCapacity(gpa, 3); 24896 const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 24897 const cond_br_inst = block_inst + 1; 24898 const br_inst = cond_br_inst + 1; 24899 sema.air_instructions.appendAssumeCapacity(.{ 24900 .tag = .block, 24901 .data = .{ .ty_pl = .{ 24902 .ty = .void_type, 24903 .payload = sema.addExtraAssumeCapacity(Air.Block{ 24904 .body_len = 1, 24905 }), 24906 } }, 24907 }); 24908 sema.air_extra.appendAssumeCapacity(cond_br_inst); 24909 24910 sema.air_instructions.appendAssumeCapacity(.{ 24911 .tag = .cond_br, 24912 .data = .{ .pl_op = .{ 24913 .operand = ok, 24914 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 24915 .then_body_len = 1, 24916 .else_body_len = @as(u32, @intCast(fail_block.instructions.items.len)), 24917 }), 24918 } }, 24919 }); 24920 sema.air_extra.appendAssumeCapacity(br_inst); 24921 sema.air_extra.appendSliceAssumeCapacity(fail_block.instructions.items); 24922 24923 sema.air_instructions.appendAssumeCapacity(.{ 24924 .tag = .br, 24925 .data = .{ .br = .{ 24926 .block_inst = block_inst, 24927 .operand = .void_value, 24928 } }, 24929 }); 24930 24931 parent_block.instructions.appendAssumeCapacity(block_inst); 24932 } 24933 24934 fn panicWithMsg(sema: *Sema, block: *Block, msg_inst: Air.Inst.Ref) !void { 24935 const mod = sema.mod; 24936 24937 if (!mod.backendSupportsFeature(.panic_fn)) { 24938 _ = try block.addNoOp(.trap); 24939 return; 24940 } 24941 24942 try sema.prepareSimplePanic(block); 24943 24944 const panic_func = mod.funcPtrUnwrap(mod.panic_func_index).?; 24945 const panic_fn = try sema.analyzeDeclVal(block, .unneeded, panic_func.owner_decl); 24946 const null_stack_trace = try sema.addConstant(mod.null_stack_trace.toValue()); 24947 24948 const opt_usize_ty = try mod.optionalType(.usize_type); 24949 const null_ret_addr = try sema.addConstant((try mod.intern(.{ .opt = .{ 24950 .ty = opt_usize_ty.toIntern(), 24951 .val = .none, 24952 } })).toValue()); 24953 try sema.callBuiltin(block, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr }); 24954 } 24955 24956 fn panicUnwrapError( 24957 sema: *Sema, 24958 parent_block: *Block, 24959 operand: Air.Inst.Ref, 24960 unwrap_err_tag: Air.Inst.Tag, 24961 is_non_err_tag: Air.Inst.Tag, 24962 ) !void { 24963 assert(!parent_block.is_comptime); 24964 const ok = try parent_block.addUnOp(is_non_err_tag, operand); 24965 if (!sema.mod.comp.formatted_panics) { 24966 return sema.addSafetyCheck(parent_block, ok, .unwrap_error); 24967 } 24968 const gpa = sema.gpa; 24969 24970 var fail_block: Block = .{ 24971 .parent = parent_block, 24972 .sema = sema, 24973 .src_decl = parent_block.src_decl, 24974 .namespace = parent_block.namespace, 24975 .wip_capture_scope = parent_block.wip_capture_scope, 24976 .instructions = .{}, 24977 .inlining = parent_block.inlining, 24978 .is_comptime = false, 24979 }; 24980 24981 defer fail_block.instructions.deinit(gpa); 24982 24983 { 24984 if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) { 24985 _ = try fail_block.addNoOp(.trap); 24986 } else { 24987 const panic_fn = try sema.getBuiltin("panicUnwrapError"); 24988 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); 24989 const err_return_trace = try sema.getErrorReturnTrace(&fail_block); 24990 const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; 24991 try sema.callBuiltin(&fail_block, panic_fn, .auto, &args); 24992 } 24993 } 24994 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 24995 } 24996 24997 fn panicIndexOutOfBounds( 24998 sema: *Sema, 24999 parent_block: *Block, 25000 index: Air.Inst.Ref, 25001 len: Air.Inst.Ref, 25002 cmp_op: Air.Inst.Tag, 25003 ) !void { 25004 assert(!parent_block.is_comptime); 25005 const ok = try parent_block.addBinOp(cmp_op, index, len); 25006 if (!sema.mod.comp.formatted_panics) { 25007 return sema.addSafetyCheck(parent_block, ok, .index_out_of_bounds); 25008 } 25009 try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len }); 25010 } 25011 25012 fn panicInactiveUnionField( 25013 sema: *Sema, 25014 parent_block: *Block, 25015 active_tag: Air.Inst.Ref, 25016 wanted_tag: Air.Inst.Ref, 25017 ) !void { 25018 assert(!parent_block.is_comptime); 25019 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); 25020 if (!sema.mod.comp.formatted_panics) { 25021 return sema.addSafetyCheck(parent_block, ok, .inactive_union_field); 25022 } 25023 try sema.safetyCheckFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag }); 25024 } 25025 25026 fn panicSentinelMismatch( 25027 sema: *Sema, 25028 parent_block: *Block, 25029 maybe_sentinel: ?Value, 25030 sentinel_ty: Type, 25031 ptr: Air.Inst.Ref, 25032 sentinel_index: Air.Inst.Ref, 25033 ) !void { 25034 assert(!parent_block.is_comptime); 25035 const mod = sema.mod; 25036 const expected_sentinel_val = maybe_sentinel orelse return; 25037 const expected_sentinel = try sema.addConstant(expected_sentinel_val); 25038 25039 const ptr_ty = sema.typeOf(ptr); 25040 const actual_sentinel = if (ptr_ty.isSlice(mod)) 25041 try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) 25042 else blk: { 25043 const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null); 25044 const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty); 25045 break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); 25046 }; 25047 25048 const ok = if (sentinel_ty.zigTypeTag(mod) == .Vector) ok: { 25049 const eql = 25050 try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); 25051 break :ok try parent_block.addInst(.{ 25052 .tag = .reduce, 25053 .data = .{ .reduce = .{ 25054 .operand = eql, 25055 .operation = .And, 25056 } }, 25057 }); 25058 } else if (sentinel_ty.isSelfComparable(mod, true)) 25059 try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel) 25060 else { 25061 const panic_fn = try sema.getBuiltin("checkNonScalarSentinel"); 25062 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel }; 25063 try sema.callBuiltin(parent_block, panic_fn, .auto, &args); 25064 return; 25065 }; 25066 25067 if (!sema.mod.comp.formatted_panics) { 25068 return sema.addSafetyCheck(parent_block, ok, .sentinel_mismatch); 25069 } 25070 try sema.safetyCheckFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel }); 25071 } 25072 25073 fn safetyCheckFormatted( 25074 sema: *Sema, 25075 parent_block: *Block, 25076 ok: Air.Inst.Ref, 25077 func: []const u8, 25078 args: []const Air.Inst.Ref, 25079 ) CompileError!void { 25080 assert(sema.mod.comp.formatted_panics); 25081 const gpa = sema.gpa; 25082 25083 var fail_block: Block = .{ 25084 .parent = parent_block, 25085 .sema = sema, 25086 .src_decl = parent_block.src_decl, 25087 .namespace = parent_block.namespace, 25088 .wip_capture_scope = parent_block.wip_capture_scope, 25089 .instructions = .{}, 25090 .inlining = parent_block.inlining, 25091 .is_comptime = false, 25092 }; 25093 25094 defer fail_block.instructions.deinit(gpa); 25095 25096 if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) { 25097 _ = try fail_block.addNoOp(.trap); 25098 } else { 25099 const panic_fn = try sema.getBuiltin(func); 25100 try sema.callBuiltin(&fail_block, panic_fn, .auto, args); 25101 } 25102 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 25103 } 25104 25105 fn safetyPanic(sema: *Sema, block: *Block, panic_id: Module.PanicId) CompileError!void { 25106 const msg_decl_index = try sema.preparePanicId(block, panic_id); 25107 const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index); 25108 try sema.panicWithMsg(block, msg_inst); 25109 } 25110 25111 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { 25112 sema.branch_count += 1; 25113 if (sema.branch_count > sema.branch_quota) { 25114 const msg = try sema.errMsg( 25115 block, 25116 src, 25117 "evaluation exceeded {d} backwards branches", 25118 .{sema.branch_quota}, 25119 ); 25120 try sema.errNote( 25121 block, 25122 src, 25123 msg, 25124 "use @setEvalBranchQuota() to raise the branch limit from {d}", 25125 .{sema.branch_quota}, 25126 ); 25127 return sema.failWithOwnedErrorMsg(msg); 25128 } 25129 } 25130 25131 fn fieldVal( 25132 sema: *Sema, 25133 block: *Block, 25134 src: LazySrcLoc, 25135 object: Air.Inst.Ref, 25136 field_name: InternPool.NullTerminatedString, 25137 field_name_src: LazySrcLoc, 25138 ) CompileError!Air.Inst.Ref { 25139 // When editing this function, note that there is corresponding logic to be edited 25140 // in `fieldPtr`. This function takes a value and returns a value. 25141 25142 const mod = sema.mod; 25143 const ip = &mod.intern_pool; 25144 const object_src = src; // TODO better source location 25145 const object_ty = sema.typeOf(object); 25146 25147 // Zig allows dereferencing a single pointer during field lookup. Note that 25148 // we don't actually need to generate the dereference some field lookups, like the 25149 // length of arrays and other comptime operations. 25150 const is_pointer_to = object_ty.isSinglePointer(mod); 25151 25152 const inner_ty = if (is_pointer_to) 25153 object_ty.childType(mod) 25154 else 25155 object_ty; 25156 25157 switch (inner_ty.zigTypeTag(mod)) { 25158 .Array => { 25159 if (ip.stringEqlSlice(field_name, "len")) { 25160 return sema.addConstant( 25161 try mod.intValue(Type.usize, inner_ty.arrayLen(mod)), 25162 ); 25163 } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) { 25164 const ptr_info = object_ty.ptrInfo(mod); 25165 const result_ty = try mod.ptrType(.{ 25166 .child = ptr_info.child.toType().childType(mod).toIntern(), 25167 .sentinel = ptr_info.sentinel, 25168 .flags = .{ 25169 .size = .Many, 25170 .alignment = ptr_info.flags.alignment, 25171 .is_const = ptr_info.flags.is_const, 25172 .is_volatile = ptr_info.flags.is_volatile, 25173 .is_allowzero = ptr_info.flags.is_allowzero, 25174 .address_space = ptr_info.flags.address_space, 25175 .vector_index = ptr_info.flags.vector_index, 25176 }, 25177 .packed_offset = ptr_info.packed_offset, 25178 }); 25179 return sema.coerce(block, result_ty, object, src); 25180 } else { 25181 return sema.fail( 25182 block, 25183 field_name_src, 25184 "no member named '{}' in '{}'", 25185 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 25186 ); 25187 } 25188 }, 25189 .Pointer => { 25190 const ptr_info = inner_ty.ptrInfo(mod); 25191 if (ptr_info.flags.size == .Slice) { 25192 if (ip.stringEqlSlice(field_name, "ptr")) { 25193 const slice = if (is_pointer_to) 25194 try sema.analyzeLoad(block, src, object, object_src) 25195 else 25196 object; 25197 return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); 25198 } else if (ip.stringEqlSlice(field_name, "len")) { 25199 const slice = if (is_pointer_to) 25200 try sema.analyzeLoad(block, src, object, object_src) 25201 else 25202 object; 25203 return sema.analyzeSliceLen(block, src, slice); 25204 } else { 25205 return sema.fail( 25206 block, 25207 field_name_src, 25208 "no member named '{}' in '{}'", 25209 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 25210 ); 25211 } 25212 } 25213 }, 25214 .Type => { 25215 const dereffed_type = if (is_pointer_to) 25216 try sema.analyzeLoad(block, src, object, object_src) 25217 else 25218 object; 25219 25220 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?; 25221 const child_type = val.toType(); 25222 25223 switch (try child_type.zigTypeTagOrPoison(mod)) { 25224 .ErrorSet => { 25225 switch (ip.indexToKey(child_type.toIntern())) { 25226 .error_set_type => |error_set_type| blk: { 25227 if (error_set_type.nameIndex(ip, field_name) != null) break :blk; 25228 const msg = msg: { 25229 const msg = try sema.errMsg(block, src, "no error named '{}' in '{}'", .{ 25230 field_name.fmt(ip), child_type.fmt(mod), 25231 }); 25232 errdefer msg.destroy(sema.gpa); 25233 try sema.addDeclaredHereNote(msg, child_type); 25234 break :msg msg; 25235 }; 25236 return sema.failWithOwnedErrorMsg(msg); 25237 }, 25238 .inferred_error_set_type => { 25239 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 25240 }, 25241 .simple_type => |t| { 25242 assert(t == .anyerror); 25243 _ = try mod.getErrorValue(field_name); 25244 }, 25245 else => unreachable, 25246 } 25247 25248 const error_set_type = if (!child_type.isAnyError(mod)) 25249 child_type 25250 else 25251 try mod.singleErrorSetType(field_name); 25252 return sema.addConstant((try mod.intern(.{ .err = .{ 25253 .ty = error_set_type.toIntern(), 25254 .name = field_name, 25255 } })).toValue()); 25256 }, 25257 .Union => { 25258 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25259 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 25260 return inst; 25261 } 25262 } 25263 const union_ty = try sema.resolveTypeFields(child_type); 25264 if (union_ty.unionTagType(mod)) |enum_ty| { 25265 if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| { 25266 const field_index = @as(u32, @intCast(field_index_usize)); 25267 return sema.addConstant( 25268 try mod.enumValueFieldIndex(enum_ty, field_index), 25269 ); 25270 } 25271 } 25272 return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name); 25273 }, 25274 .Enum => { 25275 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25276 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 25277 return inst; 25278 } 25279 } 25280 const field_index_usize = child_type.enumFieldIndex(field_name, mod) orelse 25281 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 25282 const field_index = @as(u32, @intCast(field_index_usize)); 25283 const enum_val = try mod.enumValueFieldIndex(child_type, field_index); 25284 return sema.addConstant(enum_val); 25285 }, 25286 .Struct, .Opaque => { 25287 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25288 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 25289 return inst; 25290 } 25291 } 25292 return sema.failWithBadMemberAccess(block, child_type, src, field_name); 25293 }, 25294 else => { 25295 const msg = msg: { 25296 const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(mod)}); 25297 errdefer msg.destroy(sema.gpa); 25298 if (child_type.isSlice(mod)) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{}); 25299 if (child_type.zigTypeTag(mod) == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{}); 25300 break :msg msg; 25301 }; 25302 return sema.failWithOwnedErrorMsg(msg); 25303 }, 25304 } 25305 }, 25306 .Struct => if (is_pointer_to) { 25307 // Avoid loading the entire struct by fetching a pointer and loading that 25308 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 25309 return sema.analyzeLoad(block, src, field_ptr, object_src); 25310 } else { 25311 return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty); 25312 }, 25313 .Union => if (is_pointer_to) { 25314 // Avoid loading the entire union by fetching a pointer and loading that 25315 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 25316 return sema.analyzeLoad(block, src, field_ptr, object_src); 25317 } else { 25318 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty); 25319 }, 25320 else => {}, 25321 } 25322 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 25323 } 25324 25325 fn fieldPtr( 25326 sema: *Sema, 25327 block: *Block, 25328 src: LazySrcLoc, 25329 object_ptr: Air.Inst.Ref, 25330 field_name: InternPool.NullTerminatedString, 25331 field_name_src: LazySrcLoc, 25332 initializing: bool, 25333 ) CompileError!Air.Inst.Ref { 25334 // When editing this function, note that there is corresponding logic to be edited 25335 // in `fieldVal`. This function takes a pointer and returns a pointer. 25336 25337 const mod = sema.mod; 25338 const ip = &mod.intern_pool; 25339 const object_ptr_src = src; // TODO better source location 25340 const object_ptr_ty = sema.typeOf(object_ptr); 25341 const object_ty = switch (object_ptr_ty.zigTypeTag(mod)) { 25342 .Pointer => object_ptr_ty.childType(mod), 25343 else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(mod)}), 25344 }; 25345 25346 // Zig allows dereferencing a single pointer during field lookup. Note that 25347 // we don't actually need to generate the dereference some field lookups, like the 25348 // length of arrays and other comptime operations. 25349 const is_pointer_to = object_ty.isSinglePointer(mod); 25350 25351 const inner_ty = if (is_pointer_to) 25352 object_ty.childType(mod) 25353 else 25354 object_ty; 25355 25356 switch (inner_ty.zigTypeTag(mod)) { 25357 .Array => { 25358 if (ip.stringEqlSlice(field_name, "len")) { 25359 var anon_decl = try block.startAnonDecl(); 25360 defer anon_decl.deinit(); 25361 return sema.analyzeDeclRef(try anon_decl.finish( 25362 Type.usize, 25363 try mod.intValue(Type.usize, inner_ty.arrayLen(mod)), 25364 .none, // default alignment 25365 )); 25366 } else { 25367 return sema.fail( 25368 block, 25369 field_name_src, 25370 "no member named '{}' in '{}'", 25371 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 25372 ); 25373 } 25374 }, 25375 .Pointer => if (inner_ty.isSlice(mod)) { 25376 const inner_ptr = if (is_pointer_to) 25377 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 25378 else 25379 object_ptr; 25380 25381 const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty; 25382 25383 if (ip.stringEqlSlice(field_name, "ptr")) { 25384 const slice_ptr_ty = inner_ty.slicePtrFieldType(mod); 25385 25386 const result_ty = try mod.ptrType(.{ 25387 .child = slice_ptr_ty.toIntern(), 25388 .flags = .{ 25389 .is_const = !attr_ptr_ty.ptrIsMutable(mod), 25390 .is_volatile = attr_ptr_ty.isVolatilePtr(mod), 25391 .address_space = attr_ptr_ty.ptrAddressSpace(mod), 25392 }, 25393 }); 25394 25395 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 25396 return sema.addConstant((try mod.intern(.{ .ptr = .{ 25397 .ty = result_ty.toIntern(), 25398 .addr = .{ .field = .{ 25399 .base = val.toIntern(), 25400 .index = Value.slice_ptr_index, 25401 } }, 25402 } })).toValue()); 25403 } 25404 try sema.requireRuntimeBlock(block, src, null); 25405 25406 return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); 25407 } else if (ip.stringEqlSlice(field_name, "len")) { 25408 const result_ty = try mod.ptrType(.{ 25409 .child = .usize_type, 25410 .flags = .{ 25411 .is_const = !attr_ptr_ty.ptrIsMutable(mod), 25412 .is_volatile = attr_ptr_ty.isVolatilePtr(mod), 25413 .address_space = attr_ptr_ty.ptrAddressSpace(mod), 25414 }, 25415 }); 25416 25417 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 25418 return sema.addConstant((try mod.intern(.{ .ptr = .{ 25419 .ty = result_ty.toIntern(), 25420 .addr = .{ .field = .{ 25421 .base = val.toIntern(), 25422 .index = Value.slice_len_index, 25423 } }, 25424 } })).toValue()); 25425 } 25426 try sema.requireRuntimeBlock(block, src, null); 25427 25428 return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); 25429 } else { 25430 return sema.fail( 25431 block, 25432 field_name_src, 25433 "no member named '{}' in '{}'", 25434 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 25435 ); 25436 } 25437 }, 25438 .Type => { 25439 _ = try sema.resolveConstValue(block, .unneeded, object_ptr, ""); 25440 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); 25441 const inner = if (is_pointer_to) 25442 try sema.analyzeLoad(block, src, result, object_ptr_src) 25443 else 25444 result; 25445 25446 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?; 25447 const child_type = val.toType(); 25448 25449 switch (child_type.zigTypeTag(mod)) { 25450 .ErrorSet => { 25451 switch (ip.indexToKey(child_type.toIntern())) { 25452 .error_set_type => |error_set_type| blk: { 25453 if (error_set_type.nameIndex(ip, field_name) != null) { 25454 break :blk; 25455 } 25456 return sema.fail(block, src, "no error named '{}' in '{}'", .{ 25457 field_name.fmt(ip), child_type.fmt(mod), 25458 }); 25459 }, 25460 .inferred_error_set_type => { 25461 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 25462 }, 25463 .simple_type => |t| { 25464 assert(t == .anyerror); 25465 _ = try mod.getErrorValue(field_name); 25466 }, 25467 else => unreachable, 25468 } 25469 25470 var anon_decl = try block.startAnonDecl(); 25471 defer anon_decl.deinit(); 25472 const error_set_type = if (!child_type.isAnyError(mod)) 25473 child_type 25474 else 25475 try mod.singleErrorSetType(field_name); 25476 return sema.analyzeDeclRef(try anon_decl.finish( 25477 error_set_type, 25478 (try mod.intern(.{ .err = .{ 25479 .ty = error_set_type.toIntern(), 25480 .name = field_name, 25481 } })).toValue(), 25482 .none, // default alignment 25483 )); 25484 }, 25485 .Union => { 25486 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25487 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 25488 return inst; 25489 } 25490 } 25491 const union_ty = try sema.resolveTypeFields(child_type); 25492 if (union_ty.unionTagType(mod)) |enum_ty| { 25493 if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| { 25494 const field_index_u32 = @as(u32, @intCast(field_index)); 25495 var anon_decl = try block.startAnonDecl(); 25496 defer anon_decl.deinit(); 25497 return sema.analyzeDeclRef(try anon_decl.finish( 25498 enum_ty, 25499 try mod.enumValueFieldIndex(enum_ty, field_index_u32), 25500 .none, // default alignment 25501 )); 25502 } 25503 } 25504 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 25505 }, 25506 .Enum => { 25507 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25508 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 25509 return inst; 25510 } 25511 } 25512 const field_index = child_type.enumFieldIndex(field_name, mod) orelse { 25513 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 25514 }; 25515 const field_index_u32 = @as(u32, @intCast(field_index)); 25516 var anon_decl = try block.startAnonDecl(); 25517 defer anon_decl.deinit(); 25518 return sema.analyzeDeclRef(try anon_decl.finish( 25519 child_type, 25520 try mod.enumValueFieldIndex(child_type, field_index_u32), 25521 .none, // default alignment 25522 )); 25523 }, 25524 .Struct, .Opaque => { 25525 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25526 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 25527 return inst; 25528 } 25529 } 25530 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 25531 }, 25532 else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(mod)}), 25533 } 25534 }, 25535 .Struct => { 25536 const inner_ptr = if (is_pointer_to) 25537 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 25538 else 25539 object_ptr; 25540 return sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 25541 }, 25542 .Union => { 25543 const inner_ptr = if (is_pointer_to) 25544 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 25545 else 25546 object_ptr; 25547 return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 25548 }, 25549 else => {}, 25550 } 25551 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 25552 } 25553 25554 const ResolvedFieldCallee = union(enum) { 25555 /// The LHS of the call was an actual field with this value. 25556 direct: Air.Inst.Ref, 25557 /// This is a method call, with the function and first argument given. 25558 method: struct { 25559 func_inst: Air.Inst.Ref, 25560 arg0_inst: Air.Inst.Ref, 25561 }, 25562 }; 25563 25564 fn fieldCallBind( 25565 sema: *Sema, 25566 block: *Block, 25567 src: LazySrcLoc, 25568 raw_ptr: Air.Inst.Ref, 25569 field_name: InternPool.NullTerminatedString, 25570 field_name_src: LazySrcLoc, 25571 ) CompileError!ResolvedFieldCallee { 25572 // When editing this function, note that there is corresponding logic to be edited 25573 // in `fieldVal`. This function takes a pointer and returns a pointer. 25574 25575 const mod = sema.mod; 25576 const ip = &mod.intern_pool; 25577 const raw_ptr_src = src; // TODO better source location 25578 const raw_ptr_ty = sema.typeOf(raw_ptr); 25579 const inner_ty = if (raw_ptr_ty.zigTypeTag(mod) == .Pointer and (raw_ptr_ty.ptrSize(mod) == .One or raw_ptr_ty.ptrSize(mod) == .C)) 25580 raw_ptr_ty.childType(mod) 25581 else 25582 return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(mod)}); 25583 25584 // Optionally dereference a second pointer to get the concrete type. 25585 const is_double_ptr = inner_ty.zigTypeTag(mod) == .Pointer and inner_ty.ptrSize(mod) == .One; 25586 const concrete_ty = if (is_double_ptr) inner_ty.childType(mod) else inner_ty; 25587 const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; 25588 const object_ptr = if (is_double_ptr) 25589 try sema.analyzeLoad(block, src, raw_ptr, src) 25590 else 25591 raw_ptr; 25592 25593 find_field: { 25594 switch (concrete_ty.zigTypeTag(mod)) { 25595 .Struct => { 25596 const struct_ty = try sema.resolveTypeFields(concrete_ty); 25597 if (mod.typeToStruct(struct_ty)) |struct_obj| { 25598 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse 25599 break :find_field; 25600 const field_index = @as(u32, @intCast(field_index_usize)); 25601 const field = struct_obj.fields.values()[field_index]; 25602 25603 return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr); 25604 } else if (struct_ty.isTuple(mod)) { 25605 if (ip.stringEqlSlice(field_name, "len")) { 25606 return .{ .direct = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod)) }; 25607 } 25608 if (field_name.toUnsigned(ip)) |field_index| { 25609 if (field_index >= struct_ty.structFieldCount(mod)) break :find_field; 25610 return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(field_index, mod), field_index, object_ptr); 25611 } 25612 } else { 25613 const max = struct_ty.structFieldCount(mod); 25614 for (0..max) |i_usize| { 25615 const i = @as(u32, @intCast(i_usize)); 25616 if (field_name == struct_ty.structFieldName(i, mod)) { 25617 return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(i, mod), i, object_ptr); 25618 } 25619 } 25620 } 25621 }, 25622 .Union => { 25623 const union_ty = try sema.resolveTypeFields(concrete_ty); 25624 const fields = union_ty.unionFields(mod); 25625 const field_index_usize = fields.getIndex(field_name) orelse break :find_field; 25626 const field_index = @as(u32, @intCast(field_index_usize)); 25627 const field = fields.values()[field_index]; 25628 25629 return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr); 25630 }, 25631 .Type => { 25632 const namespace = try sema.analyzeLoad(block, src, object_ptr, src); 25633 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) }; 25634 }, 25635 else => {}, 25636 } 25637 } 25638 25639 // If we get here, we need to look for a decl in the struct type instead. 25640 const found_decl = switch (concrete_ty.zigTypeTag(mod)) { 25641 .Struct, .Opaque, .Union, .Enum => found_decl: { 25642 if (concrete_ty.getNamespaceIndex(mod).unwrap()) |namespace| { 25643 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| { 25644 try sema.addReferencedBy(block, src, decl_idx); 25645 const decl_val = try sema.analyzeDeclVal(block, src, decl_idx); 25646 const decl_type = sema.typeOf(decl_val); 25647 if (mod.typeToFunc(decl_type)) |func_type| f: { 25648 if (func_type.param_types.len == 0) break :f; 25649 25650 const first_param_type = func_type.param_types[0].toType(); 25651 // zig fmt: off 25652 if (first_param_type.isGenericPoison() or ( 25653 first_param_type.zigTypeTag(mod) == .Pointer and 25654 (first_param_type.ptrSize(mod) == .One or 25655 first_param_type.ptrSize(mod) == .C) and 25656 first_param_type.childType(mod).eql(concrete_ty, mod))) 25657 { 25658 // zig fmt: on 25659 // Note that if the param type is generic poison, we know that it must 25660 // specifically be `anytype` since it's the first parameter, meaning we 25661 // can safely assume it can be a pointer. 25662 // TODO: bound fn calls on rvalues should probably 25663 // generate a by-value argument somehow. 25664 return .{ .method = .{ 25665 .func_inst = decl_val, 25666 .arg0_inst = object_ptr, 25667 } }; 25668 } else if (first_param_type.eql(concrete_ty, mod)) { 25669 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 25670 return .{ .method = .{ 25671 .func_inst = decl_val, 25672 .arg0_inst = deref, 25673 } }; 25674 } else if (first_param_type.zigTypeTag(mod) == .Optional) { 25675 const child = first_param_type.optionalChild(mod); 25676 if (child.eql(concrete_ty, mod)) { 25677 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 25678 return .{ .method = .{ 25679 .func_inst = decl_val, 25680 .arg0_inst = deref, 25681 } }; 25682 } else if (child.zigTypeTag(mod) == .Pointer and 25683 child.ptrSize(mod) == .One and 25684 child.childType(mod).eql(concrete_ty, mod)) 25685 { 25686 return .{ .method = .{ 25687 .func_inst = decl_val, 25688 .arg0_inst = object_ptr, 25689 } }; 25690 } 25691 } else if (first_param_type.zigTypeTag(mod) == .ErrorUnion and 25692 first_param_type.errorUnionPayload(mod).eql(concrete_ty, mod)) 25693 { 25694 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 25695 return .{ .method = .{ 25696 .func_inst = decl_val, 25697 .arg0_inst = deref, 25698 } }; 25699 } 25700 } 25701 break :found_decl decl_idx; 25702 } 25703 } 25704 break :found_decl null; 25705 }, 25706 else => null, 25707 }; 25708 25709 const msg = msg: { 25710 const msg = try sema.errMsg(block, src, "no field or member function named '{}' in '{}'", .{ 25711 field_name.fmt(ip), 25712 concrete_ty.fmt(mod), 25713 }); 25714 errdefer msg.destroy(sema.gpa); 25715 try sema.addDeclaredHereNote(msg, concrete_ty); 25716 if (found_decl) |decl_idx| { 25717 const decl = mod.declPtr(decl_idx); 25718 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{}' is not a member function", .{field_name.fmt(ip)}); 25719 } 25720 break :msg msg; 25721 }; 25722 return sema.failWithOwnedErrorMsg(msg); 25723 } 25724 25725 fn finishFieldCallBind( 25726 sema: *Sema, 25727 block: *Block, 25728 src: LazySrcLoc, 25729 ptr_ty: Type, 25730 field_ty: Type, 25731 field_index: u32, 25732 object_ptr: Air.Inst.Ref, 25733 ) CompileError!ResolvedFieldCallee { 25734 const mod = sema.mod; 25735 const ptr_field_ty = try mod.ptrType(.{ 25736 .child = field_ty.toIntern(), 25737 .flags = .{ 25738 .is_const = !ptr_ty.ptrIsMutable(mod), 25739 .address_space = ptr_ty.ptrAddressSpace(mod), 25740 }, 25741 }); 25742 25743 const container_ty = ptr_ty.childType(mod); 25744 if (container_ty.zigTypeTag(mod) == .Struct) { 25745 if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| { 25746 return .{ .direct = try sema.addConstant(default_val) }; 25747 } 25748 } 25749 25750 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| { 25751 const pointer = try sema.addConstant((try mod.intern(.{ .ptr = .{ 25752 .ty = ptr_field_ty.toIntern(), 25753 .addr = .{ .field = .{ 25754 .base = struct_ptr_val.toIntern(), 25755 .index = field_index, 25756 } }, 25757 } })).toValue()); 25758 return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) }; 25759 } 25760 25761 try sema.requireRuntimeBlock(block, src, null); 25762 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); 25763 return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) }; 25764 } 25765 25766 fn namespaceLookup( 25767 sema: *Sema, 25768 block: *Block, 25769 src: LazySrcLoc, 25770 namespace: Namespace.Index, 25771 decl_name: InternPool.NullTerminatedString, 25772 ) CompileError!?Decl.Index { 25773 const mod = sema.mod; 25774 const gpa = sema.gpa; 25775 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { 25776 const decl = mod.declPtr(decl_index); 25777 if (!decl.is_pub and decl.getFileScope(mod) != block.getFileScope(mod)) { 25778 const msg = msg: { 25779 const msg = try sema.errMsg(block, src, "'{}' is not marked 'pub'", .{ 25780 decl_name.fmt(&mod.intern_pool), 25781 }); 25782 errdefer msg.destroy(gpa); 25783 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "declared here", .{}); 25784 break :msg msg; 25785 }; 25786 return sema.failWithOwnedErrorMsg(msg); 25787 } 25788 return decl_index; 25789 } 25790 return null; 25791 } 25792 25793 fn namespaceLookupRef( 25794 sema: *Sema, 25795 block: *Block, 25796 src: LazySrcLoc, 25797 namespace: Namespace.Index, 25798 decl_name: InternPool.NullTerminatedString, 25799 ) CompileError!?Air.Inst.Ref { 25800 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; 25801 try sema.addReferencedBy(block, src, decl); 25802 return try sema.analyzeDeclRef(decl); 25803 } 25804 25805 fn namespaceLookupVal( 25806 sema: *Sema, 25807 block: *Block, 25808 src: LazySrcLoc, 25809 namespace: Namespace.Index, 25810 decl_name: InternPool.NullTerminatedString, 25811 ) CompileError!?Air.Inst.Ref { 25812 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; 25813 return try sema.analyzeDeclVal(block, src, decl); 25814 } 25815 25816 fn structFieldPtr( 25817 sema: *Sema, 25818 block: *Block, 25819 src: LazySrcLoc, 25820 struct_ptr: Air.Inst.Ref, 25821 field_name: InternPool.NullTerminatedString, 25822 field_name_src: LazySrcLoc, 25823 unresolved_struct_ty: Type, 25824 initializing: bool, 25825 ) CompileError!Air.Inst.Ref { 25826 const mod = sema.mod; 25827 assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); 25828 25829 const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); 25830 try sema.resolveStructLayout(struct_ty); 25831 25832 if (struct_ty.isTuple(mod)) { 25833 if (mod.intern_pool.stringEqlSlice(field_name, "len")) { 25834 const len_inst = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod)); 25835 return sema.analyzeRef(block, src, len_inst); 25836 } 25837 const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); 25838 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); 25839 } else if (struct_ty.isAnonStruct(mod)) { 25840 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); 25841 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); 25842 } 25843 25844 const struct_obj = mod.typeToStruct(struct_ty).?; 25845 25846 const field_index_big = struct_obj.fields.getIndex(field_name) orelse 25847 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); 25848 const field_index = @as(u32, @intCast(field_index_big)); 25849 25850 return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty, initializing); 25851 } 25852 25853 fn structFieldPtrByIndex( 25854 sema: *Sema, 25855 block: *Block, 25856 src: LazySrcLoc, 25857 struct_ptr: Air.Inst.Ref, 25858 field_index: u32, 25859 field_src: LazySrcLoc, 25860 struct_ty: Type, 25861 initializing: bool, 25862 ) CompileError!Air.Inst.Ref { 25863 const mod = sema.mod; 25864 if (struct_ty.isAnonStruct(mod)) { 25865 return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing); 25866 } 25867 25868 const struct_obj = mod.typeToStruct(struct_ty).?; 25869 const field = struct_obj.fields.values()[field_index]; 25870 const struct_ptr_ty = sema.typeOf(struct_ptr); 25871 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); 25872 25873 var ptr_ty_data: InternPool.Key.PtrType = .{ 25874 .child = field.ty.toIntern(), 25875 .flags = .{ 25876 .is_const = struct_ptr_ty_info.flags.is_const, 25877 .is_volatile = struct_ptr_ty_info.flags.is_volatile, 25878 .address_space = struct_ptr_ty_info.flags.address_space, 25879 }, 25880 }; 25881 25882 const target = mod.getTarget(); 25883 25884 const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse 25885 try sema.typeAbiAlignment(struct_ptr_ty_info.child.toType()); 25886 25887 if (struct_obj.layout == .Packed) { 25888 comptime assert(Type.packed_struct_layout_version == 2); 25889 25890 var running_bits: u16 = 0; 25891 for (struct_obj.fields.values(), 0..) |f, i| { 25892 if (!(try sema.typeHasRuntimeBits(f.ty))) continue; 25893 25894 if (i == field_index) { 25895 ptr_ty_data.packed_offset.bit_offset = running_bits; 25896 } 25897 running_bits += @as(u16, @intCast(f.ty.bitSize(mod))); 25898 } 25899 ptr_ty_data.packed_offset.host_size = (running_bits + 7) / 8; 25900 25901 // If this is a packed struct embedded in another one, we need to offset 25902 // the bits against each other. 25903 if (struct_ptr_ty_info.packed_offset.host_size != 0) { 25904 ptr_ty_data.packed_offset.host_size = struct_ptr_ty_info.packed_offset.host_size; 25905 ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset; 25906 } 25907 25908 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align); 25909 25910 // If the field happens to be byte-aligned, simplify the pointer type. 25911 // The pointee type bit size must match its ABI byte size so that loads and stores 25912 // do not interfere with the surrounding packed bits. 25913 // We do not attempt this with big-endian targets yet because of nested 25914 // structs and floats. I need to double-check the desired behavior for big endian 25915 // targets before adding the necessary complications to this code. This will not 25916 // cause miscompilations; it only means the field pointer uses bit masking when it 25917 // might not be strictly necessary. 25918 if (parent_align != 0 and ptr_ty_data.packed_offset.bit_offset % 8 == 0 and 25919 target.cpu.arch.endian() == .Little) 25920 { 25921 const elem_size_bytes = ptr_ty_data.child.toType().abiSize(mod); 25922 const elem_size_bits = ptr_ty_data.child.toType().bitSize(mod); 25923 if (elem_size_bytes * 8 == elem_size_bits) { 25924 const byte_offset = ptr_ty_data.packed_offset.bit_offset / 8; 25925 const new_align = @as(Alignment, @enumFromInt(@ctz(byte_offset | parent_align))); 25926 assert(new_align != .none); 25927 ptr_ty_data.flags.alignment = new_align; 25928 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 }; 25929 } 25930 } 25931 } else if (struct_obj.layout == .Extern) { 25932 // For extern structs, field aligment might be bigger than type's natural alignment. Eg, in 25933 // `extern struct { x: u32, y: u16 }` the second field is aligned as u32. 25934 const field_offset = struct_ty.structFieldOffset(field_index, mod); 25935 ptr_ty_data.flags.alignment = Alignment.fromByteUnits( 25936 if (parent_align == 0) 0 else std.math.gcd(field_offset, parent_align), 25937 ); 25938 } else { 25939 // Our alignment is capped at the field alignment 25940 const field_align = try sema.structFieldAlignment(field, struct_obj.layout); 25941 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(@min(field_align, parent_align)); 25942 } 25943 25944 const ptr_field_ty = try mod.ptrType(ptr_ty_data); 25945 25946 if (field.is_comptime) { 25947 const val = try mod.intern(.{ .ptr = .{ 25948 .ty = ptr_field_ty.toIntern(), 25949 .addr = .{ .comptime_field = field.default_val }, 25950 } }); 25951 return sema.addConstant(val.toValue()); 25952 } 25953 25954 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { 25955 const val = try mod.intern(.{ .ptr = .{ 25956 .ty = ptr_field_ty.toIntern(), 25957 .addr = .{ .field = .{ 25958 .base = try struct_ptr_val.intern(struct_ptr_ty, mod), 25959 .index = field_index, 25960 } }, 25961 } }); 25962 return sema.addConstant(val.toValue()); 25963 } 25964 25965 try sema.requireRuntimeBlock(block, src, null); 25966 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); 25967 } 25968 25969 fn structFieldVal( 25970 sema: *Sema, 25971 block: *Block, 25972 src: LazySrcLoc, 25973 struct_byval: Air.Inst.Ref, 25974 field_name: InternPool.NullTerminatedString, 25975 field_name_src: LazySrcLoc, 25976 unresolved_struct_ty: Type, 25977 ) CompileError!Air.Inst.Ref { 25978 const mod = sema.mod; 25979 assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); 25980 25981 const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); 25982 switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) { 25983 .struct_type => |struct_type| { 25984 const struct_obj = mod.structPtrUnwrap(struct_type.index).?; 25985 if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); 25986 25987 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse 25988 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); 25989 const field_index = @as(u32, @intCast(field_index_usize)); 25990 const field = struct_obj.fields.values()[field_index]; 25991 25992 if (field.is_comptime) { 25993 return sema.addConstant(field.default_val.toValue()); 25994 } 25995 25996 if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| { 25997 if (struct_val.isUndef(mod)) return sema.addConstUndef(field.ty); 25998 if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| { 25999 return sema.addConstant(opv); 26000 } 26001 return sema.addConstant(try struct_val.fieldValue(mod, field_index)); 26002 } 26003 26004 try sema.requireRuntimeBlock(block, src, null); 26005 return block.addStructFieldVal(struct_byval, field_index, field.ty); 26006 }, 26007 .anon_struct_type => |anon_struct| { 26008 if (anon_struct.names.len == 0) { 26009 return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); 26010 } else { 26011 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); 26012 return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty); 26013 } 26014 }, 26015 else => unreachable, 26016 } 26017 } 26018 26019 fn tupleFieldVal( 26020 sema: *Sema, 26021 block: *Block, 26022 src: LazySrcLoc, 26023 tuple_byval: Air.Inst.Ref, 26024 field_name: InternPool.NullTerminatedString, 26025 field_name_src: LazySrcLoc, 26026 tuple_ty: Type, 26027 ) CompileError!Air.Inst.Ref { 26028 const mod = sema.mod; 26029 if (mod.intern_pool.stringEqlSlice(field_name, "len")) { 26030 return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount(mod)); 26031 } 26032 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src); 26033 return sema.tupleFieldValByIndex(block, src, tuple_byval, field_index, tuple_ty); 26034 } 26035 26036 /// Asserts that `field_name` is not "len". 26037 fn tupleFieldIndex( 26038 sema: *Sema, 26039 block: *Block, 26040 tuple_ty: Type, 26041 field_name: InternPool.NullTerminatedString, 26042 field_name_src: LazySrcLoc, 26043 ) CompileError!u32 { 26044 const mod = sema.mod; 26045 assert(!mod.intern_pool.stringEqlSlice(field_name, "len")); 26046 if (field_name.toUnsigned(&mod.intern_pool)) |field_index| { 26047 if (field_index < tuple_ty.structFieldCount(mod)) return field_index; 26048 return sema.fail(block, field_name_src, "index '{}' out of bounds of tuple '{}'", .{ 26049 field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), 26050 }); 26051 } 26052 26053 return sema.fail(block, field_name_src, "no field named '{}' in tuple '{}'", .{ 26054 field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), 26055 }); 26056 } 26057 26058 fn tupleFieldValByIndex( 26059 sema: *Sema, 26060 block: *Block, 26061 src: LazySrcLoc, 26062 tuple_byval: Air.Inst.Ref, 26063 field_index: u32, 26064 tuple_ty: Type, 26065 ) CompileError!Air.Inst.Ref { 26066 const mod = sema.mod; 26067 const field_ty = tuple_ty.structFieldType(field_index, mod); 26068 26069 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| { 26070 return sema.addConstant(default_value); 26071 } 26072 26073 if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| { 26074 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { 26075 return sema.addConstant(opv); 26076 } 26077 return switch (mod.intern_pool.indexToKey(tuple_val.toIntern())) { 26078 .undef => sema.addConstUndef(field_ty), 26079 .aggregate => |aggregate| sema.addConstant(switch (aggregate.storage) { 26080 .bytes => |bytes| try mod.intValue(Type.u8, bytes[0]), 26081 .elems => |elems| elems[field_index].toValue(), 26082 .repeated_elem => |elem| elem.toValue(), 26083 }), 26084 else => unreachable, 26085 }; 26086 } 26087 26088 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| { 26089 return sema.addConstant(default_val); 26090 } 26091 26092 try sema.requireRuntimeBlock(block, src, null); 26093 return block.addStructFieldVal(tuple_byval, field_index, field_ty); 26094 } 26095 26096 fn unionFieldPtr( 26097 sema: *Sema, 26098 block: *Block, 26099 src: LazySrcLoc, 26100 union_ptr: Air.Inst.Ref, 26101 field_name: InternPool.NullTerminatedString, 26102 field_name_src: LazySrcLoc, 26103 unresolved_union_ty: Type, 26104 initializing: bool, 26105 ) CompileError!Air.Inst.Ref { 26106 const mod = sema.mod; 26107 const ip = &mod.intern_pool; 26108 26109 assert(unresolved_union_ty.zigTypeTag(mod) == .Union); 26110 26111 const union_ptr_ty = sema.typeOf(union_ptr); 26112 const union_ptr_info = union_ptr_ty.ptrInfo(mod); 26113 const union_ty = try sema.resolveTypeFields(unresolved_union_ty); 26114 const union_obj = mod.typeToUnion(union_ty).?; 26115 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 26116 const field = union_obj.fields.values()[field_index]; 26117 const ptr_field_ty = try mod.ptrType(.{ 26118 .child = field.ty.toIntern(), 26119 .flags = .{ 26120 .is_const = union_ptr_info.flags.is_const, 26121 .is_volatile = union_ptr_info.flags.is_volatile, 26122 .address_space = union_ptr_info.flags.address_space, 26123 .alignment = if (union_obj.layout == .Auto) blk: { 26124 const union_align = union_ptr_info.flags.alignment.toByteUnitsOptional() orelse try sema.typeAbiAlignment(union_ty); 26125 const field_align = try sema.unionFieldAlignment(field); 26126 break :blk InternPool.Alignment.fromByteUnits(@min(union_align, field_align)); 26127 } else union_ptr_info.flags.alignment, 26128 }, 26129 .packed_offset = union_ptr_info.packed_offset, 26130 }); 26131 const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?)); 26132 26133 if (initializing and field.ty.zigTypeTag(mod) == .NoReturn) { 26134 const msg = msg: { 26135 const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{}); 26136 errdefer msg.destroy(sema.gpa); 26137 26138 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 26139 field_name.fmt(ip), 26140 }); 26141 try sema.addDeclaredHereNote(msg, union_ty); 26142 break :msg msg; 26143 }; 26144 return sema.failWithOwnedErrorMsg(msg); 26145 } 26146 26147 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { 26148 switch (union_obj.layout) { 26149 .Auto => if (!initializing) { 26150 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse 26151 break :ct; 26152 if (union_val.isUndef(mod)) { 26153 return sema.failWithUseOfUndef(block, src); 26154 } 26155 const un = ip.indexToKey(union_val.toIntern()).un; 26156 const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); 26157 const tag_matches = un.tag == field_tag.toIntern(); 26158 if (!tag_matches) { 26159 const msg = msg: { 26160 const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?; 26161 const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); 26162 const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ 26163 field_name.fmt(ip), 26164 active_field_name.fmt(ip), 26165 }); 26166 errdefer msg.destroy(sema.gpa); 26167 try sema.addDeclaredHereNote(msg, union_ty); 26168 break :msg msg; 26169 }; 26170 return sema.failWithOwnedErrorMsg(msg); 26171 } 26172 }, 26173 .Packed, .Extern => {}, 26174 } 26175 return sema.addConstant((try mod.intern(.{ .ptr = .{ 26176 .ty = ptr_field_ty.toIntern(), 26177 .addr = .{ .field = .{ 26178 .base = union_ptr_val.toIntern(), 26179 .index = field_index, 26180 } }, 26181 } })).toValue()); 26182 } 26183 26184 try sema.requireRuntimeBlock(block, src, null); 26185 if (!initializing and union_obj.layout == .Auto and block.wantSafety() and 26186 union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) 26187 { 26188 const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); 26189 const wanted_tag = try sema.addConstant(wanted_tag_val); 26190 // TODO would it be better if get_union_tag supported pointers to unions? 26191 const union_val = try block.addTyOp(.load, union_ty, union_ptr); 26192 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val); 26193 try sema.panicInactiveUnionField(block, active_tag, wanted_tag); 26194 } 26195 if (field.ty.zigTypeTag(mod) == .NoReturn) { 26196 _ = try block.addNoOp(.unreach); 26197 return Air.Inst.Ref.unreachable_value; 26198 } 26199 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); 26200 } 26201 26202 fn unionFieldVal( 26203 sema: *Sema, 26204 block: *Block, 26205 src: LazySrcLoc, 26206 union_byval: Air.Inst.Ref, 26207 field_name: InternPool.NullTerminatedString, 26208 field_name_src: LazySrcLoc, 26209 unresolved_union_ty: Type, 26210 ) CompileError!Air.Inst.Ref { 26211 const mod = sema.mod; 26212 const ip = &mod.intern_pool; 26213 assert(unresolved_union_ty.zigTypeTag(mod) == .Union); 26214 26215 const union_ty = try sema.resolveTypeFields(unresolved_union_ty); 26216 const union_obj = mod.typeToUnion(union_ty).?; 26217 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 26218 const field = union_obj.fields.values()[field_index]; 26219 const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?)); 26220 26221 if (try sema.resolveMaybeUndefVal(union_byval)) |union_val| { 26222 if (union_val.isUndef(mod)) return sema.addConstUndef(field.ty); 26223 26224 const un = ip.indexToKey(union_val.toIntern()).un; 26225 const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); 26226 const tag_matches = un.tag == field_tag.toIntern(); 26227 switch (union_obj.layout) { 26228 .Auto => { 26229 if (tag_matches) { 26230 return sema.addConstant(un.val.toValue()); 26231 } else { 26232 const msg = msg: { 26233 const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?; 26234 const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); 26235 const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ 26236 field_name.fmt(ip), active_field_name.fmt(ip), 26237 }); 26238 errdefer msg.destroy(sema.gpa); 26239 try sema.addDeclaredHereNote(msg, union_ty); 26240 break :msg msg; 26241 }; 26242 return sema.failWithOwnedErrorMsg(msg); 26243 } 26244 }, 26245 .Packed, .Extern => { 26246 if (tag_matches) { 26247 return sema.addConstant(un.val.toValue()); 26248 } else { 26249 const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod); 26250 if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field.ty, 0)) |new_val| { 26251 return sema.addConstant(new_val); 26252 } 26253 } 26254 }, 26255 } 26256 } 26257 26258 try sema.requireRuntimeBlock(block, src, null); 26259 if (union_obj.layout == .Auto and block.wantSafety() and 26260 union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) 26261 { 26262 const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); 26263 const wanted_tag = try sema.addConstant(wanted_tag_val); 26264 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval); 26265 try sema.panicInactiveUnionField(block, active_tag, wanted_tag); 26266 } 26267 if (field.ty.zigTypeTag(mod) == .NoReturn) { 26268 _ = try block.addNoOp(.unreach); 26269 return Air.Inst.Ref.unreachable_value; 26270 } 26271 return block.addStructFieldVal(union_byval, field_index, field.ty); 26272 } 26273 26274 fn elemPtr( 26275 sema: *Sema, 26276 block: *Block, 26277 src: LazySrcLoc, 26278 indexable_ptr: Air.Inst.Ref, 26279 elem_index: Air.Inst.Ref, 26280 elem_index_src: LazySrcLoc, 26281 init: bool, 26282 oob_safety: bool, 26283 ) CompileError!Air.Inst.Ref { 26284 const mod = sema.mod; 26285 const indexable_ptr_src = src; // TODO better source location 26286 const indexable_ptr_ty = sema.typeOf(indexable_ptr); 26287 26288 const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(mod)) { 26289 .Pointer => indexable_ptr_ty.childType(mod), 26290 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(mod)}), 26291 }; 26292 try checkIndexable(sema, block, src, indexable_ty); 26293 26294 switch (indexable_ty.zigTypeTag(mod)) { 26295 .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), 26296 .Struct => { 26297 // Tuple field access. 26298 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); 26299 const index = @as(u32, @intCast(index_val.toUnsignedInt(mod))); 26300 return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); 26301 }, 26302 else => { 26303 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); 26304 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); 26305 }, 26306 } 26307 } 26308 26309 /// Asserts that the type of indexable is pointer. 26310 fn elemPtrOneLayerOnly( 26311 sema: *Sema, 26312 block: *Block, 26313 src: LazySrcLoc, 26314 indexable: Air.Inst.Ref, 26315 elem_index: Air.Inst.Ref, 26316 elem_index_src: LazySrcLoc, 26317 init: bool, 26318 oob_safety: bool, 26319 ) CompileError!Air.Inst.Ref { 26320 const indexable_src = src; // TODO better source location 26321 const indexable_ty = sema.typeOf(indexable); 26322 const mod = sema.mod; 26323 26324 try checkIndexable(sema, block, src, indexable_ty); 26325 26326 switch (indexable_ty.ptrSize(mod)) { 26327 .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 26328 .Many, .C => { 26329 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 26330 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 26331 const runtime_src = rs: { 26332 const ptr_val = maybe_ptr_val orelse break :rs indexable_src; 26333 const index_val = maybe_index_val orelse break :rs elem_index_src; 26334 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26335 const result_ty = try sema.elemPtrType(indexable_ty, index); 26336 const elem_ptr = try ptr_val.elemPtr(result_ty, index, mod); 26337 return sema.addConstant(elem_ptr); 26338 }; 26339 const result_ty = try sema.elemPtrType(indexable_ty, null); 26340 26341 try sema.requireRuntimeBlock(block, src, runtime_src); 26342 return block.addPtrElemPtr(indexable, elem_index, result_ty); 26343 }, 26344 .One => { 26345 const child_ty = indexable_ty.childType(mod); 26346 switch (child_ty.zigTypeTag(mod)) { 26347 .Array, .Vector => { 26348 return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety); 26349 }, 26350 .Struct => { 26351 assert(child_ty.isTuple(mod)); 26352 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); 26353 const index = @as(u32, @intCast(index_val.toUnsignedInt(mod))); 26354 return sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); 26355 }, 26356 else => unreachable, // Guaranteed by checkIndexable 26357 } 26358 }, 26359 } 26360 } 26361 26362 fn elemVal( 26363 sema: *Sema, 26364 block: *Block, 26365 src: LazySrcLoc, 26366 indexable: Air.Inst.Ref, 26367 elem_index_uncasted: Air.Inst.Ref, 26368 elem_index_src: LazySrcLoc, 26369 oob_safety: bool, 26370 ) CompileError!Air.Inst.Ref { 26371 const indexable_src = src; // TODO better source location 26372 const indexable_ty = sema.typeOf(indexable); 26373 const mod = sema.mod; 26374 26375 try checkIndexable(sema, block, src, indexable_ty); 26376 26377 // TODO in case of a vector of pointers, we need to detect whether the element 26378 // index is a scalar or vector instead of unconditionally casting to usize. 26379 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src); 26380 26381 switch (indexable_ty.zigTypeTag(mod)) { 26382 .Pointer => switch (indexable_ty.ptrSize(mod)) { 26383 .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 26384 .Many, .C => { 26385 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 26386 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 26387 26388 const runtime_src = rs: { 26389 const indexable_val = maybe_indexable_val orelse break :rs indexable_src; 26390 const index_val = maybe_index_val orelse break :rs elem_index_src; 26391 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26392 const elem_ty = indexable_ty.elemType2(mod); 26393 const many_ptr_ty = try mod.manyConstPtrType(elem_ty); 26394 const many_ptr_val = try mod.getCoerced(indexable_val, many_ptr_ty); 26395 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty); 26396 const elem_ptr_val = try many_ptr_val.elemPtr(elem_ptr_ty, index, mod); 26397 if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { 26398 return sema.addConstant(try mod.getCoerced(elem_val, elem_ty)); 26399 } 26400 break :rs indexable_src; 26401 }; 26402 26403 try sema.requireRuntimeBlock(block, src, runtime_src); 26404 return block.addBinOp(.ptr_elem_val, indexable, elem_index); 26405 }, 26406 .One => { 26407 arr_sent: { 26408 const inner_ty = indexable_ty.childType(mod); 26409 if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent; 26410 const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent; 26411 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; 26412 const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod)); 26413 if (index != inner_ty.arrayLen(mod)) break :arr_sent; 26414 return sema.addConstant(sentinel); 26415 } 26416 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); 26417 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); 26418 }, 26419 }, 26420 .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 26421 .Vector => { 26422 // TODO: If the index is a vector, the result should be a vector. 26423 return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety); 26424 }, 26425 .Struct => { 26426 // Tuple field access. 26427 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); 26428 const index = @as(u32, @intCast(index_val.toUnsignedInt(mod))); 26429 return sema.tupleField(block, indexable_src, indexable, elem_index_src, index); 26430 }, 26431 else => unreachable, 26432 } 26433 } 26434 26435 fn validateRuntimeElemAccess( 26436 sema: *Sema, 26437 block: *Block, 26438 elem_index_src: LazySrcLoc, 26439 elem_ty: Type, 26440 parent_ty: Type, 26441 parent_src: LazySrcLoc, 26442 ) CompileError!void { 26443 const mod = sema.mod; 26444 const valid_rt = try sema.validateRunTimeType(elem_ty, false); 26445 if (!valid_rt) { 26446 const msg = msg: { 26447 const msg = try sema.errMsg( 26448 block, 26449 elem_index_src, 26450 "values of type '{}' must be comptime-known, but index value is runtime-known", 26451 .{parent_ty.fmt(mod)}, 26452 ); 26453 errdefer msg.destroy(sema.gpa); 26454 26455 const src_decl = mod.declPtr(block.src_decl); 26456 try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty); 26457 26458 break :msg msg; 26459 }; 26460 return sema.failWithOwnedErrorMsg(msg); 26461 } 26462 } 26463 26464 fn tupleFieldPtr( 26465 sema: *Sema, 26466 block: *Block, 26467 tuple_ptr_src: LazySrcLoc, 26468 tuple_ptr: Air.Inst.Ref, 26469 field_index_src: LazySrcLoc, 26470 field_index: u32, 26471 init: bool, 26472 ) CompileError!Air.Inst.Ref { 26473 const mod = sema.mod; 26474 const tuple_ptr_ty = sema.typeOf(tuple_ptr); 26475 const tuple_ty = tuple_ptr_ty.childType(mod); 26476 _ = try sema.resolveTypeFields(tuple_ty); 26477 const field_count = tuple_ty.structFieldCount(mod); 26478 26479 if (field_count == 0) { 26480 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); 26481 } 26482 26483 if (field_index >= field_count) { 26484 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 26485 field_index, field_count, 26486 }); 26487 } 26488 26489 const field_ty = tuple_ty.structFieldType(field_index, mod); 26490 const ptr_field_ty = try mod.ptrType(.{ 26491 .child = field_ty.toIntern(), 26492 .flags = .{ 26493 .is_const = !tuple_ptr_ty.ptrIsMutable(mod), 26494 .is_volatile = tuple_ptr_ty.isVolatilePtr(mod), 26495 .address_space = tuple_ptr_ty.ptrAddressSpace(mod), 26496 }, 26497 }); 26498 26499 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| { 26500 return sema.addConstant((try mod.intern(.{ .ptr = .{ 26501 .ty = ptr_field_ty.toIntern(), 26502 .addr = .{ .comptime_field = default_val.toIntern() }, 26503 } })).toValue()); 26504 } 26505 26506 if (try sema.resolveMaybeUndefVal(tuple_ptr)) |tuple_ptr_val| { 26507 return sema.addConstant((try mod.intern(.{ .ptr = .{ 26508 .ty = ptr_field_ty.toIntern(), 26509 .addr = .{ .field = .{ 26510 .base = tuple_ptr_val.toIntern(), 26511 .index = field_index, 26512 } }, 26513 } })).toValue()); 26514 } 26515 26516 if (!init) { 26517 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src); 26518 } 26519 26520 try sema.requireRuntimeBlock(block, tuple_ptr_src, null); 26521 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); 26522 } 26523 26524 fn tupleField( 26525 sema: *Sema, 26526 block: *Block, 26527 tuple_src: LazySrcLoc, 26528 tuple: Air.Inst.Ref, 26529 field_index_src: LazySrcLoc, 26530 field_index: u32, 26531 ) CompileError!Air.Inst.Ref { 26532 const mod = sema.mod; 26533 const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple)); 26534 const field_count = tuple_ty.structFieldCount(mod); 26535 26536 if (field_count == 0) { 26537 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); 26538 } 26539 26540 if (field_index >= field_count) { 26541 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 26542 field_index, field_count, 26543 }); 26544 } 26545 26546 const field_ty = tuple_ty.structFieldType(field_index, mod); 26547 26548 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| { 26549 return sema.addConstant(default_value); // comptime field 26550 } 26551 26552 if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| { 26553 if (tuple_val.isUndef(mod)) return sema.addConstUndef(field_ty); 26554 return sema.addConstant(try tuple_val.fieldValue(mod, field_index)); 26555 } 26556 26557 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); 26558 26559 try sema.requireRuntimeBlock(block, tuple_src, null); 26560 return block.addStructFieldVal(tuple, field_index, field_ty); 26561 } 26562 26563 fn elemValArray( 26564 sema: *Sema, 26565 block: *Block, 26566 src: LazySrcLoc, 26567 array_src: LazySrcLoc, 26568 array: Air.Inst.Ref, 26569 elem_index_src: LazySrcLoc, 26570 elem_index: Air.Inst.Ref, 26571 oob_safety: bool, 26572 ) CompileError!Air.Inst.Ref { 26573 const mod = sema.mod; 26574 const array_ty = sema.typeOf(array); 26575 const array_sent = array_ty.sentinel(mod); 26576 const array_len = array_ty.arrayLen(mod); 26577 const array_len_s = array_len + @intFromBool(array_sent != null); 26578 const elem_ty = array_ty.childType(mod); 26579 26580 if (array_len_s == 0) { 26581 return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); 26582 } 26583 26584 const maybe_undef_array_val = try sema.resolveMaybeUndefVal(array); 26585 // index must be defined since it can access out of bounds 26586 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 26587 26588 if (maybe_index_val) |index_val| { 26589 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26590 if (array_sent) |s| { 26591 if (index == array_len) { 26592 return sema.addConstant(s); 26593 } 26594 } 26595 if (index >= array_len_s) { 26596 const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else ""; 26597 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 26598 } 26599 } 26600 if (maybe_undef_array_val) |array_val| { 26601 if (array_val.isUndef(mod)) { 26602 return sema.addConstUndef(elem_ty); 26603 } 26604 if (maybe_index_val) |index_val| { 26605 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26606 const elem_val = try array_val.elemValue(mod, index); 26607 return sema.addConstant(elem_val); 26608 } 26609 } 26610 26611 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src); 26612 26613 const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src; 26614 try sema.requireRuntimeBlock(block, src, runtime_src); 26615 if (oob_safety and block.wantSafety()) { 26616 // Runtime check is only needed if unable to comptime check 26617 if (maybe_index_val == null) { 26618 const len_inst = try sema.addIntUnsigned(Type.usize, array_len); 26619 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt; 26620 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); 26621 } 26622 } 26623 return block.addBinOp(.array_elem_val, array, elem_index); 26624 } 26625 26626 fn elemPtrArray( 26627 sema: *Sema, 26628 block: *Block, 26629 src: LazySrcLoc, 26630 array_ptr_src: LazySrcLoc, 26631 array_ptr: Air.Inst.Ref, 26632 elem_index_src: LazySrcLoc, 26633 elem_index: Air.Inst.Ref, 26634 init: bool, 26635 oob_safety: bool, 26636 ) CompileError!Air.Inst.Ref { 26637 const mod = sema.mod; 26638 const array_ptr_ty = sema.typeOf(array_ptr); 26639 const array_ty = array_ptr_ty.childType(mod); 26640 const array_sent = array_ty.sentinel(mod) != null; 26641 const array_len = array_ty.arrayLen(mod); 26642 const array_len_s = array_len + @intFromBool(array_sent); 26643 26644 if (array_len_s == 0) { 26645 return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{}); 26646 } 26647 26648 const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(array_ptr); 26649 // The index must not be undefined since it can be out of bounds. 26650 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 26651 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod)); 26652 if (index >= array_len_s) { 26653 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; 26654 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 26655 } 26656 break :o index; 26657 } else null; 26658 26659 const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset); 26660 26661 if (maybe_undef_array_ptr_val) |array_ptr_val| { 26662 if (array_ptr_val.isUndef(mod)) { 26663 return sema.addConstUndef(elem_ptr_ty); 26664 } 26665 if (offset) |index| { 26666 const elem_ptr = try array_ptr_val.elemPtr(elem_ptr_ty, index, mod); 26667 return sema.addConstant(elem_ptr); 26668 } 26669 } 26670 26671 if (!init) { 26672 try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(mod), array_ty, array_ptr_src); 26673 } 26674 26675 const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; 26676 try sema.requireRuntimeBlock(block, src, runtime_src); 26677 26678 // Runtime check is only needed if unable to comptime check. 26679 if (oob_safety and block.wantSafety() and offset == null) { 26680 const len_inst = try sema.addIntUnsigned(Type.usize, array_len); 26681 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; 26682 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); 26683 } 26684 26685 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); 26686 } 26687 26688 fn elemValSlice( 26689 sema: *Sema, 26690 block: *Block, 26691 src: LazySrcLoc, 26692 slice_src: LazySrcLoc, 26693 slice: Air.Inst.Ref, 26694 elem_index_src: LazySrcLoc, 26695 elem_index: Air.Inst.Ref, 26696 oob_safety: bool, 26697 ) CompileError!Air.Inst.Ref { 26698 const mod = sema.mod; 26699 const slice_ty = sema.typeOf(slice); 26700 const slice_sent = slice_ty.sentinel(mod) != null; 26701 const elem_ty = slice_ty.elemType2(mod); 26702 var runtime_src = slice_src; 26703 26704 // slice must be defined since it can dereferenced as null 26705 const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); 26706 // index must be defined since it can index out of bounds 26707 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 26708 26709 if (maybe_slice_val) |slice_val| { 26710 runtime_src = elem_index_src; 26711 const slice_len = slice_val.sliceLen(mod); 26712 const slice_len_s = slice_len + @intFromBool(slice_sent); 26713 if (slice_len_s == 0) { 26714 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 26715 } 26716 if (maybe_index_val) |index_val| { 26717 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26718 if (index >= slice_len_s) { 26719 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 26720 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 26721 } 26722 const elem_ptr_ty = try sema.elemPtrType(slice_ty, index); 26723 const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod); 26724 if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { 26725 return sema.addConstant(elem_val); 26726 } 26727 runtime_src = slice_src; 26728 } 26729 } 26730 26731 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src); 26732 26733 try sema.requireRuntimeBlock(block, src, runtime_src); 26734 if (oob_safety and block.wantSafety()) { 26735 const len_inst = if (maybe_slice_val) |slice_val| 26736 try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)) 26737 else 26738 try block.addTyOp(.slice_len, Type.usize, slice); 26739 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 26740 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); 26741 } 26742 try sema.queueFullTypeResolution(sema.typeOf(slice)); 26743 return block.addBinOp(.slice_elem_val, slice, elem_index); 26744 } 26745 26746 fn elemPtrSlice( 26747 sema: *Sema, 26748 block: *Block, 26749 src: LazySrcLoc, 26750 slice_src: LazySrcLoc, 26751 slice: Air.Inst.Ref, 26752 elem_index_src: LazySrcLoc, 26753 elem_index: Air.Inst.Ref, 26754 oob_safety: bool, 26755 ) CompileError!Air.Inst.Ref { 26756 const mod = sema.mod; 26757 const slice_ty = sema.typeOf(slice); 26758 const slice_sent = slice_ty.sentinel(mod) != null; 26759 26760 const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(slice); 26761 // The index must not be undefined since it can be out of bounds. 26762 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 26763 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod)); 26764 break :o index; 26765 } else null; 26766 26767 const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset); 26768 26769 if (maybe_undef_slice_val) |slice_val| { 26770 if (slice_val.isUndef(mod)) { 26771 return sema.addConstUndef(elem_ptr_ty); 26772 } 26773 const slice_len = slice_val.sliceLen(mod); 26774 const slice_len_s = slice_len + @intFromBool(slice_sent); 26775 if (slice_len_s == 0) { 26776 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 26777 } 26778 if (offset) |index| { 26779 if (index >= slice_len_s) { 26780 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 26781 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 26782 } 26783 const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod); 26784 return sema.addConstant(elem_ptr_val); 26785 } 26786 } 26787 26788 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src); 26789 26790 const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src; 26791 try sema.requireRuntimeBlock(block, src, runtime_src); 26792 if (oob_safety and block.wantSafety()) { 26793 const len_inst = len: { 26794 if (maybe_undef_slice_val) |slice_val| 26795 if (!slice_val.isUndef(mod)) 26796 break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)); 26797 break :len try block.addTyOp(.slice_len, Type.usize, slice); 26798 }; 26799 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 26800 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); 26801 } 26802 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); 26803 } 26804 26805 fn coerce( 26806 sema: *Sema, 26807 block: *Block, 26808 dest_ty_unresolved: Type, 26809 inst: Air.Inst.Ref, 26810 inst_src: LazySrcLoc, 26811 ) CompileError!Air.Inst.Ref { 26812 return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) { 26813 error.NotCoercible => unreachable, 26814 else => |e| return e, 26815 }; 26816 } 26817 26818 const CoersionError = CompileError || error{ 26819 /// When coerce is called recursively, this error should be returned instead of using `fail` 26820 /// to ensure correct types in compile errors. 26821 NotCoercible, 26822 }; 26823 26824 const CoerceOpts = struct { 26825 /// Should coerceExtra emit error messages. 26826 report_err: bool = true, 26827 /// Ignored if `report_err == false`. 26828 is_ret: bool = false, 26829 /// Should coercion to comptime_int ermit an error message. 26830 no_cast_to_comptime_int: bool = false, 26831 26832 param_src: struct { 26833 func_inst: Air.Inst.Ref = .none, 26834 param_i: u32 = undefined, 26835 26836 fn get(info: @This(), sema: *Sema) !?Module.SrcLoc { 26837 if (info.func_inst == .none) return null; 26838 const mod = sema.mod; 26839 const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null; 26840 const param_src = Module.paramSrc(0, mod, fn_decl, info.param_i); 26841 if (param_src == .node_offset_param) { 26842 return Module.SrcLoc{ 26843 .file_scope = fn_decl.getFileScope(mod), 26844 .parent_decl_node = fn_decl.src_node, 26845 .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param), 26846 }; 26847 } 26848 return param_src.toSrcLoc(fn_decl, mod); 26849 } 26850 } = .{}, 26851 }; 26852 26853 fn coerceExtra( 26854 sema: *Sema, 26855 block: *Block, 26856 dest_ty_unresolved: Type, 26857 inst: Air.Inst.Ref, 26858 inst_src: LazySrcLoc, 26859 opts: CoerceOpts, 26860 ) CoersionError!Air.Inst.Ref { 26861 if (dest_ty_unresolved.isGenericPoison()) return inst; 26862 const mod = sema.mod; 26863 const dest_ty_src = inst_src; // TODO better source location 26864 const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); 26865 const inst_ty = try sema.resolveTypeFields(sema.typeOf(inst)); 26866 const target = mod.getTarget(); 26867 // If the types are the same, we can return the operand. 26868 if (dest_ty.eql(inst_ty, mod)) 26869 return inst; 26870 26871 const maybe_inst_val = try sema.resolveMaybeUndefVal(inst); 26872 26873 var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); 26874 if (in_memory_result == .ok) { 26875 if (maybe_inst_val) |val| { 26876 return sema.coerceInMemory(val, dest_ty); 26877 } 26878 try sema.requireRuntimeBlock(block, inst_src, null); 26879 return block.addBitCast(dest_ty, inst); 26880 } 26881 26882 const is_undef = inst_ty.zigTypeTag(mod) == .Undefined; 26883 26884 switch (dest_ty.zigTypeTag(mod)) { 26885 .Optional => optional: { 26886 // undefined sets the optional bit also to undefined. 26887 if (is_undef) { 26888 return sema.addConstUndef(dest_ty); 26889 } 26890 26891 // null to ?T 26892 if (inst_ty.zigTypeTag(mod) == .Null) { 26893 return sema.addConstant((try mod.intern(.{ .opt = .{ 26894 .ty = dest_ty.toIntern(), 26895 .val = .none, 26896 } })).toValue()); 26897 } 26898 26899 // cast from ?*T and ?[*]T to ?*anyopaque 26900 // but don't do it if the source type is a double pointer 26901 if (dest_ty.isPtrLikeOptional(mod) and 26902 dest_ty.elemType2(mod).toIntern() == .anyopaque_type and 26903 inst_ty.isPtrAtRuntime(mod)) 26904 anyopaque_check: { 26905 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; 26906 const elem_ty = inst_ty.elemType2(mod); 26907 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) { 26908 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 26909 .actual = inst_ty, 26910 .wanted = dest_ty, 26911 } }; 26912 break :optional; 26913 } 26914 // Let the logic below handle wrapping the optional now that 26915 // it has been checked to correctly coerce. 26916 if (!inst_ty.isPtrLikeOptional(mod)) break :anyopaque_check; 26917 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 26918 } 26919 26920 // T to ?T 26921 const child_type = dest_ty.optionalChild(mod); 26922 const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 26923 error.NotCoercible => { 26924 if (in_memory_result == .no_match) { 26925 // Try to give more useful notes 26926 in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src); 26927 } 26928 break :optional; 26929 }, 26930 else => |e| return e, 26931 }; 26932 return try sema.wrapOptional(block, dest_ty, intermediate, inst_src); 26933 }, 26934 .Pointer => pointer: { 26935 const dest_info = dest_ty.ptrInfo(mod); 26936 26937 // Function body to function pointer. 26938 if (inst_ty.zigTypeTag(mod) == .Fn) { 26939 const fn_val = try sema.resolveConstValue(block, .unneeded, inst, ""); 26940 const fn_decl = fn_val.pointerDecl(mod).?; 26941 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl); 26942 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); 26943 } 26944 26945 // *T to *[1]T 26946 single_item: { 26947 if (dest_info.flags.size != .One) break :single_item; 26948 if (!inst_ty.isSinglePointer(mod)) break :single_item; 26949 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 26950 const ptr_elem_ty = inst_ty.childType(mod); 26951 const array_ty = dest_info.child.toType(); 26952 if (array_ty.zigTypeTag(mod) != .Array) break :single_item; 26953 const array_elem_ty = array_ty.childType(mod); 26954 if (array_ty.arrayLen(mod) != 1) break :single_item; 26955 const dest_is_mut = !dest_info.flags.is_const; 26956 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { 26957 .ok => {}, 26958 else => break :single_item, 26959 } 26960 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 26961 } 26962 26963 // Coercions where the source is a single pointer to an array. 26964 src_array_ptr: { 26965 if (!inst_ty.isSinglePointer(mod)) break :src_array_ptr; 26966 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 26967 const array_ty = inst_ty.childType(mod); 26968 if (array_ty.zigTypeTag(mod) != .Array) break :src_array_ptr; 26969 const array_elem_type = array_ty.childType(mod); 26970 const dest_is_mut = !dest_info.flags.is_const; 26971 26972 const dst_elem_type = dest_info.child.toType(); 26973 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src); 26974 switch (elem_res) { 26975 .ok => {}, 26976 else => { 26977 in_memory_result = .{ .ptr_child = .{ 26978 .child = try elem_res.dupe(sema.arena), 26979 .actual = array_elem_type, 26980 .wanted = dst_elem_type, 26981 } }; 26982 break :src_array_ptr; 26983 }, 26984 } 26985 26986 if (dest_info.sentinel != .none) { 26987 if (array_ty.sentinel(mod)) |inst_sent| { 26988 if (dest_info.sentinel != (try mod.getCoerced(inst_sent, dst_elem_type)).toIntern()) { 26989 in_memory_result = .{ .ptr_sentinel = .{ 26990 .actual = inst_sent, 26991 .wanted = dest_info.sentinel.toValue(), 26992 .ty = dst_elem_type, 26993 } }; 26994 break :src_array_ptr; 26995 } 26996 } else { 26997 in_memory_result = .{ .ptr_sentinel = .{ 26998 .actual = Value.@"unreachable", 26999 .wanted = dest_info.sentinel.toValue(), 27000 .ty = dst_elem_type, 27001 } }; 27002 break :src_array_ptr; 27003 } 27004 } 27005 27006 switch (dest_info.flags.size) { 27007 .Slice => { 27008 // *[N]T to []T 27009 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src); 27010 }, 27011 .C => { 27012 // *[N]T to [*c]T 27013 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27014 }, 27015 .Many => { 27016 // *[N]T to [*]T 27017 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27018 }, 27019 .One => {}, 27020 } 27021 } 27022 27023 // coercion from C pointer 27024 if (inst_ty.isCPtr(mod)) src_c_ptr: { 27025 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr; 27026 // In this case we must add a safety check because the C pointer 27027 // could be null. 27028 const src_elem_ty = inst_ty.childType(mod); 27029 const dest_is_mut = !dest_info.flags.is_const; 27030 const dst_elem_type = dest_info.child.toType(); 27031 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { 27032 .ok => {}, 27033 else => break :src_c_ptr, 27034 } 27035 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27036 } 27037 27038 // cast from *T and [*]T to *anyopaque 27039 // but don't do it if the source type is a double pointer 27040 if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(mod) == .Pointer) to_anyopaque: { 27041 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 27042 const elem_ty = inst_ty.elemType2(mod); 27043 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) { 27044 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 27045 .actual = inst_ty, 27046 .wanted = dest_ty, 27047 } }; 27048 break :pointer; 27049 } 27050 if (dest_ty.isSlice(mod)) break :to_anyopaque; 27051 if (inst_ty.isSlice(mod)) { 27052 in_memory_result = .{ .slice_to_anyopaque = .{ 27053 .actual = inst_ty, 27054 .wanted = dest_ty, 27055 } }; 27056 break :pointer; 27057 } 27058 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27059 } 27060 27061 switch (dest_info.flags.size) { 27062 // coercion to C pointer 27063 .C => switch (inst_ty.zigTypeTag(mod)) { 27064 .Null => { 27065 return sema.addConstant(try mod.getCoerced(Value.null, dest_ty)); 27066 }, 27067 .ComptimeInt => { 27068 const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 27069 error.NotCoercible => break :pointer, 27070 else => |e| return e, 27071 }; 27072 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 27073 }, 27074 .Int => { 27075 const ptr_size_ty = switch (inst_ty.intInfo(mod).signedness) { 27076 .signed => Type.isize, 27077 .unsigned => Type.usize, 27078 }; 27079 const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 27080 error.NotCoercible => { 27081 // Try to give more useful notes 27082 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src); 27083 break :pointer; 27084 }, 27085 else => |e| return e, 27086 }; 27087 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 27088 }, 27089 .Pointer => p: { 27090 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 27091 const inst_info = inst_ty.ptrInfo(mod); 27092 switch (try sema.coerceInMemoryAllowed( 27093 block, 27094 dest_info.child.toType(), 27095 inst_info.child.toType(), 27096 !dest_info.flags.is_const, 27097 target, 27098 dest_ty_src, 27099 inst_src, 27100 )) { 27101 .ok => {}, 27102 else => break :p, 27103 } 27104 if (inst_info.flags.size == .Slice) { 27105 assert(dest_info.sentinel == .none); 27106 if (inst_info.sentinel == .none or 27107 inst_info.sentinel != (try mod.intValue(inst_info.child.toType(), 0)).toIntern()) 27108 break :p; 27109 27110 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 27111 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 27112 } 27113 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27114 }, 27115 else => {}, 27116 }, 27117 .One => switch (dest_info.child.toType().zigTypeTag(mod)) { 27118 .Union => { 27119 // pointer to anonymous struct to pointer to union 27120 if (inst_ty.isSinglePointer(mod) and 27121 inst_ty.childType(mod).isAnonStruct(mod) and 27122 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 27123 { 27124 return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src); 27125 } 27126 }, 27127 .Struct => { 27128 // pointer to anonymous struct to pointer to struct 27129 if (inst_ty.isSinglePointer(mod) and 27130 inst_ty.childType(mod).isAnonStruct(mod) and 27131 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 27132 { 27133 return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) { 27134 error.NotCoercible => break :pointer, 27135 else => |e| return e, 27136 }; 27137 } 27138 }, 27139 .Array => { 27140 // pointer to tuple to pointer to array 27141 if (inst_ty.isSinglePointer(mod) and 27142 inst_ty.childType(mod).isTuple(mod) and 27143 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 27144 { 27145 return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src); 27146 } 27147 }, 27148 else => {}, 27149 }, 27150 .Slice => to_slice: { 27151 if (inst_ty.zigTypeTag(mod) == .Array) { 27152 return sema.fail( 27153 block, 27154 inst_src, 27155 "array literal requires address-of operator (&) to coerce to slice type '{}'", 27156 .{dest_ty.fmt(mod)}, 27157 ); 27158 } 27159 27160 if (!inst_ty.isSinglePointer(mod)) break :to_slice; 27161 const inst_child_ty = inst_ty.childType(mod); 27162 if (!inst_child_ty.isTuple(mod)) break :to_slice; 27163 27164 // empty tuple to zero-length slice 27165 // note that this allows coercing to a mutable slice. 27166 if (inst_child_ty.structFieldCount(mod) == 0) { 27167 // Optional slice is represented with a null pointer so 27168 // we use a dummy pointer value with the required alignment. 27169 return sema.addConstant((try mod.intern(.{ .ptr = .{ 27170 .ty = dest_ty.toIntern(), 27171 .addr = .{ .int = (if (dest_info.flags.alignment != .none) 27172 try mod.intValue(Type.usize, dest_info.flags.alignment.toByteUnitsOptional().?) 27173 else 27174 try mod.getCoerced(try dest_info.child.toType().lazyAbiAlignment(mod), Type.usize)).toIntern() }, 27175 .len = (try mod.intValue(Type.usize, 0)).toIntern(), 27176 } })).toValue()); 27177 } 27178 27179 // pointer to tuple to slice 27180 if (!dest_info.flags.is_const) { 27181 const err_msg = err_msg: { 27182 const err_msg = try sema.errMsg(block, inst_src, "cannot cast pointer to tuple to '{}'", .{dest_ty.fmt(mod)}); 27183 errdefer err_msg.deinit(sema.gpa); 27184 try sema.errNote(block, dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{}); 27185 break :err_msg err_msg; 27186 }; 27187 return sema.failWithOwnedErrorMsg(err_msg); 27188 } 27189 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); 27190 }, 27191 .Many => p: { 27192 if (!inst_ty.isSlice(mod)) break :p; 27193 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 27194 const inst_info = inst_ty.ptrInfo(mod); 27195 27196 switch (try sema.coerceInMemoryAllowed( 27197 block, 27198 dest_info.child.toType(), 27199 inst_info.child.toType(), 27200 !dest_info.flags.is_const, 27201 target, 27202 dest_ty_src, 27203 inst_src, 27204 )) { 27205 .ok => {}, 27206 else => break :p, 27207 } 27208 27209 if (dest_info.sentinel == .none or inst_info.sentinel == .none or 27210 dest_info.sentinel != 27211 try mod.intern_pool.getCoerced(sema.gpa, inst_info.sentinel, dest_info.child)) 27212 break :p; 27213 27214 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 27215 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 27216 }, 27217 } 27218 }, 27219 .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) { 27220 .Float, .ComptimeFloat => float: { 27221 if (is_undef) { 27222 return sema.addConstUndef(dest_ty); 27223 } 27224 const val = (try sema.resolveMaybeUndefVal(inst)) orelse { 27225 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { 27226 if (!opts.report_err) return error.NotCoercible; 27227 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known"); 27228 } 27229 break :float; 27230 }; 27231 27232 if (val.floatHasFraction(mod)) { 27233 return sema.fail( 27234 block, 27235 inst_src, 27236 "fractional component prevents float value '{}' from coercion to type '{}'", 27237 .{ val.fmtValue(inst_ty, mod), dest_ty.fmt(mod) }, 27238 ); 27239 } 27240 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty); 27241 return try sema.addConstant(result_val); 27242 }, 27243 .Int, .ComptimeInt => { 27244 if (is_undef) { 27245 return sema.addConstUndef(dest_ty); 27246 } 27247 if (try sema.resolveMaybeUndefVal(inst)) |val| { 27248 // comptime-known integer to other number 27249 if (!(try sema.intFitsInType(val, dest_ty, null))) { 27250 if (!opts.report_err) return error.NotCoercible; 27251 return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) }); 27252 } 27253 return try sema.addConstant(try mod.getCoerced(val, dest_ty)); 27254 } 27255 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { 27256 if (!opts.report_err) return error.NotCoercible; 27257 if (opts.no_cast_to_comptime_int) return inst; 27258 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known"); 27259 } 27260 27261 // integer widening 27262 const dst_info = dest_ty.intInfo(mod); 27263 const src_info = inst_ty.intInfo(mod); 27264 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or 27265 // small enough unsigned ints can get casted to large enough signed ints 27266 (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) 27267 { 27268 try sema.requireRuntimeBlock(block, inst_src, null); 27269 return block.addTyOp(.intcast, dest_ty, inst); 27270 } 27271 }, 27272 .Undefined => { 27273 return sema.addConstUndef(dest_ty); 27274 }, 27275 else => {}, 27276 }, 27277 .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) { 27278 .ComptimeFloat => { 27279 const val = try sema.resolveConstValue(block, .unneeded, inst, ""); 27280 const result_val = try val.floatCast(dest_ty, mod); 27281 return try sema.addConstant(result_val); 27282 }, 27283 .Float => { 27284 if (is_undef) { 27285 return sema.addConstUndef(dest_ty); 27286 } 27287 if (try sema.resolveMaybeUndefVal(inst)) |val| { 27288 const result_val = try val.floatCast(dest_ty, mod); 27289 if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) { 27290 return sema.fail( 27291 block, 27292 inst_src, 27293 "type '{}' cannot represent float value '{}'", 27294 .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) }, 27295 ); 27296 } 27297 return try sema.addConstant(result_val); 27298 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { 27299 if (!opts.report_err) return error.NotCoercible; 27300 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known"); 27301 } 27302 27303 // float widening 27304 const src_bits = inst_ty.floatBits(target); 27305 const dst_bits = dest_ty.floatBits(target); 27306 if (dst_bits >= src_bits) { 27307 try sema.requireRuntimeBlock(block, inst_src, null); 27308 return block.addTyOp(.fpext, dest_ty, inst); 27309 } 27310 }, 27311 .Int, .ComptimeInt => int: { 27312 if (is_undef) { 27313 return sema.addConstUndef(dest_ty); 27314 } 27315 const val = (try sema.resolveMaybeUndefVal(inst)) orelse { 27316 if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { 27317 if (!opts.report_err) return error.NotCoercible; 27318 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known"); 27319 } 27320 break :int; 27321 }; 27322 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, mod, sema); 27323 // TODO implement this compile error 27324 //const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty); 27325 //if (!int_again_val.eql(val, inst_ty, mod)) { 27326 // return sema.fail( 27327 // block, 27328 // inst_src, 27329 // "type '{}' cannot represent integer value '{}'", 27330 // .{ dest_ty.fmt(mod), val }, 27331 // ); 27332 //} 27333 return try sema.addConstant(result_val); 27334 }, 27335 .Undefined => { 27336 return sema.addConstUndef(dest_ty); 27337 }, 27338 else => {}, 27339 }, 27340 .Enum => switch (inst_ty.zigTypeTag(mod)) { 27341 .EnumLiteral => { 27342 // enum literal to enum 27343 const val = try sema.resolveConstValue(block, .unneeded, inst, ""); 27344 const string = mod.intern_pool.indexToKey(val.toIntern()).enum_literal; 27345 const field_index = dest_ty.enumFieldIndex(string, mod) orelse { 27346 const msg = msg: { 27347 const msg = try sema.errMsg( 27348 block, 27349 inst_src, 27350 "no field named '{}' in enum '{}'", 27351 .{ string.fmt(&mod.intern_pool), dest_ty.fmt(mod) }, 27352 ); 27353 errdefer msg.destroy(sema.gpa); 27354 try sema.addDeclaredHereNote(msg, dest_ty); 27355 break :msg msg; 27356 }; 27357 return sema.failWithOwnedErrorMsg(msg); 27358 }; 27359 return sema.addConstant( 27360 try mod.enumValueFieldIndex(dest_ty, @as(u32, @intCast(field_index))), 27361 ); 27362 }, 27363 .Union => blk: { 27364 // union to its own tag type 27365 const union_tag_ty = inst_ty.unionTagType(mod) orelse break :blk; 27366 if (union_tag_ty.eql(dest_ty, mod)) { 27367 return sema.unionToTag(block, dest_ty, inst, inst_src); 27368 } 27369 }, 27370 .Undefined => { 27371 return sema.addConstUndef(dest_ty); 27372 }, 27373 else => {}, 27374 }, 27375 .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) { 27376 .ErrorUnion => eu: { 27377 if (maybe_inst_val) |inst_val| { 27378 switch (inst_val.toIntern()) { 27379 .undef => return sema.addConstUndef(dest_ty), 27380 else => switch (mod.intern_pool.indexToKey(inst_val.toIntern())) { 27381 .error_union => |error_union| switch (error_union.val) { 27382 .err_name => |err_name| { 27383 const error_set_ty = inst_ty.errorUnionSet(mod); 27384 const error_set_val = try sema.addConstant((try mod.intern(.{ .err = .{ 27385 .ty = error_set_ty.toIntern(), 27386 .name = err_name, 27387 } })).toValue()); 27388 return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src); 27389 }, 27390 .payload => |payload| { 27391 const payload_val = try sema.addConstant( 27392 payload.toValue(), 27393 ); 27394 return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) { 27395 error.NotCoercible => break :eu, 27396 else => |e| return e, 27397 }; 27398 }, 27399 }, 27400 else => unreachable, 27401 }, 27402 } 27403 } 27404 }, 27405 .ErrorSet => { 27406 // E to E!T 27407 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src); 27408 }, 27409 .Undefined => { 27410 return sema.addConstUndef(dest_ty); 27411 }, 27412 else => eu: { 27413 // T to E!T 27414 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) { 27415 error.NotCoercible => break :eu, 27416 else => |e| return e, 27417 }; 27418 }, 27419 }, 27420 .Union => switch (inst_ty.zigTypeTag(mod)) { 27421 .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), 27422 .Struct => { 27423 if (inst_ty.isAnonStruct(mod)) { 27424 return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src); 27425 } 27426 }, 27427 .Undefined => { 27428 return sema.addConstUndef(dest_ty); 27429 }, 27430 else => {}, 27431 }, 27432 .Array => switch (inst_ty.zigTypeTag(mod)) { 27433 .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 27434 .Struct => { 27435 if (inst == .empty_struct) { 27436 return sema.arrayInitEmpty(block, inst_src, dest_ty); 27437 } 27438 if (inst_ty.isTuple(mod)) { 27439 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 27440 } 27441 }, 27442 .Undefined => { 27443 return sema.addConstUndef(dest_ty); 27444 }, 27445 else => {}, 27446 }, 27447 .Vector => switch (inst_ty.zigTypeTag(mod)) { 27448 .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 27449 .Struct => { 27450 if (inst_ty.isTuple(mod)) { 27451 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 27452 } 27453 }, 27454 .Undefined => { 27455 return sema.addConstUndef(dest_ty); 27456 }, 27457 else => {}, 27458 }, 27459 .Struct => blk: { 27460 if (inst == .empty_struct) { 27461 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src); 27462 } 27463 if (inst_ty.isTupleOrAnonStruct(mod)) { 27464 return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) { 27465 error.NotCoercible => break :blk, 27466 else => |e| return e, 27467 }; 27468 } 27469 }, 27470 else => {}, 27471 } 27472 27473 // undefined to anything. We do this after the big switch above so that 27474 // special logic has a chance to run first, such as `*[N]T` to `[]T` which 27475 // should initialize the length field of the slice. 27476 if (is_undef) { 27477 return sema.addConstUndef(dest_ty); 27478 } 27479 27480 if (!opts.report_err) return error.NotCoercible; 27481 27482 if (opts.is_ret and dest_ty.zigTypeTag(mod) == .NoReturn) { 27483 const msg = msg: { 27484 const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{}); 27485 errdefer msg.destroy(sema.gpa); 27486 27487 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 27488 const src_decl = mod.declPtr(sema.func.?.owner_decl); 27489 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{}); 27490 break :msg msg; 27491 }; 27492 return sema.failWithOwnedErrorMsg(msg); 27493 } 27494 27495 const msg = msg: { 27496 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), inst_ty.fmt(mod) }); 27497 errdefer msg.destroy(sema.gpa); 27498 27499 // E!T to T 27500 if (inst_ty.zigTypeTag(mod) == .ErrorUnion and 27501 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) 27502 { 27503 try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{}); 27504 try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); 27505 } 27506 27507 // ?T to T 27508 if (inst_ty.zigTypeTag(mod) == .Optional and 27509 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) 27510 { 27511 try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{}); 27512 try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 27513 } 27514 27515 try in_memory_result.report(sema, block, inst_src, msg); 27516 27517 // Add notes about function return type 27518 if (opts.is_ret and mod.test_functions.get(sema.func.?.owner_decl) == null) { 27519 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 27520 const src_decl = mod.declPtr(sema.func.?.owner_decl); 27521 if (inst_ty.isError(mod) and !dest_ty.isError(mod)) { 27522 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{}); 27523 } else { 27524 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{}); 27525 } 27526 } 27527 27528 if (try opts.param_src.get(sema)) |param_src| { 27529 try mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{}); 27530 } 27531 27532 // TODO maybe add "cannot store an error in type '{}'" note 27533 27534 break :msg msg; 27535 }; 27536 return sema.failWithOwnedErrorMsg(msg); 27537 } 27538 27539 fn coerceInMemory( 27540 sema: *Sema, 27541 val: Value, 27542 dst_ty: Type, 27543 ) CompileError!Air.Inst.Ref { 27544 return sema.addConstant(try sema.mod.getCoerced(val, dst_ty)); 27545 } 27546 27547 const InMemoryCoercionResult = union(enum) { 27548 ok, 27549 no_match: Pair, 27550 int_not_coercible: Int, 27551 error_union_payload: PairAndChild, 27552 array_len: IntPair, 27553 array_sentinel: Sentinel, 27554 array_elem: PairAndChild, 27555 vector_len: IntPair, 27556 vector_elem: PairAndChild, 27557 optional_shape: Pair, 27558 optional_child: PairAndChild, 27559 from_anyerror, 27560 missing_error: []const InternPool.NullTerminatedString, 27561 /// true if wanted is var args 27562 fn_var_args: bool, 27563 /// true if wanted is generic 27564 fn_generic: bool, 27565 fn_param_count: IntPair, 27566 fn_param_noalias: IntPair, 27567 fn_param_comptime: ComptimeParam, 27568 fn_param: Param, 27569 fn_cc: CC, 27570 fn_return_type: PairAndChild, 27571 ptr_child: PairAndChild, 27572 ptr_addrspace: AddressSpace, 27573 ptr_sentinel: Sentinel, 27574 ptr_size: Size, 27575 ptr_qualifiers: Qualifiers, 27576 ptr_allowzero: Pair, 27577 ptr_bit_range: BitRange, 27578 ptr_alignment: IntPair, 27579 double_ptr_to_anyopaque: Pair, 27580 slice_to_anyopaque: Pair, 27581 27582 const Pair = struct { 27583 actual: Type, 27584 wanted: Type, 27585 }; 27586 27587 const PairAndChild = struct { 27588 child: *InMemoryCoercionResult, 27589 actual: Type, 27590 wanted: Type, 27591 }; 27592 27593 const Param = struct { 27594 child: *InMemoryCoercionResult, 27595 actual: Type, 27596 wanted: Type, 27597 index: u64, 27598 }; 27599 27600 const ComptimeParam = struct { 27601 index: u64, 27602 wanted: bool, 27603 }; 27604 27605 const Sentinel = struct { 27606 // unreachable_value indicates no sentinel 27607 actual: Value, 27608 wanted: Value, 27609 ty: Type, 27610 }; 27611 27612 const Int = struct { 27613 actual_signedness: std.builtin.Signedness, 27614 wanted_signedness: std.builtin.Signedness, 27615 actual_bits: u16, 27616 wanted_bits: u16, 27617 }; 27618 27619 const IntPair = struct { 27620 actual: u64, 27621 wanted: u64, 27622 }; 27623 27624 const Size = struct { 27625 actual: std.builtin.Type.Pointer.Size, 27626 wanted: std.builtin.Type.Pointer.Size, 27627 }; 27628 27629 const Qualifiers = struct { 27630 actual_const: bool, 27631 wanted_const: bool, 27632 actual_volatile: bool, 27633 wanted_volatile: bool, 27634 }; 27635 27636 const AddressSpace = struct { 27637 actual: std.builtin.AddressSpace, 27638 wanted: std.builtin.AddressSpace, 27639 }; 27640 27641 const CC = struct { 27642 actual: std.builtin.CallingConvention, 27643 wanted: std.builtin.CallingConvention, 27644 }; 27645 27646 const BitRange = struct { 27647 actual_host: u16, 27648 wanted_host: u16, 27649 actual_offset: u16, 27650 wanted_offset: u16, 27651 }; 27652 27653 fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult { 27654 const res = try arena.create(InMemoryCoercionResult); 27655 res.* = child.*; 27656 return res; 27657 } 27658 27659 fn report(res: *const InMemoryCoercionResult, sema: *Sema, block: *Block, src: LazySrcLoc, msg: *Module.ErrorMsg) !void { 27660 const mod = sema.mod; 27661 var cur = res; 27662 while (true) switch (cur.*) { 27663 .ok => unreachable, 27664 .no_match => |types| { 27665 try sema.addDeclaredHereNote(msg, types.wanted); 27666 try sema.addDeclaredHereNote(msg, types.actual); 27667 break; 27668 }, 27669 .int_not_coercible => |int| { 27670 try sema.errNote(block, src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{ 27671 @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits, 27672 }); 27673 break; 27674 }, 27675 .error_union_payload => |pair| { 27676 try sema.errNote(block, src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{ 27677 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27678 }); 27679 cur = pair.child; 27680 }, 27681 .array_len => |lens| { 27682 try sema.errNote(block, src, msg, "array of length {d} cannot cast into an array of length {d}", .{ 27683 lens.actual, lens.wanted, 27684 }); 27685 break; 27686 }, 27687 .array_sentinel => |sentinel| { 27688 if (sentinel.actual.toIntern() != .unreachable_value) { 27689 try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{ 27690 sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod), 27691 }); 27692 } else { 27693 try sema.errNote(block, src, msg, "destination array requires '{}' sentinel", .{ 27694 sentinel.wanted.fmtValue(sentinel.ty, mod), 27695 }); 27696 } 27697 break; 27698 }, 27699 .array_elem => |pair| { 27700 try sema.errNote(block, src, msg, "array element type '{}' cannot cast into array element type '{}'", .{ 27701 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27702 }); 27703 cur = pair.child; 27704 }, 27705 .vector_len => |lens| { 27706 try sema.errNote(block, src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{ 27707 lens.actual, lens.wanted, 27708 }); 27709 break; 27710 }, 27711 .vector_elem => |pair| { 27712 try sema.errNote(block, src, msg, "vector element type '{}' cannot cast into vector element type '{}'", .{ 27713 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27714 }); 27715 cur = pair.child; 27716 }, 27717 .optional_shape => |pair| { 27718 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ 27719 pair.actual.optionalChild(mod).fmt(mod), pair.wanted.optionalChild(mod).fmt(mod), 27720 }); 27721 break; 27722 }, 27723 .optional_child => |pair| { 27724 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ 27725 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27726 }); 27727 cur = pair.child; 27728 }, 27729 .from_anyerror => { 27730 try sema.errNote(block, src, msg, "global error set cannot cast into a smaller set", .{}); 27731 break; 27732 }, 27733 .missing_error => |missing_errors| { 27734 for (missing_errors) |err| { 27735 try sema.errNote(block, src, msg, "'error.{}' not a member of destination error set", .{err.fmt(&mod.intern_pool)}); 27736 } 27737 break; 27738 }, 27739 .fn_var_args => |wanted_var_args| { 27740 if (wanted_var_args) { 27741 try sema.errNote(block, src, msg, "non-variadic function cannot cast into a variadic function", .{}); 27742 } else { 27743 try sema.errNote(block, src, msg, "variadic function cannot cast into a non-variadic function", .{}); 27744 } 27745 break; 27746 }, 27747 .fn_generic => |wanted_generic| { 27748 if (wanted_generic) { 27749 try sema.errNote(block, src, msg, "non-generic function cannot cast into a generic function", .{}); 27750 } else { 27751 try sema.errNote(block, src, msg, "generic function cannot cast into a non-generic function", .{}); 27752 } 27753 break; 27754 }, 27755 .fn_param_count => |lens| { 27756 try sema.errNote(block, src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{ 27757 lens.actual, lens.wanted, 27758 }); 27759 break; 27760 }, 27761 .fn_param_noalias => |param| { 27762 var index: u6 = 0; 27763 var actual_noalias = false; 27764 while (true) : (index += 1) { 27765 const actual = @as(u1, @truncate(param.actual >> index)); 27766 const wanted = @as(u1, @truncate(param.wanted >> index)); 27767 if (actual != wanted) { 27768 actual_noalias = actual == 1; 27769 break; 27770 } 27771 } 27772 if (!actual_noalias) { 27773 try sema.errNote(block, src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index}); 27774 } else { 27775 try sema.errNote(block, src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index}); 27776 } 27777 break; 27778 }, 27779 .fn_param_comptime => |param| { 27780 if (param.wanted) { 27781 try sema.errNote(block, src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index}); 27782 } else { 27783 try sema.errNote(block, src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index}); 27784 } 27785 break; 27786 }, 27787 .fn_param => |param| { 27788 try sema.errNote(block, src, msg, "parameter {d} '{}' cannot cast into '{}'", .{ 27789 param.index, param.actual.fmt(mod), param.wanted.fmt(mod), 27790 }); 27791 cur = param.child; 27792 }, 27793 .fn_cc => |cc| { 27794 try sema.errNote(block, src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) }); 27795 break; 27796 }, 27797 .fn_return_type => |pair| { 27798 try sema.errNote(block, src, msg, "return type '{}' cannot cast into return type '{}'", .{ 27799 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27800 }); 27801 cur = pair.child; 27802 }, 27803 .ptr_child => |pair| { 27804 try sema.errNote(block, src, msg, "pointer type child '{}' cannot cast into pointer type child '{}'", .{ 27805 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27806 }); 27807 cur = pair.child; 27808 }, 27809 .ptr_addrspace => |@"addrspace"| { 27810 try sema.errNote(block, src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) }); 27811 break; 27812 }, 27813 .ptr_sentinel => |sentinel| { 27814 if (sentinel.actual.toIntern() != .unreachable_value) { 27815 try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{ 27816 sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod), 27817 }); 27818 } else { 27819 try sema.errNote(block, src, msg, "destination pointer requires '{}' sentinel", .{ 27820 sentinel.wanted.fmtValue(sentinel.ty, mod), 27821 }); 27822 } 27823 break; 27824 }, 27825 .ptr_size => |size| { 27826 try sema.errNote(block, src, msg, "a {s} pointer cannot cast into a {s} pointer", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) }); 27827 break; 27828 }, 27829 .ptr_qualifiers => |qualifiers| { 27830 const ok_const = !qualifiers.actual_const or qualifiers.wanted_const; 27831 const ok_volatile = !qualifiers.actual_volatile or qualifiers.wanted_volatile; 27832 if (!ok_const) { 27833 try sema.errNote(block, src, msg, "cast discards const qualifier", .{}); 27834 } else if (!ok_volatile) { 27835 try sema.errNote(block, src, msg, "cast discards volatile qualifier", .{}); 27836 } 27837 break; 27838 }, 27839 .ptr_allowzero => |pair| { 27840 const wanted_allow_zero = pair.wanted.ptrAllowsZero(mod); 27841 const actual_allow_zero = pair.actual.ptrAllowsZero(mod); 27842 if (actual_allow_zero and !wanted_allow_zero) { 27843 try sema.errNote(block, src, msg, "'{}' could have null values which are illegal in type '{}'", .{ 27844 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27845 }); 27846 } else { 27847 try sema.errNote(block, src, msg, "mutable '{}' allows illegal null values stored to type '{}'", .{ 27848 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27849 }); 27850 } 27851 break; 27852 }, 27853 .ptr_bit_range => |bit_range| { 27854 if (bit_range.actual_host != bit_range.wanted_host) { 27855 try sema.errNote(block, src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{ 27856 bit_range.actual_host, bit_range.wanted_host, 27857 }); 27858 } 27859 if (bit_range.actual_offset != bit_range.wanted_offset) { 27860 try sema.errNote(block, src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{ 27861 bit_range.actual_offset, bit_range.wanted_offset, 27862 }); 27863 } 27864 break; 27865 }, 27866 .ptr_alignment => |pair| { 27867 try sema.errNote(block, src, msg, "pointer alignment '{}' cannot cast into pointer alignment '{}'", .{ 27868 pair.actual, pair.wanted, 27869 }); 27870 break; 27871 }, 27872 .double_ptr_to_anyopaque => |pair| { 27873 try sema.errNote(block, src, msg, "cannot implicitly cast double pointer '{}' to anyopaque pointer '{}'", .{ 27874 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27875 }); 27876 break; 27877 }, 27878 .slice_to_anyopaque => |pair| { 27879 try sema.errNote(block, src, msg, "cannot implicitly cast slice '{}' to anyopaque pointer '{}'", .{ 27880 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27881 }); 27882 try sema.errNote(block, src, msg, "consider using '.ptr'", .{}); 27883 break; 27884 }, 27885 }; 27886 } 27887 }; 27888 27889 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 { 27890 return switch (size) { 27891 .One => "single", 27892 .Many => "many", 27893 .C => "C", 27894 .Slice => unreachable, 27895 }; 27896 } 27897 27898 /// If pointers have the same representation in runtime memory, a bitcast AIR instruction 27899 /// may be used for the coercion. 27900 /// * `const` attribute can be gained 27901 /// * `volatile` attribute can be gained 27902 /// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut 27903 /// * alignment can be decreased 27904 /// * bit offset attributes must match exactly 27905 /// * `*`/`[*]` must match exactly, but `[*c]` matches either one 27906 /// * sentinel-terminated pointers can coerce into `[*]` 27907 fn coerceInMemoryAllowed( 27908 sema: *Sema, 27909 block: *Block, 27910 dest_ty: Type, 27911 src_ty: Type, 27912 dest_is_mut: bool, 27913 target: std.Target, 27914 dest_src: LazySrcLoc, 27915 src_src: LazySrcLoc, 27916 ) CompileError!InMemoryCoercionResult { 27917 const mod = sema.mod; 27918 27919 if (dest_ty.eql(src_ty, mod)) 27920 return .ok; 27921 27922 const dest_tag = dest_ty.zigTypeTag(mod); 27923 const src_tag = src_ty.zigTypeTag(mod); 27924 27925 // Differently-named integers with the same number of bits. 27926 if (dest_tag == .Int and src_tag == .Int) { 27927 const dest_info = dest_ty.intInfo(mod); 27928 const src_info = src_ty.intInfo(mod); 27929 27930 if (dest_info.signedness == src_info.signedness and 27931 dest_info.bits == src_info.bits) 27932 { 27933 return .ok; 27934 } 27935 27936 if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or 27937 // small enough unsigned ints can get casted to large enough signed ints 27938 (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or 27939 (dest_info.signedness == .unsigned and src_info.signedness == .signed)) 27940 { 27941 return InMemoryCoercionResult{ .int_not_coercible = .{ 27942 .actual_signedness = src_info.signedness, 27943 .wanted_signedness = dest_info.signedness, 27944 .actual_bits = src_info.bits, 27945 .wanted_bits = dest_info.bits, 27946 } }; 27947 } 27948 } 27949 27950 // Differently-named floats with the same number of bits. 27951 if (dest_tag == .Float and src_tag == .Float) { 27952 const dest_bits = dest_ty.floatBits(target); 27953 const src_bits = src_ty.floatBits(target); 27954 if (dest_bits == src_bits) { 27955 return .ok; 27956 } 27957 } 27958 27959 // Pointers / Pointer-like Optionals 27960 const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty); 27961 const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty); 27962 if (maybe_dest_ptr_ty) |dest_ptr_ty| { 27963 if (maybe_src_ptr_ty) |src_ptr_ty| { 27964 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src); 27965 } 27966 } 27967 27968 // Slices 27969 if (dest_ty.isSlice(mod) and src_ty.isSlice(mod)) { 27970 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); 27971 } 27972 27973 // Functions 27974 if (dest_tag == .Fn and src_tag == .Fn) { 27975 return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, target, dest_src, src_src); 27976 } 27977 27978 // Error Unions 27979 if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) { 27980 const dest_payload = dest_ty.errorUnionPayload(mod); 27981 const src_payload = src_ty.errorUnionPayload(mod); 27982 const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src); 27983 if (child != .ok) { 27984 return InMemoryCoercionResult{ .error_union_payload = .{ 27985 .child = try child.dupe(sema.arena), 27986 .actual = src_payload, 27987 .wanted = dest_payload, 27988 } }; 27989 } 27990 return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src); 27991 } 27992 27993 // Error Sets 27994 if (dest_tag == .ErrorSet and src_tag == .ErrorSet) { 27995 return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src); 27996 } 27997 27998 // Arrays 27999 if (dest_tag == .Array and src_tag == .Array) { 28000 const dest_info = dest_ty.arrayInfo(mod); 28001 const src_info = src_ty.arrayInfo(mod); 28002 if (dest_info.len != src_info.len) { 28003 return InMemoryCoercionResult{ .array_len = .{ 28004 .actual = src_info.len, 28005 .wanted = dest_info.len, 28006 } }; 28007 } 28008 28009 const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src); 28010 if (child != .ok) { 28011 return InMemoryCoercionResult{ .array_elem = .{ 28012 .child = try child.dupe(sema.arena), 28013 .actual = src_info.elem_type, 28014 .wanted = dest_info.elem_type, 28015 } }; 28016 } 28017 const ok_sent = dest_info.sentinel == null or 28018 (src_info.sentinel != null and 28019 dest_info.sentinel.?.eql( 28020 try mod.getCoerced(src_info.sentinel.?, dest_info.elem_type), 28021 dest_info.elem_type, 28022 mod, 28023 )); 28024 if (!ok_sent) { 28025 return InMemoryCoercionResult{ .array_sentinel = .{ 28026 .actual = src_info.sentinel orelse Value.@"unreachable", 28027 .wanted = dest_info.sentinel orelse Value.@"unreachable", 28028 .ty = dest_info.elem_type, 28029 } }; 28030 } 28031 return .ok; 28032 } 28033 28034 // Vectors 28035 if (dest_tag == .Vector and src_tag == .Vector) { 28036 const dest_len = dest_ty.vectorLen(mod); 28037 const src_len = src_ty.vectorLen(mod); 28038 if (dest_len != src_len) { 28039 return InMemoryCoercionResult{ .vector_len = .{ 28040 .actual = src_len, 28041 .wanted = dest_len, 28042 } }; 28043 } 28044 28045 const dest_elem_ty = dest_ty.scalarType(mod); 28046 const src_elem_ty = src_ty.scalarType(mod); 28047 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src); 28048 if (child != .ok) { 28049 return InMemoryCoercionResult{ .vector_elem = .{ 28050 .child = try child.dupe(sema.arena), 28051 .actual = src_elem_ty, 28052 .wanted = dest_elem_ty, 28053 } }; 28054 } 28055 28056 return .ok; 28057 } 28058 28059 // Optionals 28060 if (dest_tag == .Optional and src_tag == .Optional) { 28061 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) { 28062 return InMemoryCoercionResult{ .optional_shape = .{ 28063 .actual = src_ty, 28064 .wanted = dest_ty, 28065 } }; 28066 } 28067 const dest_child_type = dest_ty.optionalChild(mod); 28068 const src_child_type = src_ty.optionalChild(mod); 28069 28070 const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src); 28071 if (child != .ok) { 28072 return InMemoryCoercionResult{ .optional_child = .{ 28073 .child = try child.dupe(sema.arena), 28074 .actual = src_child_type, 28075 .wanted = dest_child_type, 28076 } }; 28077 } 28078 28079 return .ok; 28080 } 28081 28082 // Tuples (with in-memory-coercible fields) 28083 if (dest_ty.isTuple(mod) and src_ty.isTuple(mod)) tuple: { 28084 if (dest_ty.containerLayout(mod) != src_ty.containerLayout(mod)) break :tuple; 28085 if (dest_ty.structFieldCount(mod) != src_ty.structFieldCount(mod)) break :tuple; 28086 const field_count = dest_ty.structFieldCount(mod); 28087 for (0..field_count) |field_idx| { 28088 if (dest_ty.structFieldIsComptime(field_idx, mod) != src_ty.structFieldIsComptime(field_idx, mod)) break :tuple; 28089 if (dest_ty.structFieldAlign(field_idx, mod) != src_ty.structFieldAlign(field_idx, mod)) break :tuple; 28090 const dest_field_ty = dest_ty.structFieldType(field_idx, mod); 28091 const src_field_ty = src_ty.structFieldType(field_idx, mod); 28092 const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src); 28093 if (field != .ok) break :tuple; 28094 } 28095 return .ok; 28096 } 28097 28098 return InMemoryCoercionResult{ .no_match = .{ 28099 .actual = dest_ty, 28100 .wanted = src_ty, 28101 } }; 28102 } 28103 28104 fn coerceInMemoryAllowedErrorSets( 28105 sema: *Sema, 28106 block: *Block, 28107 dest_ty: Type, 28108 src_ty: Type, 28109 dest_src: LazySrcLoc, 28110 src_src: LazySrcLoc, 28111 ) !InMemoryCoercionResult { 28112 const mod = sema.mod; 28113 const gpa = sema.gpa; 28114 const ip = &mod.intern_pool; 28115 28116 // Coercion to `anyerror`. Note that this check can return false negatives 28117 // in case the error sets did not get resolved. 28118 if (dest_ty.isAnyError(mod)) { 28119 return .ok; 28120 } 28121 28122 if (mod.typeToInferredErrorSetIndex(dest_ty).unwrap()) |dst_ies_index| { 28123 const dst_ies = mod.inferredErrorSetPtr(dst_ies_index); 28124 // We will make an effort to return `ok` without resolving either error set, to 28125 // avoid unnecessary "unable to resolve error set" dependency loop errors. 28126 switch (src_ty.toIntern()) { 28127 .anyerror_type => {}, 28128 else => switch (ip.indexToKey(src_ty.toIntern())) { 28129 .inferred_error_set_type => |src_index| { 28130 // If both are inferred error sets of functions, and 28131 // the dest includes the source function, the coercion is OK. 28132 // This check is important because it works without forcing a full resolution 28133 // of inferred error sets. 28134 if (dst_ies.inferred_error_sets.contains(src_index)) { 28135 return .ok; 28136 } 28137 }, 28138 .error_set_type => |error_set_type| { 28139 for (error_set_type.names) |name| { 28140 if (!dst_ies.errors.contains(name)) break; 28141 } else return .ok; 28142 }, 28143 else => unreachable, 28144 }, 28145 } 28146 28147 if (dst_ies.func == sema.owner_func_index.unwrap()) { 28148 // We are trying to coerce an error set to the current function's 28149 // inferred error set. 28150 try dst_ies.addErrorSet(src_ty, ip, gpa); 28151 return .ok; 28152 } 28153 28154 try sema.resolveInferredErrorSet(block, dest_src, dst_ies_index); 28155 // isAnyError might have changed from a false negative to a true positive after resolution. 28156 if (dest_ty.isAnyError(mod)) { 28157 return .ok; 28158 } 28159 } 28160 28161 var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa); 28162 defer missing_error_buf.deinit(); 28163 28164 switch (src_ty.toIntern()) { 28165 .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { 28166 .simple_type => unreachable, // filtered out above 28167 .error_set_type, .inferred_error_set_type => return .from_anyerror, 28168 else => unreachable, 28169 }, 28170 28171 else => switch (ip.indexToKey(src_ty.toIntern())) { 28172 .inferred_error_set_type => |src_index| { 28173 const src_data = mod.inferredErrorSetPtr(src_index); 28174 28175 try sema.resolveInferredErrorSet(block, src_src, src_index); 28176 // src anyerror status might have changed after the resolution. 28177 if (src_ty.isAnyError(mod)) { 28178 // dest_ty.isAnyError(mod) == true is already checked for at this point. 28179 return .from_anyerror; 28180 } 28181 28182 for (src_data.errors.keys()) |key| { 28183 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) { 28184 try missing_error_buf.append(key); 28185 } 28186 } 28187 28188 if (missing_error_buf.items.len != 0) { 28189 return InMemoryCoercionResult{ 28190 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 28191 }; 28192 } 28193 28194 return .ok; 28195 }, 28196 .error_set_type => |error_set_type| { 28197 for (error_set_type.names) |name| { 28198 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) { 28199 try missing_error_buf.append(name); 28200 } 28201 } 28202 28203 if (missing_error_buf.items.len != 0) { 28204 return InMemoryCoercionResult{ 28205 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 28206 }; 28207 } 28208 28209 return .ok; 28210 }, 28211 else => unreachable, 28212 }, 28213 } 28214 } 28215 28216 fn coerceInMemoryAllowedFns( 28217 sema: *Sema, 28218 block: *Block, 28219 dest_ty: Type, 28220 src_ty: Type, 28221 target: std.Target, 28222 dest_src: LazySrcLoc, 28223 src_src: LazySrcLoc, 28224 ) !InMemoryCoercionResult { 28225 const mod = sema.mod; 28226 28227 { 28228 const dest_info = mod.typeToFunc(dest_ty).?; 28229 const src_info = mod.typeToFunc(src_ty).?; 28230 28231 if (dest_info.is_var_args != src_info.is_var_args) { 28232 return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; 28233 } 28234 28235 if (dest_info.is_generic != src_info.is_generic) { 28236 return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; 28237 } 28238 28239 if (dest_info.cc != src_info.cc) { 28240 return InMemoryCoercionResult{ .fn_cc = .{ 28241 .actual = src_info.cc, 28242 .wanted = dest_info.cc, 28243 } }; 28244 } 28245 28246 switch (src_info.return_type) { 28247 .noreturn_type, .generic_poison_type => {}, 28248 else => { 28249 const dest_return_type = dest_info.return_type.toType(); 28250 const src_return_type = src_info.return_type.toType(); 28251 const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src); 28252 if (rt != .ok) { 28253 return InMemoryCoercionResult{ .fn_return_type = .{ 28254 .child = try rt.dupe(sema.arena), 28255 .actual = src_return_type, 28256 .wanted = dest_return_type, 28257 } }; 28258 } 28259 }, 28260 } 28261 } 28262 28263 const params_len = params_len: { 28264 const dest_info = mod.typeToFunc(dest_ty).?; 28265 const src_info = mod.typeToFunc(src_ty).?; 28266 28267 if (dest_info.param_types.len != src_info.param_types.len) { 28268 return InMemoryCoercionResult{ .fn_param_count = .{ 28269 .actual = src_info.param_types.len, 28270 .wanted = dest_info.param_types.len, 28271 } }; 28272 } 28273 28274 if (dest_info.noalias_bits != src_info.noalias_bits) { 28275 return InMemoryCoercionResult{ .fn_param_noalias = .{ 28276 .actual = src_info.noalias_bits, 28277 .wanted = dest_info.noalias_bits, 28278 } }; 28279 } 28280 28281 break :params_len dest_info.param_types.len; 28282 }; 28283 28284 for (0..params_len) |param_i| { 28285 const dest_info = mod.typeToFunc(dest_ty).?; 28286 const src_info = mod.typeToFunc(src_ty).?; 28287 28288 const dest_param_ty = dest_info.param_types[param_i].toType(); 28289 const src_param_ty = src_info.param_types[param_i].toType(); 28290 28291 const param_i_small = @as(u5, @intCast(param_i)); 28292 if (dest_info.paramIsComptime(param_i_small) != src_info.paramIsComptime(param_i_small)) { 28293 return InMemoryCoercionResult{ .fn_param_comptime = .{ 28294 .index = param_i, 28295 .wanted = dest_info.paramIsComptime(param_i_small), 28296 } }; 28297 } 28298 28299 switch (src_param_ty.toIntern()) { 28300 .generic_poison_type => {}, 28301 else => { 28302 // Note: Cast direction is reversed here. 28303 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src); 28304 if (param != .ok) { 28305 return InMemoryCoercionResult{ .fn_param = .{ 28306 .child = try param.dupe(sema.arena), 28307 .actual = src_param_ty, 28308 .wanted = dest_param_ty, 28309 .index = param_i, 28310 } }; 28311 } 28312 }, 28313 } 28314 } 28315 28316 return .ok; 28317 } 28318 28319 fn coerceInMemoryAllowedPtrs( 28320 sema: *Sema, 28321 block: *Block, 28322 dest_ty: Type, 28323 src_ty: Type, 28324 dest_ptr_ty: Type, 28325 src_ptr_ty: Type, 28326 dest_is_mut: bool, 28327 target: std.Target, 28328 dest_src: LazySrcLoc, 28329 src_src: LazySrcLoc, 28330 ) !InMemoryCoercionResult { 28331 const mod = sema.mod; 28332 const dest_info = dest_ptr_ty.ptrInfo(mod); 28333 const src_info = src_ptr_ty.ptrInfo(mod); 28334 28335 const ok_ptr_size = src_info.flags.size == dest_info.flags.size or 28336 src_info.flags.size == .C or dest_info.flags.size == .C; 28337 if (!ok_ptr_size) { 28338 return InMemoryCoercionResult{ .ptr_size = .{ 28339 .actual = src_info.flags.size, 28340 .wanted = dest_info.flags.size, 28341 } }; 28342 } 28343 28344 const ok_cv_qualifiers = 28345 (!src_info.flags.is_const or dest_info.flags.is_const) and 28346 (!src_info.flags.is_volatile or dest_info.flags.is_volatile); 28347 28348 if (!ok_cv_qualifiers) { 28349 return InMemoryCoercionResult{ .ptr_qualifiers = .{ 28350 .actual_const = src_info.flags.is_const, 28351 .wanted_const = dest_info.flags.is_const, 28352 .actual_volatile = src_info.flags.is_volatile, 28353 .wanted_volatile = dest_info.flags.is_volatile, 28354 } }; 28355 } 28356 28357 if (dest_info.flags.address_space != src_info.flags.address_space) { 28358 return InMemoryCoercionResult{ .ptr_addrspace = .{ 28359 .actual = src_info.flags.address_space, 28360 .wanted = dest_info.flags.address_space, 28361 } }; 28362 } 28363 28364 const child = try sema.coerceInMemoryAllowed(block, dest_info.child.toType(), src_info.child.toType(), !dest_info.flags.is_const, target, dest_src, src_src); 28365 if (child != .ok) { 28366 return InMemoryCoercionResult{ .ptr_child = .{ 28367 .child = try child.dupe(sema.arena), 28368 .actual = src_info.child.toType(), 28369 .wanted = dest_info.child.toType(), 28370 } }; 28371 } 28372 28373 const dest_allow_zero = dest_ty.ptrAllowsZero(mod); 28374 const src_allow_zero = src_ty.ptrAllowsZero(mod); 28375 28376 const ok_allows_zero = (dest_allow_zero and 28377 (src_allow_zero or !dest_is_mut)) or 28378 (!dest_allow_zero and !src_allow_zero); 28379 if (!ok_allows_zero) { 28380 return InMemoryCoercionResult{ .ptr_allowzero = .{ 28381 .actual = src_ty, 28382 .wanted = dest_ty, 28383 } }; 28384 } 28385 28386 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or 28387 src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) 28388 { 28389 return InMemoryCoercionResult{ .ptr_bit_range = .{ 28390 .actual_host = src_info.packed_offset.host_size, 28391 .wanted_host = dest_info.packed_offset.host_size, 28392 .actual_offset = src_info.packed_offset.bit_offset, 28393 .wanted_offset = dest_info.packed_offset.bit_offset, 28394 } }; 28395 } 28396 28397 const ok_sent = dest_info.sentinel == .none or src_info.flags.size == .C or 28398 (src_info.sentinel != .none and 28399 dest_info.sentinel == try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child)); 28400 if (!ok_sent) { 28401 return InMemoryCoercionResult{ .ptr_sentinel = .{ 28402 .actual = switch (src_info.sentinel) { 28403 .none => Value.@"unreachable", 28404 else => src_info.sentinel.toValue(), 28405 }, 28406 .wanted = switch (dest_info.sentinel) { 28407 .none => Value.@"unreachable", 28408 else => dest_info.sentinel.toValue(), 28409 }, 28410 .ty = dest_info.child.toType(), 28411 } }; 28412 } 28413 28414 // If both pointers have alignment 0, it means they both want ABI alignment. 28415 // In this case, if they share the same child type, no need to resolve 28416 // pointee type alignment. Otherwise both pointee types must have their alignment 28417 // resolved and we compare the alignment numerically. 28418 if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or 28419 dest_info.child != src_info.child) 28420 { 28421 const src_align = src_info.flags.alignment.toByteUnitsOptional() orelse 28422 src_info.child.toType().abiAlignment(mod); 28423 28424 const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse 28425 dest_info.child.toType().abiAlignment(mod); 28426 28427 if (dest_align > src_align) { 28428 return InMemoryCoercionResult{ .ptr_alignment = .{ 28429 .actual = src_align, 28430 .wanted = dest_align, 28431 } }; 28432 } 28433 } 28434 28435 return .ok; 28436 } 28437 28438 fn coerceVarArgParam( 28439 sema: *Sema, 28440 block: *Block, 28441 inst: Air.Inst.Ref, 28442 inst_src: LazySrcLoc, 28443 ) !Air.Inst.Ref { 28444 if (block.is_typeof) return inst; 28445 28446 const mod = sema.mod; 28447 const uncasted_ty = sema.typeOf(inst); 28448 const coerced = switch (uncasted_ty.zigTypeTag(mod)) { 28449 // TODO consider casting to c_int/f64 if they fit 28450 .ComptimeInt, .ComptimeFloat => return sema.fail( 28451 block, 28452 inst_src, 28453 "integer and float literals passed to variadic function must be casted to a fixed-size number type", 28454 .{}, 28455 ), 28456 .Fn => blk: { 28457 const fn_val = try sema.resolveConstValue(block, .unneeded, inst, ""); 28458 const fn_decl = fn_val.pointerDecl(mod).?; 28459 break :blk try sema.analyzeDeclRef(fn_decl); 28460 }, 28461 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), 28462 .Float => float: { 28463 const target = sema.mod.getTarget(); 28464 const double_bits = target.c_type_bit_size(.double); 28465 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget()); 28466 if (inst_bits >= double_bits) break :float inst; 28467 switch (double_bits) { 28468 32 => break :float try sema.coerce(block, Type.f32, inst, inst_src), 28469 64 => break :float try sema.coerce(block, Type.f64, inst, inst_src), 28470 else => unreachable, 28471 } 28472 }, 28473 else => inst, 28474 }; 28475 28476 const coerced_ty = sema.typeOf(coerced); 28477 if (!try sema.validateExternType(coerced_ty, .param_ty)) { 28478 const msg = msg: { 28479 const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)}); 28480 errdefer msg.destroy(sema.gpa); 28481 28482 const src_decl = sema.mod.declPtr(block.src_decl); 28483 try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty); 28484 28485 try sema.addDeclaredHereNote(msg, coerced_ty); 28486 break :msg msg; 28487 }; 28488 return sema.failWithOwnedErrorMsg(msg); 28489 } 28490 return coerced; 28491 } 28492 28493 // TODO migrate callsites to use storePtr2 instead. 28494 fn storePtr( 28495 sema: *Sema, 28496 block: *Block, 28497 src: LazySrcLoc, 28498 ptr: Air.Inst.Ref, 28499 uncasted_operand: Air.Inst.Ref, 28500 ) CompileError!void { 28501 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store; 28502 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag); 28503 } 28504 28505 fn storePtr2( 28506 sema: *Sema, 28507 block: *Block, 28508 src: LazySrcLoc, 28509 ptr: Air.Inst.Ref, 28510 ptr_src: LazySrcLoc, 28511 uncasted_operand: Air.Inst.Ref, 28512 operand_src: LazySrcLoc, 28513 air_tag: Air.Inst.Tag, 28514 ) CompileError!void { 28515 const mod = sema.mod; 28516 const ptr_ty = sema.typeOf(ptr); 28517 if (ptr_ty.isConstPtr(mod)) 28518 return sema.fail(block, ptr_src, "cannot assign to constant", .{}); 28519 28520 const elem_ty = ptr_ty.childType(mod); 28521 28522 // To generate better code for tuples, we detect a tuple operand here, and 28523 // analyze field loads and stores directly. This avoids an extra allocation + memcpy 28524 // which would occur if we used `coerce`. 28525 // However, we avoid this mechanism if the destination element type is a tuple, 28526 // because the regular store will be better for this case. 28527 // If the destination type is a struct we don't want this mechanism to trigger, because 28528 // this code does not handle tuple-to-struct coercion which requires dealing with missing 28529 // fields. 28530 const operand_ty = sema.typeOf(uncasted_operand); 28531 if (operand_ty.isTuple(mod) and elem_ty.zigTypeTag(mod) == .Array) { 28532 const field_count = operand_ty.structFieldCount(mod); 28533 var i: u32 = 0; 28534 while (i < field_count) : (i += 1) { 28535 const elem_src = operand_src; // TODO better source location 28536 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); 28537 const elem_index = try sema.addIntUnsigned(Type.usize, i); 28538 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true); 28539 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); 28540 } 28541 return; 28542 } 28543 28544 // TODO do the same thing for anon structs as for tuples above. 28545 // However, beware of the need to handle missing/extra fields. 28546 28547 const is_ret = air_tag == .ret_ptr; 28548 28549 // Detect if we are storing an array operand to a bitcasted vector pointer. 28550 // If so, we instead reach through the bitcasted pointer to the vector pointer, 28551 // bitcast the array operand to a vector, and then lower this as a store of 28552 // a vector value to a vector pointer. This generally results in better code, 28553 // as well as working around an LLVM bug: 28554 // https://github.com/ziglang/zig/issues/11154 28555 if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { 28556 const vector_ty = sema.typeOf(vector_ptr).childType(mod); 28557 const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 28558 error.NotCoercible => unreachable, 28559 else => |e| return e, 28560 }; 28561 try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store); 28562 return; 28563 } 28564 28565 const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 28566 error.NotCoercible => unreachable, 28567 else => |e| return e, 28568 }; 28569 const maybe_operand_val = try sema.resolveMaybeUndefVal(operand); 28570 28571 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 28572 const operand_val = maybe_operand_val orelse { 28573 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); 28574 break :rs operand_src; 28575 }; 28576 if (ptr_val.isComptimeMutablePtr(mod)) { 28577 try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); 28578 return; 28579 } else break :rs ptr_src; 28580 } else ptr_src; 28581 28582 // We do this after the possible comptime store above, for the case of field_ptr stores 28583 // to unions because we want the comptime tag to be set, even if the field type is void. 28584 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) 28585 return; 28586 28587 if (air_tag == .bitcast) { 28588 // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr` 28589 // to avoid calling `requireRuntimeBlock` for the dummy block. 28590 _ = try block.addBinOp(.store, ptr, operand); 28591 return; 28592 } 28593 28594 try sema.requireRuntimeBlock(block, src, runtime_src); 28595 try sema.queueFullTypeResolution(elem_ty); 28596 28597 if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) { 28598 const ptr_inst = Air.refToIndex(ptr).?; 28599 const air_tags = sema.air_instructions.items(.tag); 28600 if (air_tags[ptr_inst] == .ptr_elem_ptr) { 28601 const ty_pl = sema.air_instructions.items(.data)[ptr_inst].ty_pl; 28602 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 28603 _ = try block.addInst(.{ 28604 .tag = .vector_store_elem, 28605 .data = .{ .vector_store_elem = .{ 28606 .vector_ptr = bin_op.lhs, 28607 .payload = try block.sema.addExtra(Air.Bin{ 28608 .lhs = bin_op.rhs, 28609 .rhs = operand, 28610 }), 28611 } }, 28612 }); 28613 return; 28614 } 28615 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{ 28616 ptr_ty.fmt(sema.mod), 28617 }); 28618 } 28619 28620 if (is_ret) { 28621 _ = try block.addBinOp(.store, ptr, operand); 28622 } else { 28623 _ = try block.addBinOp(air_tag, ptr, operand); 28624 } 28625 } 28626 28627 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector 28628 /// pointer. Only if the final element type matches the vector element type, and the 28629 /// lengths match. 28630 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { 28631 const mod = sema.mod; 28632 const array_ty = sema.typeOf(ptr).childType(mod); 28633 if (array_ty.zigTypeTag(mod) != .Array) return null; 28634 var ptr_ref = ptr; 28635 var ptr_inst = Air.refToIndex(ptr_ref) orelse return null; 28636 const air_datas = sema.air_instructions.items(.data); 28637 const air_tags = sema.air_instructions.items(.tag); 28638 const vector_ty = while (air_tags[ptr_inst] == .bitcast) { 28639 ptr_ref = air_datas[ptr_inst].ty_op.operand; 28640 if (!sema.isKnownZigType(ptr_ref, .Pointer)) return null; 28641 const child_ty = sema.typeOf(ptr_ref).childType(mod); 28642 if (child_ty.zigTypeTag(mod) == .Vector) break child_ty; 28643 ptr_inst = Air.refToIndex(ptr_ref) orelse return null; 28644 } else return null; 28645 28646 // We have a pointer-to-array and a pointer-to-vector. If the elements and 28647 // lengths match, return the result. 28648 if (array_ty.childType(mod).eql(vector_ty.childType(mod), sema.mod) and 28649 array_ty.arrayLen(mod) == vector_ty.vectorLen(mod)) 28650 { 28651 return ptr_ref; 28652 } else { 28653 return null; 28654 } 28655 } 28656 28657 /// Call when you have Value objects rather than Air instructions, and you want to 28658 /// assert the store must be done at comptime. 28659 fn storePtrVal( 28660 sema: *Sema, 28661 block: *Block, 28662 src: LazySrcLoc, 28663 ptr_val: Value, 28664 operand_val: Value, 28665 operand_ty: Type, 28666 ) !void { 28667 const mod = sema.mod; 28668 var mut_kit = try sema.beginComptimePtrMutation(block, src, ptr_val, operand_ty); 28669 try sema.checkComptimeVarStore(block, src, mut_kit.mut_decl); 28670 28671 switch (mut_kit.pointee) { 28672 .direct => |val_ptr| { 28673 if (mut_kit.mut_decl.runtime_index == .comptime_field_ptr) { 28674 if (!operand_val.eql(val_ptr.*, operand_ty, mod)) { 28675 // TODO use failWithInvalidComptimeFieldStore 28676 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); 28677 } 28678 return; 28679 } 28680 val_ptr.* = (try operand_val.intern(operand_ty, mod)).toValue(); 28681 }, 28682 .reinterpret => |reinterpret| { 28683 const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod)); 28684 const buffer = try sema.gpa.alloc(u8, abi_size); 28685 defer sema.gpa.free(buffer); 28686 reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) { 28687 error.OutOfMemory => return error.OutOfMemory, 28688 error.ReinterpretDeclRef => unreachable, 28689 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 28690 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), 28691 }; 28692 operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) { 28693 error.OutOfMemory => return error.OutOfMemory, 28694 error.ReinterpretDeclRef => unreachable, 28695 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 28696 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), 28697 }; 28698 28699 reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue(); 28700 }, 28701 .bad_decl_ty, .bad_ptr_ty => { 28702 // TODO show the decl declaration site in a note and explain whether the decl 28703 // or the pointer is the problematic type 28704 return sema.fail( 28705 block, 28706 src, 28707 "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout", 28708 .{mut_kit.ty.fmt(mod)}, 28709 ); 28710 }, 28711 } 28712 } 28713 28714 const ComptimePtrMutationKit = struct { 28715 mut_decl: InternPool.Key.Ptr.Addr.MutDecl, 28716 pointee: union(enum) { 28717 /// The pointer type matches the actual comptime Value so a direct 28718 /// modification is possible. 28719 direct: *Value, 28720 /// The largest parent Value containing pointee and having a well-defined memory layout. 28721 /// This is used for bitcasting, if direct dereferencing failed. 28722 reinterpret: struct { 28723 val_ptr: *Value, 28724 byte_offset: usize, 28725 }, 28726 /// If the root decl could not be used as parent, this means `ty` is the type that 28727 /// caused that by not having a well-defined layout. 28728 /// This one means the Decl that owns the value trying to be modified does not 28729 /// have a well defined memory layout. 28730 bad_decl_ty, 28731 /// If the root decl could not be used as parent, this means `ty` is the type that 28732 /// caused that by not having a well-defined layout. 28733 /// This one means the pointer type that is being stored through does not 28734 /// have a well defined memory layout. 28735 bad_ptr_ty, 28736 }, 28737 ty: Type, 28738 }; 28739 28740 fn beginComptimePtrMutation( 28741 sema: *Sema, 28742 block: *Block, 28743 src: LazySrcLoc, 28744 ptr_val: Value, 28745 ptr_elem_ty: Type, 28746 ) CompileError!ComptimePtrMutationKit { 28747 const mod = sema.mod; 28748 const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr; 28749 switch (ptr.addr) { 28750 .decl, .int => unreachable, // isComptimeMutablePtr has been checked already 28751 .mut_decl => |mut_decl| { 28752 const decl = mod.declPtr(mut_decl.decl); 28753 return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl); 28754 }, 28755 .comptime_field => |comptime_field| { 28756 const duped = try sema.arena.create(Value); 28757 duped.* = comptime_field.toValue(); 28758 return sema.beginComptimePtrMutationInner(block, src, mod.intern_pool.typeOf(comptime_field).toType(), duped, ptr_elem_ty, .{ 28759 .decl = undefined, 28760 .runtime_index = .comptime_field_ptr, 28761 }); 28762 }, 28763 .eu_payload => |eu_ptr| { 28764 const eu_ty = mod.intern_pool.typeOf(eu_ptr).toType().childType(mod); 28765 var parent = try sema.beginComptimePtrMutation(block, src, eu_ptr.toValue(), eu_ty); 28766 switch (parent.pointee) { 28767 .direct => |val_ptr| { 28768 const payload_ty = parent.ty.errorUnionPayload(mod); 28769 if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) { 28770 return ComptimePtrMutationKit{ 28771 .mut_decl = parent.mut_decl, 28772 .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data }, 28773 .ty = payload_ty, 28774 }; 28775 } else { 28776 // An error union has been initialized to undefined at comptime and now we 28777 // are for the first time setting the payload. We must change the 28778 // representation of the error union from `undef` to `opt_payload`. 28779 28780 const payload = try sema.arena.create(Value.Payload.SubValue); 28781 payload.* = .{ 28782 .base = .{ .tag = .eu_payload }, 28783 .data = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(), 28784 }; 28785 28786 val_ptr.* = Value.initPayload(&payload.base); 28787 28788 return ComptimePtrMutationKit{ 28789 .mut_decl = parent.mut_decl, 28790 .pointee = .{ .direct = &payload.data }, 28791 .ty = payload_ty, 28792 }; 28793 } 28794 }, 28795 .bad_decl_ty, .bad_ptr_ty => return parent, 28796 // Even though the parent value type has well-defined memory layout, our 28797 // pointer type does not. 28798 .reinterpret => return ComptimePtrMutationKit{ 28799 .mut_decl = parent.mut_decl, 28800 .pointee = .bad_ptr_ty, 28801 .ty = eu_ty, 28802 }, 28803 } 28804 }, 28805 .opt_payload => |opt_ptr| { 28806 const opt_ty = mod.intern_pool.typeOf(opt_ptr).toType().childType(mod); 28807 var parent = try sema.beginComptimePtrMutation(block, src, opt_ptr.toValue(), opt_ty); 28808 switch (parent.pointee) { 28809 .direct => |val_ptr| { 28810 const payload_ty = parent.ty.optionalChild(mod); 28811 switch (val_ptr.ip_index) { 28812 .none => return ComptimePtrMutationKit{ 28813 .mut_decl = parent.mut_decl, 28814 .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data }, 28815 .ty = payload_ty, 28816 }, 28817 else => { 28818 const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) { 28819 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }), 28820 .opt => |opt| switch (opt.val) { 28821 .none => try mod.intern(.{ .undef = payload_ty.toIntern() }), 28822 else => |payload| payload, 28823 }, 28824 else => unreachable, 28825 }; 28826 28827 // An optional has been initialized to undefined at comptime and now we 28828 // are for the first time setting the payload. We must change the 28829 // representation of the optional from `undef` to `opt_payload`. 28830 28831 const payload = try sema.arena.create(Value.Payload.SubValue); 28832 payload.* = .{ 28833 .base = .{ .tag = .opt_payload }, 28834 .data = payload_val.toValue(), 28835 }; 28836 28837 val_ptr.* = Value.initPayload(&payload.base); 28838 28839 return ComptimePtrMutationKit{ 28840 .mut_decl = parent.mut_decl, 28841 .pointee = .{ .direct = &payload.data }, 28842 .ty = payload_ty, 28843 }; 28844 }, 28845 } 28846 }, 28847 .bad_decl_ty, .bad_ptr_ty => return parent, 28848 // Even though the parent value type has well-defined memory layout, our 28849 // pointer type does not. 28850 .reinterpret => return ComptimePtrMutationKit{ 28851 .mut_decl = parent.mut_decl, 28852 .pointee = .bad_ptr_ty, 28853 .ty = opt_ty, 28854 }, 28855 } 28856 }, 28857 .elem => |elem_ptr| { 28858 const base_elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod); 28859 var parent = try sema.beginComptimePtrMutation(block, src, elem_ptr.base.toValue(), base_elem_ty); 28860 28861 switch (parent.pointee) { 28862 .direct => |val_ptr| switch (parent.ty.zigTypeTag(mod)) { 28863 .Array, .Vector => { 28864 const check_len = parent.ty.arrayLenIncludingSentinel(mod); 28865 if (elem_ptr.index >= check_len) { 28866 // TODO have the parent include the decl so we can say "declared here" 28867 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{ 28868 elem_ptr.index, check_len, 28869 }); 28870 } 28871 const elem_ty = parent.ty.childType(mod); 28872 28873 // We might have a pointer to multiple elements of the array (e.g. a pointer 28874 // to a sub-array). In this case, we just have to reinterpret the relevant 28875 // bytes of the whole array rather than any single element. 28876 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty); 28877 if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) { 28878 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); 28879 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 28880 return .{ 28881 .mut_decl = parent.mut_decl, 28882 .pointee = .{ .reinterpret = .{ 28883 .val_ptr = val_ptr, 28884 .byte_offset = elem_abi_size * elem_idx, 28885 } }, 28886 .ty = parent.ty, 28887 }; 28888 } 28889 28890 switch (val_ptr.ip_index) { 28891 .none => switch (val_ptr.tag()) { 28892 .bytes => { 28893 // An array is memory-optimized to store a slice of bytes, but we are about 28894 // to modify an individual field and the representation has to change. 28895 // If we wanted to avoid this, there would need to be special detection 28896 // elsewhere to identify when writing a value to an array element that is stored 28897 // using the `bytes` tag, and handle it without making a call to this function. 28898 const arena = mod.tmp_hack_arena.allocator(); 28899 28900 const bytes = val_ptr.castTag(.bytes).?.data; 28901 const dest_len = parent.ty.arrayLenIncludingSentinel(mod); 28902 // bytes.len may be one greater than dest_len because of the case when 28903 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. 28904 assert(bytes.len >= dest_len); 28905 const elems = try arena.alloc(Value, @as(usize, @intCast(dest_len))); 28906 for (elems, 0..) |*elem, i| { 28907 elem.* = try mod.intValue(elem_ty, bytes[i]); 28908 } 28909 28910 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 28911 28912 return beginComptimePtrMutationInner( 28913 sema, 28914 block, 28915 src, 28916 elem_ty, 28917 &elems[@as(usize, @intCast(elem_ptr.index))], 28918 ptr_elem_ty, 28919 parent.mut_decl, 28920 ); 28921 }, 28922 .repeated => { 28923 // An array is memory-optimized to store only a single element value, and 28924 // that value is understood to be the same for the entire length of the array. 28925 // However, now we want to modify an individual field and so the 28926 // representation has to change. If we wanted to avoid this, there would 28927 // need to be special detection elsewhere to identify when writing a value to an 28928 // array element that is stored using the `repeated` tag, and handle it 28929 // without making a call to this function. 28930 const arena = mod.tmp_hack_arena.allocator(); 28931 28932 const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod); 28933 const array_len_including_sentinel = 28934 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); 28935 const elems = try arena.alloc(Value, array_len_including_sentinel); 28936 @memset(elems, repeated_val.toValue()); 28937 28938 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 28939 28940 return beginComptimePtrMutationInner( 28941 sema, 28942 block, 28943 src, 28944 elem_ty, 28945 &elems[@as(usize, @intCast(elem_ptr.index))], 28946 ptr_elem_ty, 28947 parent.mut_decl, 28948 ); 28949 }, 28950 28951 .aggregate => return beginComptimePtrMutationInner( 28952 sema, 28953 block, 28954 src, 28955 elem_ty, 28956 &val_ptr.castTag(.aggregate).?.data[@as(usize, @intCast(elem_ptr.index))], 28957 ptr_elem_ty, 28958 parent.mut_decl, 28959 ), 28960 28961 else => unreachable, 28962 }, 28963 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) { 28964 .undef => { 28965 // An array has been initialized to undefined at comptime and now we 28966 // are for the first time setting an element. We must change the representation 28967 // of the array from `undef` to `array`. 28968 const arena = mod.tmp_hack_arena.allocator(); 28969 28970 const array_len_including_sentinel = 28971 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); 28972 const elems = try arena.alloc(Value, array_len_including_sentinel); 28973 @memset(elems, (try mod.intern(.{ .undef = elem_ty.toIntern() })).toValue()); 28974 28975 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 28976 28977 return beginComptimePtrMutationInner( 28978 sema, 28979 block, 28980 src, 28981 elem_ty, 28982 &elems[@as(usize, @intCast(elem_ptr.index))], 28983 ptr_elem_ty, 28984 parent.mut_decl, 28985 ); 28986 }, 28987 else => unreachable, 28988 }, 28989 } 28990 }, 28991 else => { 28992 if (elem_ptr.index != 0) { 28993 // TODO include a "declared here" note for the decl 28994 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{ 28995 elem_ptr.index, 28996 }); 28997 } 28998 return beginComptimePtrMutationInner( 28999 sema, 29000 block, 29001 src, 29002 parent.ty, 29003 val_ptr, 29004 ptr_elem_ty, 29005 parent.mut_decl, 29006 ); 29007 }, 29008 }, 29009 .reinterpret => |reinterpret| { 29010 if (!base_elem_ty.hasWellDefinedLayout(mod)) { 29011 // Even though the parent value type has well-defined memory layout, our 29012 // pointer type does not. 29013 return ComptimePtrMutationKit{ 29014 .mut_decl = parent.mut_decl, 29015 .pointee = .bad_ptr_ty, 29016 .ty = base_elem_ty, 29017 }; 29018 } 29019 29020 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty); 29021 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); 29022 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 29023 return ComptimePtrMutationKit{ 29024 .mut_decl = parent.mut_decl, 29025 .pointee = .{ .reinterpret = .{ 29026 .val_ptr = reinterpret.val_ptr, 29027 .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_idx, 29028 } }, 29029 .ty = parent.ty, 29030 }; 29031 }, 29032 .bad_decl_ty, .bad_ptr_ty => return parent, 29033 } 29034 }, 29035 .field => |field_ptr| { 29036 const base_child_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod); 29037 const field_index = @as(u32, @intCast(field_ptr.index)); 29038 29039 var parent = try sema.beginComptimePtrMutation(block, src, field_ptr.base.toValue(), base_child_ty); 29040 switch (parent.pointee) { 29041 .direct => |val_ptr| switch (val_ptr.ip_index) { 29042 .empty_struct => { 29043 const duped = try sema.arena.create(Value); 29044 duped.* = val_ptr.*; 29045 return beginComptimePtrMutationInner( 29046 sema, 29047 block, 29048 src, 29049 parent.ty.structFieldType(field_index, mod), 29050 duped, 29051 ptr_elem_ty, 29052 parent.mut_decl, 29053 ); 29054 }, 29055 .none => switch (val_ptr.tag()) { 29056 .aggregate => return beginComptimePtrMutationInner( 29057 sema, 29058 block, 29059 src, 29060 parent.ty.structFieldType(field_index, mod), 29061 &val_ptr.castTag(.aggregate).?.data[field_index], 29062 ptr_elem_ty, 29063 parent.mut_decl, 29064 ), 29065 .repeated => { 29066 const arena = mod.tmp_hack_arena.allocator(); 29067 29068 const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod)); 29069 @memset(elems, val_ptr.castTag(.repeated).?.data); 29070 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 29071 29072 return beginComptimePtrMutationInner( 29073 sema, 29074 block, 29075 src, 29076 parent.ty.structFieldType(field_index, mod), 29077 &elems[field_index], 29078 ptr_elem_ty, 29079 parent.mut_decl, 29080 ); 29081 }, 29082 .@"union" => { 29083 // We need to set the active field of the union. 29084 const union_tag_ty = base_child_ty.unionTagTypeHypothetical(mod); 29085 29086 const payload = &val_ptr.castTag(.@"union").?.data; 29087 payload.tag = try mod.enumValueFieldIndex(union_tag_ty, field_index); 29088 29089 return beginComptimePtrMutationInner( 29090 sema, 29091 block, 29092 src, 29093 parent.ty.structFieldType(field_index, mod), 29094 &payload.val, 29095 ptr_elem_ty, 29096 parent.mut_decl, 29097 ); 29098 }, 29099 .slice => switch (field_index) { 29100 Value.slice_ptr_index => return beginComptimePtrMutationInner( 29101 sema, 29102 block, 29103 src, 29104 parent.ty.slicePtrFieldType(mod), 29105 &val_ptr.castTag(.slice).?.data.ptr, 29106 ptr_elem_ty, 29107 parent.mut_decl, 29108 ), 29109 29110 Value.slice_len_index => return beginComptimePtrMutationInner( 29111 sema, 29112 block, 29113 src, 29114 Type.usize, 29115 &val_ptr.castTag(.slice).?.data.len, 29116 ptr_elem_ty, 29117 parent.mut_decl, 29118 ), 29119 29120 else => unreachable, 29121 }, 29122 else => unreachable, 29123 }, 29124 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) { 29125 .undef => { 29126 // A struct or union has been initialized to undefined at comptime and now we 29127 // are for the first time setting a field. We must change the representation 29128 // of the struct/union from `undef` to `struct`/`union`. 29129 const arena = mod.tmp_hack_arena.allocator(); 29130 29131 switch (parent.ty.zigTypeTag(mod)) { 29132 .Struct => { 29133 const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod)); 29134 for (fields, 0..) |*field, i| field.* = (try mod.intern(.{ 29135 .undef = parent.ty.structFieldType(i, mod).toIntern(), 29136 })).toValue(); 29137 29138 val_ptr.* = try Value.Tag.aggregate.create(arena, fields); 29139 29140 return beginComptimePtrMutationInner( 29141 sema, 29142 block, 29143 src, 29144 parent.ty.structFieldType(field_index, mod), 29145 &fields[field_index], 29146 ptr_elem_ty, 29147 parent.mut_decl, 29148 ); 29149 }, 29150 .Union => { 29151 const payload = try arena.create(Value.Payload.Union); 29152 const tag_ty = parent.ty.unionTagTypeHypothetical(mod); 29153 const payload_ty = parent.ty.structFieldType(field_index, mod); 29154 payload.* = .{ .data = .{ 29155 .tag = try mod.enumValueFieldIndex(tag_ty, field_index), 29156 .val = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(), 29157 } }; 29158 29159 val_ptr.* = Value.initPayload(&payload.base); 29160 29161 return beginComptimePtrMutationInner( 29162 sema, 29163 block, 29164 src, 29165 payload_ty, 29166 &payload.data.val, 29167 ptr_elem_ty, 29168 parent.mut_decl, 29169 ); 29170 }, 29171 .Pointer => { 29172 assert(parent.ty.isSlice(mod)); 29173 const ptr_ty = parent.ty.slicePtrFieldType(mod); 29174 val_ptr.* = try Value.Tag.slice.create(arena, .{ 29175 .ptr = (try mod.intern(.{ .undef = ptr_ty.toIntern() })).toValue(), 29176 .len = (try mod.intern(.{ .undef = .usize_type })).toValue(), 29177 }); 29178 29179 switch (field_index) { 29180 Value.slice_ptr_index => return beginComptimePtrMutationInner( 29181 sema, 29182 block, 29183 src, 29184 ptr_ty, 29185 &val_ptr.castTag(.slice).?.data.ptr, 29186 ptr_elem_ty, 29187 parent.mut_decl, 29188 ), 29189 Value.slice_len_index => return beginComptimePtrMutationInner( 29190 sema, 29191 block, 29192 src, 29193 Type.usize, 29194 &val_ptr.castTag(.slice).?.data.len, 29195 ptr_elem_ty, 29196 parent.mut_decl, 29197 ), 29198 29199 else => unreachable, 29200 } 29201 }, 29202 else => unreachable, 29203 } 29204 }, 29205 else => unreachable, 29206 }, 29207 }, 29208 .reinterpret => |reinterpret| { 29209 const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod); 29210 const field_offset = try sema.usizeCast(block, src, field_offset_u64); 29211 return ComptimePtrMutationKit{ 29212 .mut_decl = parent.mut_decl, 29213 .pointee = .{ .reinterpret = .{ 29214 .val_ptr = reinterpret.val_ptr, 29215 .byte_offset = reinterpret.byte_offset + field_offset, 29216 } }, 29217 .ty = parent.ty, 29218 }; 29219 }, 29220 .bad_decl_ty, .bad_ptr_ty => return parent, 29221 } 29222 }, 29223 } 29224 } 29225 29226 fn beginComptimePtrMutationInner( 29227 sema: *Sema, 29228 block: *Block, 29229 src: LazySrcLoc, 29230 decl_ty: Type, 29231 decl_val: *Value, 29232 ptr_elem_ty: Type, 29233 mut_decl: InternPool.Key.Ptr.Addr.MutDecl, 29234 ) CompileError!ComptimePtrMutationKit { 29235 const mod = sema.mod; 29236 const target = mod.getTarget(); 29237 const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok; 29238 29239 decl_val.* = try decl_val.unintern(sema.arena, mod); 29240 29241 if (coerce_ok) { 29242 return ComptimePtrMutationKit{ 29243 .mut_decl = mut_decl, 29244 .pointee = .{ .direct = decl_val }, 29245 .ty = decl_ty, 29246 }; 29247 } 29248 29249 // Handle the case that the decl is an array and we're actually trying to point to an element. 29250 if (decl_ty.isArrayOrVector(mod)) { 29251 const decl_elem_ty = decl_ty.childType(mod); 29252 if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) { 29253 return ComptimePtrMutationKit{ 29254 .mut_decl = mut_decl, 29255 .pointee = .{ .direct = decl_val }, 29256 .ty = decl_ty, 29257 }; 29258 } 29259 } 29260 29261 if (!decl_ty.hasWellDefinedLayout(mod)) { 29262 return ComptimePtrMutationKit{ 29263 .mut_decl = mut_decl, 29264 .pointee = .bad_decl_ty, 29265 .ty = decl_ty, 29266 }; 29267 } 29268 if (!ptr_elem_ty.hasWellDefinedLayout(mod)) { 29269 return ComptimePtrMutationKit{ 29270 .mut_decl = mut_decl, 29271 .pointee = .bad_ptr_ty, 29272 .ty = ptr_elem_ty, 29273 }; 29274 } 29275 return ComptimePtrMutationKit{ 29276 .mut_decl = mut_decl, 29277 .pointee = .{ .reinterpret = .{ 29278 .val_ptr = decl_val, 29279 .byte_offset = 0, 29280 } }, 29281 .ty = decl_ty, 29282 }; 29283 } 29284 29285 const TypedValueAndOffset = struct { 29286 tv: TypedValue, 29287 byte_offset: usize, 29288 }; 29289 29290 const ComptimePtrLoadKit = struct { 29291 /// The Value and Type corresponding to the pointee of the provided pointer. 29292 /// If a direct dereference is not possible, this is null. 29293 pointee: ?TypedValue, 29294 /// The largest parent Value containing `pointee` and having a well-defined memory layout. 29295 /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null). 29296 parent: ?TypedValueAndOffset, 29297 /// Whether the `pointee` could be mutated by further 29298 /// semantic analysis and a copy must be performed. 29299 is_mutable: bool, 29300 /// If the root decl could not be used as `parent`, this is the type that 29301 /// caused that by not having a well-defined layout 29302 ty_without_well_defined_layout: ?Type, 29303 }; 29304 29305 const ComptimePtrLoadError = CompileError || error{ 29306 RuntimeLoad, 29307 }; 29308 29309 /// If `maybe_array_ty` is provided, it will be used to directly dereference an 29310 /// .elem_ptr of type T to a value of [N]T, if necessary. 29311 fn beginComptimePtrLoad( 29312 sema: *Sema, 29313 block: *Block, 29314 src: LazySrcLoc, 29315 ptr_val: Value, 29316 maybe_array_ty: ?Type, 29317 ) ComptimePtrLoadError!ComptimePtrLoadKit { 29318 const mod = sema.mod; 29319 const target = mod.getTarget(); 29320 29321 var deref: ComptimePtrLoadKit = switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) { 29322 .ptr => |ptr| switch (ptr.addr) { 29323 .decl, .mut_decl => blk: { 29324 const decl_index = switch (ptr.addr) { 29325 .decl => |decl| decl, 29326 .mut_decl => |mut_decl| mut_decl.decl, 29327 else => unreachable, 29328 }; 29329 const is_mutable = ptr.addr == .mut_decl; 29330 const decl = mod.declPtr(decl_index); 29331 const decl_tv = try decl.typedValue(); 29332 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad; 29333 29334 const layout_defined = decl.ty.hasWellDefinedLayout(mod); 29335 break :blk ComptimePtrLoadKit{ 29336 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, 29337 .pointee = decl_tv, 29338 .is_mutable = is_mutable, 29339 .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, 29340 }; 29341 }, 29342 .int => return error.RuntimeLoad, 29343 .eu_payload, .opt_payload => |container_ptr| blk: { 29344 const container_ty = mod.intern_pool.typeOf(container_ptr).toType().childType(mod); 29345 const payload_ty = switch (ptr.addr) { 29346 .eu_payload => container_ty.errorUnionPayload(mod), 29347 .opt_payload => container_ty.optionalChild(mod), 29348 else => unreachable, 29349 }; 29350 var deref = try sema.beginComptimePtrLoad(block, src, container_ptr.toValue(), container_ty); 29351 29352 // eu_payload and opt_payload never have a well-defined layout 29353 if (deref.parent != null) { 29354 deref.parent = null; 29355 deref.ty_without_well_defined_layout = container_ty; 29356 } 29357 29358 if (deref.pointee) |*tv| { 29359 const coerce_in_mem_ok = 29360 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or 29361 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok; 29362 if (coerce_in_mem_ok) { 29363 const payload_val = switch (tv.val.ip_index) { 29364 .none => tv.val.cast(Value.Payload.SubValue).?.data, 29365 .null_value => return sema.fail(block, src, "attempt to use null value", .{}), 29366 else => switch (mod.intern_pool.indexToKey(tv.val.toIntern())) { 29367 .error_union => |error_union| switch (error_union.val) { 29368 .err_name => |err_name| return sema.fail( 29369 block, 29370 src, 29371 "attempt to unwrap error: {}", 29372 .{err_name.fmt(&mod.intern_pool)}, 29373 ), 29374 .payload => |payload| payload, 29375 }, 29376 .opt => |opt| switch (opt.val) { 29377 .none => return sema.fail(block, src, "attempt to use null value", .{}), 29378 else => |payload| payload, 29379 }, 29380 else => unreachable, 29381 }.toValue(), 29382 }; 29383 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; 29384 break :blk deref; 29385 } 29386 } 29387 deref.pointee = null; 29388 break :blk deref; 29389 }, 29390 .comptime_field => |comptime_field| blk: { 29391 const field_ty = mod.intern_pool.typeOf(comptime_field).toType(); 29392 break :blk ComptimePtrLoadKit{ 29393 .parent = null, 29394 .pointee = .{ .ty = field_ty, .val = comptime_field.toValue() }, 29395 .is_mutable = false, 29396 .ty_without_well_defined_layout = field_ty, 29397 }; 29398 }, 29399 .elem => |elem_ptr| blk: { 29400 const elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod); 29401 var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.base.toValue(), null); 29402 29403 // This code assumes that elem_ptrs have been "flattened" in order for direct dereference 29404 // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that 29405 // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" 29406 switch (mod.intern_pool.indexToKey(elem_ptr.base)) { 29407 .ptr => |base_ptr| switch (base_ptr.addr) { 29408 .elem => |base_elem| assert(!mod.intern_pool.typeOf(base_elem.base).toType().elemType2(mod).eql(elem_ty, mod)), 29409 else => {}, 29410 }, 29411 else => {}, 29412 } 29413 29414 if (elem_ptr.index != 0) { 29415 if (elem_ty.hasWellDefinedLayout(mod)) { 29416 if (deref.parent) |*parent| { 29417 // Update the byte offset (in-place) 29418 const elem_size = try sema.typeAbiSize(elem_ty); 29419 const offset = parent.byte_offset + elem_size * elem_ptr.index; 29420 parent.byte_offset = try sema.usizeCast(block, src, offset); 29421 } 29422 } else { 29423 deref.parent = null; 29424 deref.ty_without_well_defined_layout = elem_ty; 29425 } 29426 } 29427 29428 // If we're loading an elem that was derived from a different type 29429 // than the true type of the underlying decl, we cannot deref directly 29430 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: { 29431 const deref_elem_ty = deref.pointee.?.ty.childType(mod); 29432 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or 29433 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; 29434 } else false; 29435 if (!ty_matches) { 29436 deref.pointee = null; 29437 break :blk deref; 29438 } 29439 29440 var array_tv = deref.pointee.?; 29441 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod); 29442 if (maybe_array_ty) |load_ty| { 29443 // It's possible that we're loading a [N]T, in which case we'd like to slice 29444 // the pointee array directly from our parent array. 29445 if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) { 29446 const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod)); 29447 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 29448 deref.pointee = if (elem_ptr.index + len <= check_len) TypedValue{ 29449 .ty = try mod.arrayType(.{ 29450 .len = len, 29451 .child = elem_ty.toIntern(), 29452 }), 29453 .val = try array_tv.val.sliceArray(mod, sema.arena, elem_idx, elem_idx + len), 29454 } else null; 29455 break :blk deref; 29456 } 29457 } 29458 29459 if (elem_ptr.index >= check_len) { 29460 deref.pointee = null; 29461 break :blk deref; 29462 } 29463 if (elem_ptr.index == check_len - 1) { 29464 if (array_tv.ty.sentinel(mod)) |sent| { 29465 deref.pointee = TypedValue{ 29466 .ty = elem_ty, 29467 .val = sent, 29468 }; 29469 break :blk deref; 29470 } 29471 } 29472 deref.pointee = TypedValue{ 29473 .ty = elem_ty, 29474 .val = try array_tv.val.elemValue(mod, @as(usize, @intCast(elem_ptr.index))), 29475 }; 29476 break :blk deref; 29477 }, 29478 .field => |field_ptr| blk: { 29479 const field_index = @as(u32, @intCast(field_ptr.index)); 29480 const container_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod); 29481 var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.base.toValue(), container_ty); 29482 29483 if (container_ty.hasWellDefinedLayout(mod)) { 29484 const struct_obj = mod.typeToStruct(container_ty); 29485 if (struct_obj != null and struct_obj.?.layout == .Packed) { 29486 // packed structs are not byte addressable 29487 deref.parent = null; 29488 } else if (deref.parent) |*parent| { 29489 // Update the byte offset (in-place) 29490 try sema.resolveTypeLayout(container_ty); 29491 const field_offset = container_ty.structFieldOffset(field_index, mod); 29492 parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset); 29493 } 29494 } else { 29495 deref.parent = null; 29496 deref.ty_without_well_defined_layout = container_ty; 29497 } 29498 29499 const tv = deref.pointee orelse { 29500 deref.pointee = null; 29501 break :blk deref; 29502 }; 29503 const coerce_in_mem_ok = 29504 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or 29505 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok; 29506 if (!coerce_in_mem_ok) { 29507 deref.pointee = null; 29508 break :blk deref; 29509 } 29510 29511 if (container_ty.isSlice(mod)) { 29512 deref.pointee = switch (field_index) { 29513 Value.slice_ptr_index => TypedValue{ 29514 .ty = container_ty.slicePtrFieldType(mod), 29515 .val = tv.val.slicePtr(mod), 29516 }, 29517 Value.slice_len_index => TypedValue{ 29518 .ty = Type.usize, 29519 .val = mod.intern_pool.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len.toValue(), 29520 }, 29521 else => unreachable, 29522 }; 29523 } else { 29524 const field_ty = container_ty.structFieldType(field_index, mod); 29525 deref.pointee = TypedValue{ 29526 .ty = field_ty, 29527 .val = try tv.val.fieldValue(mod, field_index), 29528 }; 29529 } 29530 break :blk deref; 29531 }, 29532 }, 29533 .opt => |opt| switch (opt.val) { 29534 .none => return sema.fail(block, src, "attempt to use null value", .{}), 29535 else => |payload| try sema.beginComptimePtrLoad(block, src, payload.toValue(), null), 29536 }, 29537 else => unreachable, 29538 }; 29539 29540 if (deref.pointee) |tv| { 29541 if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) { 29542 deref.parent = .{ .tv = tv, .byte_offset = 0 }; 29543 } 29544 } 29545 return deref; 29546 } 29547 29548 fn bitCast( 29549 sema: *Sema, 29550 block: *Block, 29551 dest_ty_unresolved: Type, 29552 inst: Air.Inst.Ref, 29553 inst_src: LazySrcLoc, 29554 operand_src: ?LazySrcLoc, 29555 ) CompileError!Air.Inst.Ref { 29556 const mod = sema.mod; 29557 const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); 29558 try sema.resolveTypeLayout(dest_ty); 29559 29560 const old_ty = try sema.resolveTypeFields(sema.typeOf(inst)); 29561 try sema.resolveTypeLayout(old_ty); 29562 29563 const dest_bits = dest_ty.bitSize(mod); 29564 const old_bits = old_ty.bitSize(mod); 29565 29566 if (old_bits != dest_bits) { 29567 return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{ 29568 dest_ty.fmt(mod), 29569 dest_bits, 29570 old_ty.fmt(mod), 29571 old_bits, 29572 }); 29573 } 29574 29575 if (try sema.resolveMaybeUndefVal(inst)) |val| { 29576 if (try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0)) |result_val| { 29577 return sema.addConstant(result_val); 29578 } 29579 } 29580 try sema.requireRuntimeBlock(block, inst_src, operand_src); 29581 return block.addBitCast(dest_ty, inst); 29582 } 29583 29584 fn bitCastVal( 29585 sema: *Sema, 29586 block: *Block, 29587 src: LazySrcLoc, 29588 val: Value, 29589 old_ty: Type, 29590 new_ty: Type, 29591 buffer_offset: usize, 29592 ) !?Value { 29593 const mod = sema.mod; 29594 if (old_ty.eql(new_ty, mod)) return val; 29595 29596 // For types with well-defined memory layouts, we serialize them a byte buffer, 29597 // then deserialize to the new type. 29598 const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(mod)); 29599 const buffer = try sema.gpa.alloc(u8, abi_size); 29600 defer sema.gpa.free(buffer); 29601 val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) { 29602 error.OutOfMemory => return error.OutOfMemory, 29603 error.ReinterpretDeclRef => return null, 29604 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 29605 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}), 29606 }; 29607 return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena); 29608 } 29609 29610 fn coerceArrayPtrToSlice( 29611 sema: *Sema, 29612 block: *Block, 29613 dest_ty: Type, 29614 inst: Air.Inst.Ref, 29615 inst_src: LazySrcLoc, 29616 ) CompileError!Air.Inst.Ref { 29617 const mod = sema.mod; 29618 if (try sema.resolveMaybeUndefVal(inst)) |val| { 29619 const ptr_array_ty = sema.typeOf(inst); 29620 const array_ty = ptr_array_ty.childType(mod); 29621 const slice_val = try mod.intern(.{ .ptr = .{ 29622 .ty = dest_ty.toIntern(), 29623 .addr = switch (mod.intern_pool.indexToKey(val.toIntern())) { 29624 .undef => .{ .int = try mod.intern(.{ .undef = .usize_type }) }, 29625 .ptr => |ptr| ptr.addr, 29626 else => unreachable, 29627 }, 29628 .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).toIntern(), 29629 } }); 29630 return sema.addConstant(slice_val.toValue()); 29631 } 29632 try sema.requireRuntimeBlock(block, inst_src, null); 29633 return block.addTyOp(.array_to_slice, dest_ty, inst); 29634 } 29635 29636 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool { 29637 const mod = sema.mod; 29638 const dest_info = dest_ty.ptrInfo(mod); 29639 const inst_info = inst_ty.ptrInfo(mod); 29640 const len0 = (inst_info.child.toType().zigTypeTag(mod) == .Array and (inst_info.child.toType().arrayLenIncludingSentinel(mod) == 0 or 29641 (inst_info.child.toType().arrayLen(mod) == 0 and dest_info.sentinel == .none and dest_info.flags.size != .C and dest_info.flags.size != .Many))) or 29642 (inst_info.child.toType().isTuple(mod) and inst_info.child.toType().structFieldCount(mod) == 0); 29643 29644 const ok_cv_qualifiers = 29645 ((!inst_info.flags.is_const or dest_info.flags.is_const) or len0) and 29646 (!inst_info.flags.is_volatile or dest_info.flags.is_volatile); 29647 29648 if (!ok_cv_qualifiers) { 29649 in_memory_result.* = .{ .ptr_qualifiers = .{ 29650 .actual_const = inst_info.flags.is_const, 29651 .wanted_const = dest_info.flags.is_const, 29652 .actual_volatile = inst_info.flags.is_volatile, 29653 .wanted_volatile = dest_info.flags.is_volatile, 29654 } }; 29655 return false; 29656 } 29657 if (dest_info.flags.address_space != inst_info.flags.address_space) { 29658 in_memory_result.* = .{ .ptr_addrspace = .{ 29659 .actual = inst_info.flags.address_space, 29660 .wanted = dest_info.flags.address_space, 29661 } }; 29662 return false; 29663 } 29664 if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true; 29665 if (len0) return true; 29666 29667 const inst_align = inst_info.flags.alignment.toByteUnitsOptional() orelse 29668 inst_info.child.toType().abiAlignment(mod); 29669 29670 const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse 29671 dest_info.child.toType().abiAlignment(mod); 29672 29673 if (dest_align > inst_align) { 29674 in_memory_result.* = .{ .ptr_alignment = .{ 29675 .actual = inst_align, 29676 .wanted = dest_align, 29677 } }; 29678 return false; 29679 } 29680 return true; 29681 } 29682 29683 fn coerceCompatiblePtrs( 29684 sema: *Sema, 29685 block: *Block, 29686 dest_ty: Type, 29687 inst: Air.Inst.Ref, 29688 inst_src: LazySrcLoc, 29689 ) !Air.Inst.Ref { 29690 const mod = sema.mod; 29691 const inst_ty = sema.typeOf(inst); 29692 if (try sema.resolveMaybeUndefVal(inst)) |val| { 29693 if (!val.isUndef(mod) and val.isNull(mod) and !dest_ty.isAllowzeroPtr(mod)) { 29694 return sema.fail(block, inst_src, "null pointer casted to type '{}'", .{dest_ty.fmt(sema.mod)}); 29695 } 29696 // The comptime Value representation is compatible with both types. 29697 return sema.addConstant( 29698 try mod.getCoerced((try val.intern(inst_ty, mod)).toValue(), dest_ty), 29699 ); 29700 } 29701 try sema.requireRuntimeBlock(block, inst_src, null); 29702 const inst_allows_zero = inst_ty.zigTypeTag(mod) != .Pointer or inst_ty.ptrAllowsZero(mod); 29703 if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(mod) and 29704 (try sema.typeHasRuntimeBits(dest_ty.elemType2(mod)) or dest_ty.elemType2(mod).zigTypeTag(mod) == .Fn)) 29705 { 29706 const actual_ptr = if (inst_ty.isSlice(mod)) 29707 try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) 29708 else 29709 inst; 29710 const ptr_int = try block.addUnOp(.int_from_ptr, actual_ptr); 29711 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); 29712 const ok = if (inst_ty.isSlice(mod)) ok: { 29713 const len = try sema.analyzeSliceLen(block, inst_src, inst); 29714 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 29715 break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero); 29716 } else is_non_zero; 29717 try sema.addSafetyCheck(block, ok, .cast_to_null); 29718 } 29719 return sema.bitCast(block, dest_ty, inst, inst_src, null); 29720 } 29721 29722 fn coerceEnumToUnion( 29723 sema: *Sema, 29724 block: *Block, 29725 union_ty: Type, 29726 union_ty_src: LazySrcLoc, 29727 inst: Air.Inst.Ref, 29728 inst_src: LazySrcLoc, 29729 ) !Air.Inst.Ref { 29730 const mod = sema.mod; 29731 const ip = &mod.intern_pool; 29732 const inst_ty = sema.typeOf(inst); 29733 29734 const tag_ty = union_ty.unionTagType(mod) orelse { 29735 const msg = msg: { 29736 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 29737 union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), 29738 }); 29739 errdefer msg.destroy(sema.gpa); 29740 try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{}); 29741 try sema.addDeclaredHereNote(msg, union_ty); 29742 break :msg msg; 29743 }; 29744 return sema.failWithOwnedErrorMsg(msg); 29745 }; 29746 29747 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src); 29748 if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { 29749 const field_index = union_ty.unionTagFieldIndex(val, sema.mod) orelse { 29750 const msg = msg: { 29751 const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{ 29752 union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod), 29753 }); 29754 errdefer msg.destroy(sema.gpa); 29755 try sema.addDeclaredHereNote(msg, union_ty); 29756 break :msg msg; 29757 }; 29758 return sema.failWithOwnedErrorMsg(msg); 29759 }; 29760 29761 const union_obj = mod.typeToUnion(union_ty).?; 29762 const field = union_obj.fields.values()[field_index]; 29763 const field_ty = try sema.resolveTypeFields(field.ty); 29764 if (field_ty.zigTypeTag(mod) == .NoReturn) { 29765 const msg = msg: { 29766 const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{}); 29767 errdefer msg.destroy(sema.gpa); 29768 29769 const field_name = union_obj.fields.keys()[field_index]; 29770 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 29771 field_name.fmt(ip), 29772 }); 29773 try sema.addDeclaredHereNote(msg, union_ty); 29774 break :msg msg; 29775 }; 29776 return sema.failWithOwnedErrorMsg(msg); 29777 } 29778 const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { 29779 const msg = msg: { 29780 const field_name = union_obj.fields.keys()[field_index]; 29781 const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{ 29782 inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), 29783 field_ty.fmt(sema.mod), field_name.fmt(ip), 29784 }); 29785 errdefer msg.destroy(sema.gpa); 29786 29787 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 29788 field_name.fmt(ip), 29789 }); 29790 try sema.addDeclaredHereNote(msg, union_ty); 29791 break :msg msg; 29792 }; 29793 return sema.failWithOwnedErrorMsg(msg); 29794 }; 29795 29796 return sema.addConstant(try mod.unionValue(union_ty, val, opv)); 29797 } 29798 29799 try sema.requireRuntimeBlock(block, inst_src, null); 29800 29801 if (tag_ty.isNonexhaustiveEnum(mod)) { 29802 const msg = msg: { 29803 const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{ 29804 union_ty.fmt(sema.mod), 29805 }); 29806 errdefer msg.destroy(sema.gpa); 29807 try sema.addDeclaredHereNote(msg, tag_ty); 29808 break :msg msg; 29809 }; 29810 return sema.failWithOwnedErrorMsg(msg); 29811 } 29812 29813 const union_obj = mod.typeToUnion(union_ty).?; 29814 { 29815 var msg: ?*Module.ErrorMsg = null; 29816 errdefer if (msg) |some| some.destroy(sema.gpa); 29817 29818 for (union_obj.fields.values(), 0..) |field, i| { 29819 if (field.ty.zigTypeTag(mod) == .NoReturn) { 29820 const err_msg = msg orelse try sema.errMsg( 29821 block, 29822 inst_src, 29823 "runtime coercion from enum '{}' to union '{}' which has a 'noreturn' field", 29824 .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) }, 29825 ); 29826 msg = err_msg; 29827 29828 try sema.addFieldErrNote(union_ty, i, err_msg, "'noreturn' field here", .{}); 29829 } 29830 } 29831 if (msg) |some| { 29832 msg = null; 29833 try sema.addDeclaredHereNote(some, union_ty); 29834 return sema.failWithOwnedErrorMsg(some); 29835 } 29836 } 29837 29838 // If the union has all fields 0 bits, the union value is just the enum value. 29839 if (union_ty.unionHasAllZeroBitFieldTypes(mod)) { 29840 return block.addBitCast(union_ty, enum_tag); 29841 } 29842 29843 const msg = msg: { 29844 const msg = try sema.errMsg( 29845 block, 29846 inst_src, 29847 "runtime coercion from enum '{}' to union '{}' which has non-void fields", 29848 .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) }, 29849 ); 29850 errdefer msg.destroy(sema.gpa); 29851 29852 var it = union_obj.fields.iterator(); 29853 var field_index: usize = 0; 29854 while (it.next()) |field| : (field_index += 1) { 29855 const field_name = field.key_ptr.*; 29856 const field_ty = field.value_ptr.ty; 29857 if (!(try sema.typeHasRuntimeBits(field_ty))) continue; 29858 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{ 29859 field_name.fmt(ip), 29860 field_ty.fmt(sema.mod), 29861 }); 29862 } 29863 try sema.addDeclaredHereNote(msg, union_ty); 29864 break :msg msg; 29865 }; 29866 return sema.failWithOwnedErrorMsg(msg); 29867 } 29868 29869 fn coerceAnonStructToUnion( 29870 sema: *Sema, 29871 block: *Block, 29872 union_ty: Type, 29873 union_ty_src: LazySrcLoc, 29874 inst: Air.Inst.Ref, 29875 inst_src: LazySrcLoc, 29876 ) !Air.Inst.Ref { 29877 const mod = sema.mod; 29878 const inst_ty = sema.typeOf(inst); 29879 const field_info: union(enum) { 29880 name: InternPool.NullTerminatedString, 29881 count: usize, 29882 } = switch (mod.intern_pool.indexToKey(inst_ty.toIntern())) { 29883 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1) 29884 .{ .name = anon_struct_type.names[0] } 29885 else 29886 .{ .count = anon_struct_type.names.len }, 29887 .struct_type => |struct_type| name: { 29888 const field_names = mod.structPtrUnwrap(struct_type.index).?.fields.keys(); 29889 break :name if (field_names.len == 1) 29890 .{ .name = field_names[0] } 29891 else 29892 .{ .count = field_names.len }; 29893 }, 29894 else => unreachable, 29895 }; 29896 switch (field_info) { 29897 .name => |field_name| { 29898 const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty); 29899 return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src); 29900 }, 29901 .count => |field_count| { 29902 assert(field_count != 1); 29903 const msg = msg: { 29904 const msg = if (field_count > 1) try sema.errMsg( 29905 block, 29906 inst_src, 29907 "cannot initialize multiple union fields at once; unions can only have one active field", 29908 .{}, 29909 ) else try sema.errMsg( 29910 block, 29911 inst_src, 29912 "union initializer must initialize one field", 29913 .{}, 29914 ); 29915 errdefer msg.destroy(sema.gpa); 29916 29917 // TODO add notes for where the anon struct was created to point out 29918 // the extra fields. 29919 29920 try sema.addDeclaredHereNote(msg, union_ty); 29921 break :msg msg; 29922 }; 29923 return sema.failWithOwnedErrorMsg(msg); 29924 }, 29925 } 29926 } 29927 29928 fn coerceAnonStructToUnionPtrs( 29929 sema: *Sema, 29930 block: *Block, 29931 ptr_union_ty: Type, 29932 union_ty_src: LazySrcLoc, 29933 ptr_anon_struct: Air.Inst.Ref, 29934 anon_struct_src: LazySrcLoc, 29935 ) !Air.Inst.Ref { 29936 const mod = sema.mod; 29937 const union_ty = ptr_union_ty.childType(mod); 29938 const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); 29939 const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src); 29940 return sema.analyzeRef(block, union_ty_src, union_inst); 29941 } 29942 29943 fn coerceAnonStructToStructPtrs( 29944 sema: *Sema, 29945 block: *Block, 29946 ptr_struct_ty: Type, 29947 struct_ty_src: LazySrcLoc, 29948 ptr_anon_struct: Air.Inst.Ref, 29949 anon_struct_src: LazySrcLoc, 29950 ) !Air.Inst.Ref { 29951 const mod = sema.mod; 29952 const struct_ty = ptr_struct_ty.childType(mod); 29953 const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); 29954 const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, anon_struct, anon_struct_src); 29955 return sema.analyzeRef(block, struct_ty_src, struct_inst); 29956 } 29957 29958 /// If the lengths match, coerces element-wise. 29959 fn coerceArrayLike( 29960 sema: *Sema, 29961 block: *Block, 29962 dest_ty: Type, 29963 dest_ty_src: LazySrcLoc, 29964 inst: Air.Inst.Ref, 29965 inst_src: LazySrcLoc, 29966 ) !Air.Inst.Ref { 29967 const mod = sema.mod; 29968 const inst_ty = sema.typeOf(inst); 29969 const inst_len = inst_ty.arrayLen(mod); 29970 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod)); 29971 const target = mod.getTarget(); 29972 29973 if (dest_len != inst_len) { 29974 const msg = msg: { 29975 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 29976 dest_ty.fmt(mod), inst_ty.fmt(mod), 29977 }); 29978 errdefer msg.destroy(sema.gpa); 29979 try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); 29980 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len}); 29981 break :msg msg; 29982 }; 29983 return sema.failWithOwnedErrorMsg(msg); 29984 } 29985 29986 const dest_elem_ty = dest_ty.childType(mod); 29987 const inst_elem_ty = inst_ty.childType(mod); 29988 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src); 29989 if (in_memory_result == .ok) { 29990 if (try sema.resolveMaybeUndefVal(inst)) |inst_val| { 29991 // These types share the same comptime value representation. 29992 return sema.coerceInMemory(inst_val, dest_ty); 29993 } 29994 try sema.requireRuntimeBlock(block, inst_src, null); 29995 return block.addBitCast(dest_ty, inst); 29996 } 29997 29998 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); 29999 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); 30000 var runtime_src: ?LazySrcLoc = null; 30001 30002 for (element_vals, element_refs, 0..) |*val, *ref, i| { 30003 const index_ref = try sema.addConstant(try mod.intValue(Type.usize, i)); 30004 const src = inst_src; // TODO better source location 30005 const elem_src = inst_src; // TODO better source location 30006 const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true); 30007 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 30008 ref.* = coerced; 30009 if (runtime_src == null) { 30010 if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| { 30011 val.* = try elem_val.intern(dest_elem_ty, mod); 30012 } else { 30013 runtime_src = elem_src; 30014 } 30015 } 30016 } 30017 30018 if (runtime_src) |rs| { 30019 try sema.requireRuntimeBlock(block, inst_src, rs); 30020 return block.addAggregateInit(dest_ty, element_refs); 30021 } 30022 30023 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 30024 .ty = dest_ty.toIntern(), 30025 .storage = .{ .elems = element_vals }, 30026 } })).toValue()); 30027 } 30028 30029 /// If the lengths match, coerces element-wise. 30030 fn coerceTupleToArray( 30031 sema: *Sema, 30032 block: *Block, 30033 dest_ty: Type, 30034 dest_ty_src: LazySrcLoc, 30035 inst: Air.Inst.Ref, 30036 inst_src: LazySrcLoc, 30037 ) !Air.Inst.Ref { 30038 const mod = sema.mod; 30039 const inst_ty = sema.typeOf(inst); 30040 const inst_len = inst_ty.arrayLen(mod); 30041 const dest_len = dest_ty.arrayLen(mod); 30042 30043 if (dest_len != inst_len) { 30044 const msg = msg: { 30045 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 30046 dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), 30047 }); 30048 errdefer msg.destroy(sema.gpa); 30049 try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); 30050 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len}); 30051 break :msg msg; 30052 }; 30053 return sema.failWithOwnedErrorMsg(msg); 30054 } 30055 30056 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len); 30057 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems); 30058 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); 30059 const dest_elem_ty = dest_ty.childType(mod); 30060 30061 var runtime_src: ?LazySrcLoc = null; 30062 for (element_vals, element_refs, 0..) |*val, *ref, i_usize| { 30063 const i = @as(u32, @intCast(i_usize)); 30064 if (i_usize == inst_len) { 30065 const sentinel_val = dest_ty.sentinel(mod).?; 30066 val.* = sentinel_val.toIntern(); 30067 ref.* = try sema.addConstant(sentinel_val); 30068 break; 30069 } 30070 const elem_src = inst_src; // TODO better source location 30071 const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i); 30072 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 30073 ref.* = coerced; 30074 if (runtime_src == null) { 30075 if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| { 30076 val.* = try elem_val.intern(dest_elem_ty, mod); 30077 } else { 30078 runtime_src = elem_src; 30079 } 30080 } 30081 } 30082 30083 if (runtime_src) |rs| { 30084 try sema.requireRuntimeBlock(block, inst_src, rs); 30085 return block.addAggregateInit(dest_ty, element_refs); 30086 } 30087 30088 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 30089 .ty = dest_ty.toIntern(), 30090 .storage = .{ .elems = element_vals }, 30091 } })).toValue()); 30092 } 30093 30094 /// If the lengths match, coerces element-wise. 30095 fn coerceTupleToSlicePtrs( 30096 sema: *Sema, 30097 block: *Block, 30098 slice_ty: Type, 30099 slice_ty_src: LazySrcLoc, 30100 ptr_tuple: Air.Inst.Ref, 30101 tuple_src: LazySrcLoc, 30102 ) !Air.Inst.Ref { 30103 const mod = sema.mod; 30104 const tuple_ty = sema.typeOf(ptr_tuple).childType(mod); 30105 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 30106 const slice_info = slice_ty.ptrInfo(mod); 30107 const array_ty = try mod.arrayType(.{ 30108 .len = tuple_ty.structFieldCount(mod), 30109 .sentinel = slice_info.sentinel, 30110 .child = slice_info.child, 30111 }); 30112 const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); 30113 if (slice_info.flags.alignment != .none) { 30114 return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 30115 } 30116 const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst); 30117 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src); 30118 } 30119 30120 /// If the lengths match, coerces element-wise. 30121 fn coerceTupleToArrayPtrs( 30122 sema: *Sema, 30123 block: *Block, 30124 ptr_array_ty: Type, 30125 array_ty_src: LazySrcLoc, 30126 ptr_tuple: Air.Inst.Ref, 30127 tuple_src: LazySrcLoc, 30128 ) !Air.Inst.Ref { 30129 const mod = sema.mod; 30130 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 30131 const ptr_info = ptr_array_ty.ptrInfo(mod); 30132 const array_ty = ptr_info.child.toType(); 30133 const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src); 30134 if (ptr_info.flags.alignment != .none) { 30135 return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 30136 } 30137 const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst); 30138 return ptr_array; 30139 } 30140 30141 /// Handles both tuples and anon struct literals. Coerces field-wise. Reports 30142 /// errors for both extra fields and missing fields. 30143 fn coerceTupleToStruct( 30144 sema: *Sema, 30145 block: *Block, 30146 dest_ty: Type, 30147 inst: Air.Inst.Ref, 30148 inst_src: LazySrcLoc, 30149 ) !Air.Inst.Ref { 30150 const mod = sema.mod; 30151 const ip = &mod.intern_pool; 30152 const struct_ty = try sema.resolveTypeFields(dest_ty); 30153 30154 if (struct_ty.isTupleOrAnonStruct(mod)) { 30155 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src); 30156 } 30157 30158 const fields = struct_ty.structFields(mod); 30159 const field_vals = try sema.arena.alloc(InternPool.Index, fields.count()); 30160 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); 30161 @memset(field_refs, .none); 30162 30163 const inst_ty = sema.typeOf(inst); 30164 var runtime_src: ?LazySrcLoc = null; 30165 const field_count = switch (ip.indexToKey(inst_ty.toIntern())) { 30166 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 30167 .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| 30168 struct_obj.fields.count() 30169 else 30170 0, 30171 else => unreachable, 30172 }; 30173 for (0..field_count) |field_index_usize| { 30174 const field_i = @as(u32, @intCast(field_index_usize)); 30175 const field_src = inst_src; // TODO better source location 30176 // https://github.com/ziglang/zig/issues/15709 30177 const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { 30178 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) 30179 anon_struct_type.names[field_i] 30180 else 30181 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), 30182 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i], 30183 else => unreachable, 30184 }; 30185 const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); 30186 const field = fields.values()[field_index]; 30187 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); 30188 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src); 30189 field_refs[field_index] = coerced; 30190 if (field.is_comptime) { 30191 const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { 30192 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); 30193 }; 30194 30195 if (!init_val.eql(field.default_val.toValue(), field.ty, sema.mod)) { 30196 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); 30197 } 30198 } 30199 if (runtime_src == null) { 30200 if (try sema.resolveMaybeUndefVal(coerced)) |field_val| { 30201 field_vals[field_index] = field_val.toIntern(); 30202 } else { 30203 runtime_src = field_src; 30204 } 30205 } 30206 } 30207 30208 // Populate default field values and report errors for missing fields. 30209 var root_msg: ?*Module.ErrorMsg = null; 30210 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 30211 30212 for (field_refs, 0..) |*field_ref, i| { 30213 if (field_ref.* != .none) continue; 30214 30215 const field_name = fields.keys()[i]; 30216 const field = fields.values()[i]; 30217 const field_src = inst_src; // TODO better source location 30218 if (field.default_val == .none) { 30219 const template = "missing struct field: {}"; 30220 const args = .{field_name.fmt(ip)}; 30221 if (root_msg) |msg| { 30222 try sema.errNote(block, field_src, msg, template, args); 30223 } else { 30224 root_msg = try sema.errMsg(block, field_src, template, args); 30225 } 30226 continue; 30227 } 30228 if (runtime_src == null) { 30229 field_vals[i] = field.default_val; 30230 } else { 30231 field_ref.* = try sema.addConstant(field.default_val.toValue()); 30232 } 30233 } 30234 30235 if (root_msg) |msg| { 30236 try sema.addDeclaredHereNote(msg, struct_ty); 30237 root_msg = null; 30238 return sema.failWithOwnedErrorMsg(msg); 30239 } 30240 30241 if (runtime_src) |rs| { 30242 try sema.requireRuntimeBlock(block, inst_src, rs); 30243 return block.addAggregateInit(struct_ty, field_refs); 30244 } 30245 30246 const struct_val = try mod.intern(.{ .aggregate = .{ 30247 .ty = struct_ty.toIntern(), 30248 .storage = .{ .elems = field_vals }, 30249 } }); 30250 // TODO: figure out InternPool removals for incremental compilation 30251 //errdefer ip.remove(struct_val); 30252 30253 return sema.addConstant(struct_val.toValue()); 30254 } 30255 30256 fn coerceTupleToTuple( 30257 sema: *Sema, 30258 block: *Block, 30259 tuple_ty: Type, 30260 inst: Air.Inst.Ref, 30261 inst_src: LazySrcLoc, 30262 ) !Air.Inst.Ref { 30263 const mod = sema.mod; 30264 const ip = &mod.intern_pool; 30265 const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) { 30266 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 30267 .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| 30268 struct_obj.fields.count() 30269 else 30270 0, 30271 else => unreachable, 30272 }; 30273 const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); 30274 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); 30275 @memset(field_refs, .none); 30276 30277 const inst_ty = sema.typeOf(inst); 30278 const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) { 30279 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 30280 .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| 30281 struct_obj.fields.count() 30282 else 30283 0, 30284 else => unreachable, 30285 }; 30286 if (src_field_count > dest_field_count) return error.NotCoercible; 30287 30288 var runtime_src: ?LazySrcLoc = null; 30289 for (0..dest_field_count) |field_index_usize| { 30290 const field_i = @as(u32, @intCast(field_index_usize)); 30291 const field_src = inst_src; // TODO better source location 30292 // https://github.com/ziglang/zig/issues/15709 30293 const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { 30294 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) 30295 anon_struct_type.names[field_i] 30296 else 30297 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), 30298 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i], 30299 else => unreachable, 30300 }; 30301 30302 if (ip.stringEqlSlice(field_name, "len")) 30303 return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{}); 30304 30305 const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { 30306 .anon_struct_type => |anon_struct_type| anon_struct_type.types[field_index_usize].toType(), 30307 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].ty, 30308 else => unreachable, 30309 }; 30310 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 30311 .anon_struct_type => |anon_struct_type| anon_struct_type.values[field_index_usize], 30312 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].default_val, 30313 else => unreachable, 30314 }; 30315 30316 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src); 30317 30318 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); 30319 const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); 30320 field_refs[field_index] = coerced; 30321 if (default_val != .none) { 30322 const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { 30323 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); 30324 }; 30325 30326 if (!init_val.eql(default_val.toValue(), field_ty, sema.mod)) { 30327 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); 30328 } 30329 } 30330 if (runtime_src == null) { 30331 if (try sema.resolveMaybeUndefVal(coerced)) |field_val| { 30332 field_vals[field_index] = field_val.toIntern(); 30333 } else { 30334 runtime_src = field_src; 30335 } 30336 } 30337 } 30338 30339 // Populate default field values and report errors for missing fields. 30340 var root_msg: ?*Module.ErrorMsg = null; 30341 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 30342 30343 for (field_refs, 0..) |*field_ref, i| { 30344 if (field_ref.* != .none) continue; 30345 30346 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 30347 .anon_struct_type => |anon_struct_type| anon_struct_type.values[i], 30348 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[i].default_val, 30349 else => unreachable, 30350 }; 30351 30352 const field_src = inst_src; // TODO better source location 30353 if (default_val == .none) { 30354 if (tuple_ty.isTuple(mod)) { 30355 const template = "missing tuple field: {d}"; 30356 if (root_msg) |msg| { 30357 try sema.errNote(block, field_src, msg, template, .{i}); 30358 } else { 30359 root_msg = try sema.errMsg(block, field_src, template, .{i}); 30360 } 30361 continue; 30362 } 30363 const template = "missing struct field: {}"; 30364 const args = .{tuple_ty.structFieldName(i, mod).fmt(ip)}; 30365 if (root_msg) |msg| { 30366 try sema.errNote(block, field_src, msg, template, args); 30367 } else { 30368 root_msg = try sema.errMsg(block, field_src, template, args); 30369 } 30370 continue; 30371 } 30372 if (runtime_src == null) { 30373 field_vals[i] = default_val; 30374 } else { 30375 field_ref.* = try sema.addConstant(default_val.toValue()); 30376 } 30377 } 30378 30379 if (root_msg) |msg| { 30380 try sema.addDeclaredHereNote(msg, tuple_ty); 30381 root_msg = null; 30382 return sema.failWithOwnedErrorMsg(msg); 30383 } 30384 30385 if (runtime_src) |rs| { 30386 try sema.requireRuntimeBlock(block, inst_src, rs); 30387 return block.addAggregateInit(tuple_ty, field_refs); 30388 } 30389 30390 return sema.addConstant( 30391 (try mod.intern(.{ .aggregate = .{ 30392 .ty = tuple_ty.toIntern(), 30393 .storage = .{ .elems = field_vals }, 30394 } })).toValue(), 30395 ); 30396 } 30397 30398 fn analyzeDeclVal( 30399 sema: *Sema, 30400 block: *Block, 30401 src: LazySrcLoc, 30402 decl_index: Decl.Index, 30403 ) CompileError!Air.Inst.Ref { 30404 try sema.addReferencedBy(block, src, decl_index); 30405 if (sema.decl_val_table.get(decl_index)) |result| { 30406 return result; 30407 } 30408 const decl_ref = try sema.analyzeDeclRefInner(decl_index, false); 30409 const result = try sema.analyzeLoad(block, src, decl_ref, src); 30410 if (Air.refToInterned(result) != null) { 30411 if (!block.is_typeof) { 30412 try sema.decl_val_table.put(sema.gpa, decl_index, result); 30413 } 30414 } 30415 return result; 30416 } 30417 30418 fn addReferencedBy( 30419 sema: *Sema, 30420 block: *Block, 30421 src: LazySrcLoc, 30422 decl_index: Decl.Index, 30423 ) !void { 30424 if (sema.mod.comp.reference_trace == @as(u32, 0)) return; 30425 try sema.mod.reference_table.put(sema.gpa, decl_index, .{ 30426 .referencer = block.src_decl, 30427 .src = src, 30428 }); 30429 } 30430 30431 fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void { 30432 const mod = sema.mod; 30433 const decl = mod.declPtr(decl_index); 30434 if (decl.analysis == .in_progress) { 30435 const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(mod), "dependency loop detected", .{}); 30436 return sema.failWithOwnedErrorMsg(msg); 30437 } 30438 30439 mod.ensureDeclAnalyzed(decl_index) catch |err| { 30440 if (sema.owner_func) |owner_func| { 30441 owner_func.state = .dependency_failure; 30442 } else { 30443 sema.owner_decl.analysis = .dependency_failure; 30444 } 30445 return err; 30446 }; 30447 } 30448 30449 fn ensureFuncBodyAnalyzed(sema: *Sema, func: Module.Fn.Index) CompileError!void { 30450 sema.mod.ensureFuncBodyAnalyzed(func) catch |err| { 30451 if (sema.owner_func) |owner_func| { 30452 owner_func.state = .dependency_failure; 30453 } else { 30454 sema.owner_decl.analysis = .dependency_failure; 30455 } 30456 return err; 30457 }; 30458 } 30459 30460 fn refValue(sema: *Sema, block: *Block, ty: Type, val: Value) !Value { 30461 const mod = sema.mod; 30462 var anon_decl = try block.startAnonDecl(); 30463 defer anon_decl.deinit(); 30464 const decl = try anon_decl.finish( 30465 ty, 30466 val, 30467 .none, // default alignment 30468 ); 30469 try sema.maybeQueueFuncBodyAnalysis(decl); 30470 try mod.declareDeclDependency(sema.owner_decl_index, decl); 30471 const result = try mod.intern(.{ .ptr = .{ 30472 .ty = (try mod.singleConstPtrType(ty)).toIntern(), 30473 .addr = .{ .decl = decl }, 30474 } }); 30475 return result.toValue(); 30476 } 30477 30478 fn optRefValue(sema: *Sema, block: *Block, ty: Type, opt_val: ?Value) !Value { 30479 const mod = sema.mod; 30480 const ptr_anyopaque_ty = try mod.singleConstPtrType(Type.anyopaque); 30481 return (try mod.intern(.{ .opt = .{ 30482 .ty = (try mod.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(), 30483 .val = if (opt_val) |val| (try mod.getCoerced( 30484 try sema.refValue(block, ty, val), 30485 ptr_anyopaque_ty, 30486 )).toIntern() else .none, 30487 } })).toValue(); 30488 } 30489 30490 fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref { 30491 return sema.analyzeDeclRefInner(decl_index, true); 30492 } 30493 30494 /// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but 30495 /// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a 30496 /// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps 30497 /// this function with `analyze_fn_body` set to true. 30498 fn analyzeDeclRefInner(sema: *Sema, decl_index: Decl.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref { 30499 const mod = sema.mod; 30500 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 30501 try sema.ensureDeclAnalyzed(decl_index); 30502 30503 const decl = mod.declPtr(decl_index); 30504 const decl_tv = try decl.typedValue(); 30505 const ptr_ty = try mod.ptrType(.{ 30506 .child = decl_tv.ty.toIntern(), 30507 .flags = .{ 30508 .alignment = decl.alignment, 30509 .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true, 30510 .address_space = decl.@"addrspace", 30511 }, 30512 }); 30513 if (analyze_fn_body) { 30514 try sema.maybeQueueFuncBodyAnalysis(decl_index); 30515 } 30516 return sema.addConstant((try mod.intern(.{ .ptr = .{ 30517 .ty = ptr_ty.toIntern(), 30518 .addr = .{ .decl = decl_index }, 30519 } })).toValue()); 30520 } 30521 30522 fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: Decl.Index) !void { 30523 const mod = sema.mod; 30524 const decl = mod.declPtr(decl_index); 30525 const tv = try decl.typedValue(); 30526 if (tv.ty.zigTypeTag(mod) != .Fn) return; 30527 if (!try sema.fnHasRuntimeBits(tv.ty)) return; 30528 const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap() orelse return; // undef or extern_fn 30529 try mod.ensureFuncBodyAnalysisQueued(func_index); 30530 } 30531 30532 fn analyzeRef( 30533 sema: *Sema, 30534 block: *Block, 30535 src: LazySrcLoc, 30536 operand: Air.Inst.Ref, 30537 ) CompileError!Air.Inst.Ref { 30538 const mod = sema.mod; 30539 const operand_ty = sema.typeOf(operand); 30540 30541 if (try sema.resolveMaybeUndefVal(operand)) |val| { 30542 switch (mod.intern_pool.indexToKey(val.toIntern())) { 30543 .extern_func => |extern_func| return sema.analyzeDeclRef(extern_func.decl), 30544 .func => |func| return sema.analyzeDeclRef(mod.funcPtr(func.index).owner_decl), 30545 else => {}, 30546 } 30547 var anon_decl = try block.startAnonDecl(); 30548 defer anon_decl.deinit(); 30549 return sema.analyzeDeclRef(try anon_decl.finish( 30550 operand_ty, 30551 val, 30552 .none, // default alignment 30553 )); 30554 } 30555 30556 try sema.requireRuntimeBlock(block, src, null); 30557 const address_space = target_util.defaultAddressSpace(mod.getTarget(), .local); 30558 const ptr_type = try mod.ptrType(.{ 30559 .child = operand_ty.toIntern(), 30560 .flags = .{ 30561 .is_const = true, 30562 .address_space = address_space, 30563 }, 30564 }); 30565 const mut_ptr_type = try mod.ptrType(.{ 30566 .child = operand_ty.toIntern(), 30567 .flags = .{ .address_space = address_space }, 30568 }); 30569 const alloc = try block.addTy(.alloc, mut_ptr_type); 30570 try sema.storePtr(block, src, alloc, operand); 30571 30572 // TODO: Replace with sema.coerce when that supports adding pointer constness. 30573 return sema.bitCast(block, ptr_type, alloc, src, null); 30574 } 30575 30576 fn analyzeLoad( 30577 sema: *Sema, 30578 block: *Block, 30579 src: LazySrcLoc, 30580 ptr: Air.Inst.Ref, 30581 ptr_src: LazySrcLoc, 30582 ) CompileError!Air.Inst.Ref { 30583 const mod = sema.mod; 30584 const ptr_ty = sema.typeOf(ptr); 30585 const elem_ty = switch (ptr_ty.zigTypeTag(mod)) { 30586 .Pointer => ptr_ty.childType(mod), 30587 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}), 30588 }; 30589 30590 if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| { 30591 return sema.addConstant(opv); 30592 } 30593 30594 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 30595 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| { 30596 return sema.addConstant(elem_val); 30597 } 30598 } 30599 30600 if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) { 30601 const ptr_inst = Air.refToIndex(ptr).?; 30602 const air_tags = sema.air_instructions.items(.tag); 30603 if (air_tags[ptr_inst] == .ptr_elem_ptr) { 30604 const ty_pl = sema.air_instructions.items(.data)[ptr_inst].ty_pl; 30605 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 30606 return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs); 30607 } 30608 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{ 30609 ptr_ty.fmt(sema.mod), 30610 }); 30611 } 30612 30613 return block.addTyOp(.load, elem_ty, ptr); 30614 } 30615 30616 fn analyzeSlicePtr( 30617 sema: *Sema, 30618 block: *Block, 30619 slice_src: LazySrcLoc, 30620 slice: Air.Inst.Ref, 30621 slice_ty: Type, 30622 ) CompileError!Air.Inst.Ref { 30623 const mod = sema.mod; 30624 const result_ty = slice_ty.slicePtrFieldType(mod); 30625 if (try sema.resolveMaybeUndefVal(slice)) |val| { 30626 if (val.isUndef(mod)) return sema.addConstUndef(result_ty); 30627 return sema.addConstant(val.slicePtr(mod)); 30628 } 30629 try sema.requireRuntimeBlock(block, slice_src, null); 30630 return block.addTyOp(.slice_ptr, result_ty, slice); 30631 } 30632 30633 fn analyzeSliceLen( 30634 sema: *Sema, 30635 block: *Block, 30636 src: LazySrcLoc, 30637 slice_inst: Air.Inst.Ref, 30638 ) CompileError!Air.Inst.Ref { 30639 const mod = sema.mod; 30640 if (try sema.resolveMaybeUndefVal(slice_inst)) |slice_val| { 30641 if (slice_val.isUndef(mod)) { 30642 return sema.addConstUndef(Type.usize); 30643 } 30644 return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)); 30645 } 30646 try sema.requireRuntimeBlock(block, src, null); 30647 return block.addTyOp(.slice_len, Type.usize, slice_inst); 30648 } 30649 30650 fn analyzeIsNull( 30651 sema: *Sema, 30652 block: *Block, 30653 src: LazySrcLoc, 30654 operand: Air.Inst.Ref, 30655 invert_logic: bool, 30656 ) CompileError!Air.Inst.Ref { 30657 const mod = sema.mod; 30658 const result_ty = Type.bool; 30659 if (try sema.resolveMaybeUndefVal(operand)) |opt_val| { 30660 if (opt_val.isUndef(mod)) { 30661 return sema.addConstUndef(result_ty); 30662 } 30663 const is_null = opt_val.isNull(mod); 30664 const bool_value = if (invert_logic) !is_null else is_null; 30665 if (bool_value) { 30666 return Air.Inst.Ref.bool_true; 30667 } else { 30668 return Air.Inst.Ref.bool_false; 30669 } 30670 } 30671 30672 const inverted_non_null_res = if (invert_logic) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 30673 const operand_ty = sema.typeOf(operand); 30674 if (operand_ty.zigTypeTag(mod) == .Optional and operand_ty.optionalChild(mod).zigTypeTag(mod) == .NoReturn) { 30675 return inverted_non_null_res; 30676 } 30677 if (operand_ty.zigTypeTag(mod) != .Optional and !operand_ty.isPtrLikeOptional(mod)) { 30678 return inverted_non_null_res; 30679 } 30680 try sema.requireRuntimeBlock(block, src, null); 30681 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; 30682 return block.addUnOp(air_tag, operand); 30683 } 30684 30685 fn analyzePtrIsNonErrComptimeOnly( 30686 sema: *Sema, 30687 block: *Block, 30688 src: LazySrcLoc, 30689 operand: Air.Inst.Ref, 30690 ) CompileError!Air.Inst.Ref { 30691 const mod = sema.mod; 30692 const ptr_ty = sema.typeOf(operand); 30693 assert(ptr_ty.zigTypeTag(mod) == .Pointer); 30694 const child_ty = ptr_ty.childType(mod); 30695 30696 const child_tag = child_ty.zigTypeTag(mod); 30697 if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true; 30698 if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false; 30699 assert(child_tag == .ErrorUnion); 30700 30701 _ = block; 30702 _ = src; 30703 30704 return Air.Inst.Ref.none; 30705 } 30706 30707 fn analyzeIsNonErrComptimeOnly( 30708 sema: *Sema, 30709 block: *Block, 30710 src: LazySrcLoc, 30711 operand: Air.Inst.Ref, 30712 ) CompileError!Air.Inst.Ref { 30713 const mod = sema.mod; 30714 const operand_ty = sema.typeOf(operand); 30715 const ot = operand_ty.zigTypeTag(mod); 30716 if (ot != .ErrorSet and ot != .ErrorUnion) return Air.Inst.Ref.bool_true; 30717 if (ot == .ErrorSet) return Air.Inst.Ref.bool_false; 30718 assert(ot == .ErrorUnion); 30719 30720 const payload_ty = operand_ty.errorUnionPayload(mod); 30721 if (payload_ty.zigTypeTag(mod) == .NoReturn) { 30722 return Air.Inst.Ref.bool_false; 30723 } 30724 30725 if (Air.refToIndex(operand)) |operand_inst| { 30726 switch (sema.air_instructions.items(.tag)[operand_inst]) { 30727 .wrap_errunion_payload => return Air.Inst.Ref.bool_true, 30728 .wrap_errunion_err => return Air.Inst.Ref.bool_false, 30729 else => {}, 30730 } 30731 } else if (operand == .undef) { 30732 return sema.addConstUndef(Type.bool); 30733 } else if (@intFromEnum(operand) < InternPool.static_len) { 30734 // None of the ref tags can be errors. 30735 return Air.Inst.Ref.bool_true; 30736 } 30737 30738 const maybe_operand_val = try sema.resolveMaybeUndefVal(operand); 30739 30740 // exception if the error union error set is known to be empty, 30741 // we allow the comparison but always make it comptime-known. 30742 const set_ty = operand_ty.errorUnionSet(mod); 30743 switch (set_ty.toIntern()) { 30744 .anyerror_type => {}, 30745 else => switch (mod.intern_pool.indexToKey(set_ty.toIntern())) { 30746 .error_set_type => |error_set_type| { 30747 if (error_set_type.names.len == 0) return Air.Inst.Ref.bool_true; 30748 }, 30749 .inferred_error_set_type => |ies_index| blk: { 30750 // If the error set is empty, we must return a comptime true or false. 30751 // However we want to avoid unnecessarily resolving an inferred error set 30752 // in case it is already non-empty. 30753 const ies = mod.inferredErrorSetPtr(ies_index); 30754 if (ies.is_anyerror) break :blk; 30755 if (ies.errors.count() != 0) break :blk; 30756 if (maybe_operand_val == null) { 30757 // Try to avoid resolving inferred error set if possible. 30758 if (ies.errors.count() != 0) break :blk; 30759 if (ies.is_anyerror) break :blk; 30760 for (ies.inferred_error_sets.keys()) |other_ies_index| { 30761 if (ies_index == other_ies_index) continue; 30762 try sema.resolveInferredErrorSet(block, src, other_ies_index); 30763 const other_ies = mod.inferredErrorSetPtr(other_ies_index); 30764 if (other_ies.is_anyerror) { 30765 ies.is_anyerror = true; 30766 ies.is_resolved = true; 30767 break :blk; 30768 } 30769 30770 if (other_ies.errors.count() != 0) break :blk; 30771 } 30772 if (ies.func == sema.owner_func_index.unwrap()) { 30773 // We're checking the inferred errorset of the current function and none of 30774 // its child inferred error sets contained any errors meaning that any value 30775 // so far with this type can't contain errors either. 30776 return Air.Inst.Ref.bool_true; 30777 } 30778 try sema.resolveInferredErrorSet(block, src, ies_index); 30779 if (ies.is_anyerror) break :blk; 30780 if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true; 30781 } 30782 }, 30783 else => unreachable, 30784 }, 30785 } 30786 30787 if (maybe_operand_val) |err_union| { 30788 if (err_union.isUndef(mod)) { 30789 return sema.addConstUndef(Type.bool); 30790 } 30791 if (err_union.getErrorName(mod) == .none) { 30792 return Air.Inst.Ref.bool_true; 30793 } else { 30794 return Air.Inst.Ref.bool_false; 30795 } 30796 } 30797 return Air.Inst.Ref.none; 30798 } 30799 30800 fn analyzeIsNonErr( 30801 sema: *Sema, 30802 block: *Block, 30803 src: LazySrcLoc, 30804 operand: Air.Inst.Ref, 30805 ) CompileError!Air.Inst.Ref { 30806 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); 30807 if (result == .none) { 30808 try sema.requireRuntimeBlock(block, src, null); 30809 return block.addUnOp(.is_non_err, operand); 30810 } else { 30811 return result; 30812 } 30813 } 30814 30815 fn analyzePtrIsNonErr( 30816 sema: *Sema, 30817 block: *Block, 30818 src: LazySrcLoc, 30819 operand: Air.Inst.Ref, 30820 ) CompileError!Air.Inst.Ref { 30821 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand); 30822 if (result == .none) { 30823 try sema.requireRuntimeBlock(block, src, null); 30824 return block.addUnOp(.is_non_err_ptr, operand); 30825 } else { 30826 return result; 30827 } 30828 } 30829 30830 fn analyzeSlice( 30831 sema: *Sema, 30832 block: *Block, 30833 src: LazySrcLoc, 30834 ptr_ptr: Air.Inst.Ref, 30835 uncasted_start: Air.Inst.Ref, 30836 uncasted_end_opt: Air.Inst.Ref, 30837 sentinel_opt: Air.Inst.Ref, 30838 sentinel_src: LazySrcLoc, 30839 ptr_src: LazySrcLoc, 30840 start_src: LazySrcLoc, 30841 end_src: LazySrcLoc, 30842 by_length: bool, 30843 ) CompileError!Air.Inst.Ref { 30844 const mod = sema.mod; 30845 // Slice expressions can operate on a variable whose type is an array. This requires 30846 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. 30847 const ptr_ptr_ty = sema.typeOf(ptr_ptr); 30848 const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(mod)) { 30849 .Pointer => ptr_ptr_ty.childType(mod), 30850 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(mod)}), 30851 }; 30852 30853 var array_ty = ptr_ptr_child_ty; 30854 var slice_ty = ptr_ptr_ty; 30855 var ptr_or_slice = ptr_ptr; 30856 var elem_ty: Type = undefined; 30857 var ptr_sentinel: ?Value = null; 30858 switch (ptr_ptr_child_ty.zigTypeTag(mod)) { 30859 .Array => { 30860 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 30861 elem_ty = ptr_ptr_child_ty.childType(mod); 30862 }, 30863 .Pointer => switch (ptr_ptr_child_ty.ptrSize(mod)) { 30864 .One => { 30865 const double_child_ty = ptr_ptr_child_ty.childType(mod); 30866 if (double_child_ty.zigTypeTag(mod) == .Array) { 30867 ptr_sentinel = double_child_ty.sentinel(mod); 30868 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 30869 slice_ty = ptr_ptr_child_ty; 30870 array_ty = double_child_ty; 30871 elem_ty = double_child_ty.childType(mod); 30872 } else { 30873 return sema.fail(block, src, "slice of single-item pointer", .{}); 30874 } 30875 }, 30876 .Many, .C => { 30877 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 30878 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 30879 slice_ty = ptr_ptr_child_ty; 30880 array_ty = ptr_ptr_child_ty; 30881 elem_ty = ptr_ptr_child_ty.childType(mod); 30882 30883 if (ptr_ptr_child_ty.ptrSize(mod) == .C) { 30884 if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| { 30885 if (ptr_val.isNull(mod)) { 30886 return sema.fail(block, src, "slice of null pointer", .{}); 30887 } 30888 } 30889 } 30890 }, 30891 .Slice => { 30892 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 30893 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 30894 slice_ty = ptr_ptr_child_ty; 30895 array_ty = ptr_ptr_child_ty; 30896 elem_ty = ptr_ptr_child_ty.childType(mod); 30897 }, 30898 }, 30899 else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}), 30900 } 30901 30902 const ptr = if (slice_ty.isSlice(mod)) 30903 try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) 30904 else if (array_ty.zigTypeTag(mod) == .Array) ptr: { 30905 var manyptr_ty_key = mod.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type; 30906 assert(manyptr_ty_key.child == array_ty.toIntern()); 30907 assert(manyptr_ty_key.flags.size == .One); 30908 manyptr_ty_key.child = elem_ty.toIntern(); 30909 manyptr_ty_key.flags.size = .Many; 30910 break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src); 30911 } else ptr_or_slice; 30912 30913 const start = try sema.coerce(block, Type.usize, uncasted_start, start_src); 30914 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); 30915 const new_ptr_ty = sema.typeOf(new_ptr); 30916 30917 // true if and only if the end index of the slice, implicitly or explicitly, equals 30918 // the length of the underlying object being sliced. we might learn the length of the 30919 // underlying object because it is an array (which has the length in the type), or 30920 // we might learn of the length because it is a comptime-known slice value. 30921 var end_is_len = uncasted_end_opt == .none; 30922 const end = e: { 30923 if (array_ty.zigTypeTag(mod) == .Array) { 30924 const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod)); 30925 30926 if (!end_is_len) { 30927 const end = if (by_length) end: { 30928 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 30929 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 30930 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); 30931 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 30932 if (try sema.resolveMaybeUndefVal(end)) |end_val| { 30933 const len_s_val = try mod.intValue( 30934 Type.usize, 30935 array_ty.arrayLenIncludingSentinel(mod), 30936 ); 30937 if (!(try sema.compareAll(end_val, .lte, len_s_val, Type.usize))) { 30938 const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null) 30939 " +1 (sentinel)" 30940 else 30941 ""; 30942 30943 return sema.fail( 30944 block, 30945 end_src, 30946 "end index {} out of bounds for array of length {}{s}", 30947 .{ 30948 end_val.fmtValue(Type.usize, mod), 30949 len_val.fmtValue(Type.usize, mod), 30950 sentinel_label, 30951 }, 30952 ); 30953 } 30954 30955 // end_is_len is only true if we are NOT using the sentinel 30956 // length. For sentinel-length, we don't want the type to 30957 // contain the sentinel. 30958 if (end_val.eql(len_val, Type.usize, mod)) { 30959 end_is_len = true; 30960 } 30961 } 30962 break :e end; 30963 } 30964 30965 break :e try sema.addConstant(len_val); 30966 } else if (slice_ty.isSlice(mod)) { 30967 if (!end_is_len) { 30968 const end = if (by_length) end: { 30969 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 30970 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 30971 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); 30972 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 30973 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 30974 if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| { 30975 if (slice_val.isUndef(mod)) { 30976 return sema.fail(block, src, "slice of undefined", .{}); 30977 } 30978 const has_sentinel = slice_ty.sentinel(mod) != null; 30979 const slice_len = slice_val.sliceLen(mod); 30980 const len_plus_sent = slice_len + @intFromBool(has_sentinel); 30981 const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent); 30982 if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) { 30983 const sentinel_label: []const u8 = if (has_sentinel) 30984 " +1 (sentinel)" 30985 else 30986 ""; 30987 30988 return sema.fail( 30989 block, 30990 end_src, 30991 "end index {} out of bounds for slice of length {d}{s}", 30992 .{ 30993 end_val.fmtValue(Type.usize, mod), 30994 slice_val.sliceLen(mod), 30995 sentinel_label, 30996 }, 30997 ); 30998 } 30999 31000 // If the slice has a sentinel, we consider end_is_len 31001 // is only true if it equals the length WITHOUT the 31002 // sentinel, so we don't add a sentinel type. 31003 const slice_len_val = try mod.intValue(Type.usize, slice_len); 31004 if (end_val.eql(slice_len_val, Type.usize, mod)) { 31005 end_is_len = true; 31006 } 31007 } 31008 } 31009 break :e end; 31010 } 31011 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); 31012 } 31013 if (!end_is_len) { 31014 if (by_length) { 31015 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 31016 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 31017 break :e try sema.coerce(block, Type.usize, uncasted_end, end_src); 31018 } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 31019 } 31020 return sema.fail(block, src, "slice of pointer must include end value", .{}); 31021 }; 31022 31023 const sentinel = s: { 31024 if (sentinel_opt != .none) { 31025 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); 31026 break :s try sema.resolveConstValue(block, sentinel_src, casted, "slice sentinel must be comptime-known"); 31027 } 31028 // If we are slicing to the end of something that is sentinel-terminated 31029 // then the resulting slice type is also sentinel-terminated. 31030 if (end_is_len) { 31031 if (ptr_sentinel) |sent| { 31032 break :s sent; 31033 } 31034 } 31035 break :s null; 31036 }; 31037 const slice_sentinel = if (sentinel_opt != .none) sentinel else null; 31038 31039 var checked_start_lte_end = by_length; 31040 var runtime_src: ?LazySrcLoc = null; 31041 31042 // requirement: start <= end 31043 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 31044 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { 31045 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) { 31046 return sema.fail( 31047 block, 31048 start_src, 31049 "start index {} is larger than end index {}", 31050 .{ 31051 start_val.fmtValue(Type.usize, mod), 31052 end_val.fmtValue(Type.usize, mod), 31053 }, 31054 ); 31055 } 31056 checked_start_lte_end = true; 31057 if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: { 31058 const expected_sentinel = sentinel orelse break :sentinel_check; 31059 const start_int = start_val.getUnsignedInt(mod).?; 31060 const end_int = end_val.getUnsignedInt(mod).?; 31061 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); 31062 31063 const many_ptr_ty = try mod.manyConstPtrType(elem_ty); 31064 const many_ptr_val = try mod.getCoerced(ptr_val, many_ptr_ty); 31065 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty); 31066 const elem_ptr = try many_ptr_val.elemPtr(elem_ptr_ty, sentinel_index, mod); 31067 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty); 31068 const actual_sentinel = switch (res) { 31069 .runtime_load => break :sentinel_check, 31070 .val => |v| v, 31071 .needed_well_defined => |ty| return sema.fail( 31072 block, 31073 src, 31074 "comptime dereference requires '{}' to have a well-defined layout, but it does not.", 31075 .{ty.fmt(mod)}, 31076 ), 31077 .out_of_bounds => |ty| return sema.fail( 31078 block, 31079 end_src, 31080 "slice end index {d} exceeds bounds of containing decl of type '{}'", 31081 .{ end_int, ty.fmt(mod) }, 31082 ), 31083 }; 31084 31085 if (!actual_sentinel.eql(expected_sentinel, elem_ty, mod)) { 31086 const msg = msg: { 31087 const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{}); 31088 errdefer msg.destroy(sema.gpa); 31089 try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{ 31090 expected_sentinel.fmtValue(elem_ty, mod), 31091 actual_sentinel.fmtValue(elem_ty, mod), 31092 }); 31093 31094 break :msg msg; 31095 }; 31096 return sema.failWithOwnedErrorMsg(msg); 31097 } 31098 } else { 31099 runtime_src = ptr_src; 31100 } 31101 } else { 31102 runtime_src = start_src; 31103 } 31104 } else { 31105 runtime_src = end_src; 31106 } 31107 31108 if (!checked_start_lte_end and block.wantSafety() and !block.is_comptime) { 31109 // requirement: start <= end 31110 assert(!block.is_comptime); 31111 try sema.requireRuntimeBlock(block, src, runtime_src.?); 31112 const ok = try block.addBinOp(.cmp_lte, start, end); 31113 if (!sema.mod.comp.formatted_panics) { 31114 try sema.addSafetyCheck(block, ok, .start_index_greater_than_end); 31115 } else { 31116 try sema.safetyCheckFormatted(block, ok, "panicStartGreaterThanEnd", &.{ start, end }); 31117 } 31118 } 31119 const new_len = if (by_length) 31120 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src) 31121 else 31122 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); 31123 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); 31124 31125 const new_ptr_ty_info = new_ptr_ty.ptrInfo(mod); 31126 const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(mod) != .C; 31127 31128 if (opt_new_len_val) |new_len_val| { 31129 const new_len_int = new_len_val.toUnsignedInt(mod); 31130 31131 const return_ty = try mod.ptrType(.{ 31132 .child = (try mod.arrayType(.{ 31133 .len = new_len_int, 31134 .sentinel = if (sentinel) |s| s.toIntern() else .none, 31135 .child = elem_ty.toIntern(), 31136 })).toIntern(), 31137 .flags = .{ 31138 .alignment = new_ptr_ty_info.flags.alignment, 31139 .is_const = new_ptr_ty_info.flags.is_const, 31140 .is_allowzero = new_allowzero, 31141 .is_volatile = new_ptr_ty_info.flags.is_volatile, 31142 .address_space = new_ptr_ty_info.flags.address_space, 31143 }, 31144 }); 31145 31146 const opt_new_ptr_val = try sema.resolveMaybeUndefVal(new_ptr); 31147 const new_ptr_val = opt_new_ptr_val orelse { 31148 const result = try block.addBitCast(return_ty, new_ptr); 31149 if (block.wantSafety()) { 31150 // requirement: slicing C ptr is non-null 31151 if (ptr_ptr_child_ty.isCPtr(mod)) { 31152 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); 31153 try sema.addSafetyCheck(block, is_non_null, .unwrap_null); 31154 } 31155 31156 if (slice_ty.isSlice(mod)) { 31157 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); 31158 const actual_len = if (slice_ty.sentinel(mod) == null) 31159 slice_len_inst 31160 else 31161 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); 31162 31163 const actual_end = if (slice_sentinel != null) 31164 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 31165 else 31166 end; 31167 31168 try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte); 31169 } 31170 31171 // requirement: result[new_len] == slice_sentinel 31172 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len); 31173 } 31174 return result; 31175 }; 31176 31177 if (!new_ptr_val.isUndef(mod)) { 31178 return sema.addConstant(try mod.getCoerced( 31179 (try new_ptr_val.intern(new_ptr_ty, mod)).toValue(), 31180 return_ty, 31181 )); 31182 } 31183 31184 // Special case: @as([]i32, undefined)[x..x] 31185 if (new_len_int == 0) { 31186 return sema.addConstUndef(return_ty); 31187 } 31188 31189 return sema.fail(block, src, "non-zero length slice of undefined pointer", .{}); 31190 } 31191 31192 const return_ty = try mod.ptrType(.{ 31193 .child = elem_ty.toIntern(), 31194 .sentinel = if (sentinel) |s| s.toIntern() else .none, 31195 .flags = .{ 31196 .size = .Slice, 31197 .alignment = new_ptr_ty_info.flags.alignment, 31198 .is_const = new_ptr_ty_info.flags.is_const, 31199 .is_volatile = new_ptr_ty_info.flags.is_volatile, 31200 .is_allowzero = new_allowzero, 31201 .address_space = new_ptr_ty_info.flags.address_space, 31202 }, 31203 }); 31204 31205 try sema.requireRuntimeBlock(block, src, runtime_src.?); 31206 if (block.wantSafety()) { 31207 // requirement: slicing C ptr is non-null 31208 if (ptr_ptr_child_ty.isCPtr(mod)) { 31209 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); 31210 try sema.addSafetyCheck(block, is_non_null, .unwrap_null); 31211 } 31212 31213 // requirement: end <= len 31214 const opt_len_inst = if (array_ty.zigTypeTag(mod) == .Array) 31215 try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel(mod)) 31216 else if (slice_ty.isSlice(mod)) blk: { 31217 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { 31218 // we don't need to add one for sentinels because the 31219 // underlying value data includes the sentinel 31220 break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)); 31221 } 31222 31223 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); 31224 if (slice_ty.sentinel(mod) == null) break :blk slice_len_inst; 31225 31226 // we have to add one because slice lengths don't include the sentinel 31227 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); 31228 } else null; 31229 if (opt_len_inst) |len_inst| { 31230 const actual_end = if (slice_sentinel != null) 31231 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 31232 else 31233 end; 31234 try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte); 31235 } 31236 31237 // requirement: start <= end 31238 try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte); 31239 } 31240 const result = try block.addInst(.{ 31241 .tag = .slice, 31242 .data = .{ .ty_pl = .{ 31243 .ty = try sema.addType(return_ty), 31244 .payload = try sema.addExtra(Air.Bin{ 31245 .lhs = new_ptr, 31246 .rhs = new_len, 31247 }), 31248 } }, 31249 }); 31250 if (block.wantSafety()) { 31251 // requirement: result[new_len] == slice_sentinel 31252 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len); 31253 } 31254 return result; 31255 } 31256 31257 /// Asserts that lhs and rhs types are both numeric. 31258 fn cmpNumeric( 31259 sema: *Sema, 31260 block: *Block, 31261 src: LazySrcLoc, 31262 uncasted_lhs: Air.Inst.Ref, 31263 uncasted_rhs: Air.Inst.Ref, 31264 op: std.math.CompareOperator, 31265 lhs_src: LazySrcLoc, 31266 rhs_src: LazySrcLoc, 31267 ) CompileError!Air.Inst.Ref { 31268 const mod = sema.mod; 31269 const lhs_ty = sema.typeOf(uncasted_lhs); 31270 const rhs_ty = sema.typeOf(uncasted_rhs); 31271 31272 assert(lhs_ty.isNumeric(mod)); 31273 assert(rhs_ty.isNumeric(mod)); 31274 31275 const lhs_ty_tag = lhs_ty.zigTypeTag(mod); 31276 const rhs_ty_tag = rhs_ty.zigTypeTag(mod); 31277 const target = mod.getTarget(); 31278 31279 // One exception to heterogeneous comparison: comptime_float needs to 31280 // coerce to fixed-width float. 31281 31282 const lhs = if (lhs_ty_tag == .ComptimeFloat and rhs_ty_tag == .Float) 31283 try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src) 31284 else 31285 uncasted_lhs; 31286 31287 const rhs = if (lhs_ty_tag == .Float and rhs_ty_tag == .ComptimeFloat) 31288 try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src) 31289 else 31290 uncasted_rhs; 31291 31292 const runtime_src: LazySrcLoc = src: { 31293 if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| { 31294 if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { 31295 // Compare ints: const vs. undefined (or vice versa) 31296 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)) { 31297 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| { 31298 return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 31299 } 31300 } 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)) { 31301 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| { 31302 return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 31303 } 31304 } 31305 31306 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 31307 return sema.addConstUndef(Type.bool); 31308 } 31309 if (lhs_val.isNan(mod) or rhs_val.isNan(mod)) { 31310 if (op == std.math.CompareOperator.neq) { 31311 return Air.Inst.Ref.bool_true; 31312 } else { 31313 return Air.Inst.Ref.bool_false; 31314 } 31315 } 31316 if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, mod, sema)) { 31317 return Air.Inst.Ref.bool_true; 31318 } else { 31319 return Air.Inst.Ref.bool_false; 31320 } 31321 } else { 31322 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod)) { 31323 // Compare ints: const vs. var 31324 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| { 31325 return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 31326 } 31327 } 31328 break :src rhs_src; 31329 } 31330 } else { 31331 if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| { 31332 if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod)) { 31333 // Compare ints: var vs. const 31334 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| { 31335 return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 31336 } 31337 } 31338 } 31339 break :src lhs_src; 31340 } 31341 }; 31342 31343 // TODO handle comparisons against lazy zero values 31344 // Some values can be compared against zero without being runtime-known or without forcing 31345 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to 31346 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout 31347 // of this function if we don't need to. 31348 try sema.requireRuntimeBlock(block, src, runtime_src); 31349 31350 // For floats, emit a float comparison instruction. 31351 const lhs_is_float = switch (lhs_ty_tag) { 31352 .Float, .ComptimeFloat => true, 31353 else => false, 31354 }; 31355 const rhs_is_float = switch (rhs_ty_tag) { 31356 .Float, .ComptimeFloat => true, 31357 else => false, 31358 }; 31359 31360 if (lhs_is_float and rhs_is_float) { 31361 // Smaller fixed-width floats coerce to larger fixed-width floats. 31362 // comptime_float coerces to fixed-width float. 31363 const dest_ty = x: { 31364 if (lhs_ty_tag == .ComptimeFloat) { 31365 break :x rhs_ty; 31366 } else if (rhs_ty_tag == .ComptimeFloat) { 31367 break :x lhs_ty; 31368 } 31369 if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) { 31370 break :x lhs_ty; 31371 } else { 31372 break :x rhs_ty; 31373 } 31374 }; 31375 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 31376 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 31377 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs); 31378 } 31379 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. 31380 // For mixed signed and unsigned integers, implicit cast both operands to a signed 31381 // integer with + 1 bit. 31382 // For mixed floats and integers, extract the integer part from the float, cast that to 31383 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, 31384 // add/subtract 1. 31385 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| 31386 !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema)) 31387 else 31388 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(mod)); 31389 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| 31390 !(try rhs_val.compareAllWithZeroAdvanced(.gte, sema)) 31391 else 31392 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(mod)); 31393 const dest_int_is_signed = lhs_is_signed or rhs_is_signed; 31394 31395 var dest_float_type: ?Type = null; 31396 31397 var lhs_bits: usize = undefined; 31398 if (try sema.resolveMaybeUndefLazyVal(lhs)) |lhs_val| { 31399 if (lhs_val.isUndef(mod)) 31400 return sema.addConstUndef(Type.bool); 31401 if (lhs_val.isNan(mod)) switch (op) { 31402 .neq => return Air.Inst.Ref.bool_true, 31403 else => return Air.Inst.Ref.bool_false, 31404 }; 31405 if (lhs_val.isInf(mod)) switch (op) { 31406 .neq => return Air.Inst.Ref.bool_true, 31407 .eq => return Air.Inst.Ref.bool_false, 31408 .gt, .gte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true, 31409 .lt, .lte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false, 31410 }; 31411 if (!rhs_is_signed) { 31412 switch (lhs_val.orderAgainstZero(mod)) { 31413 .gt => {}, 31414 .eq => switch (op) { // LHS = 0, RHS is unsigned 31415 .lte => return Air.Inst.Ref.bool_true, 31416 .gt => return Air.Inst.Ref.bool_false, 31417 else => {}, 31418 }, 31419 .lt => switch (op) { // LHS < 0, RHS is unsigned 31420 .neq, .lt, .lte => return Air.Inst.Ref.bool_true, 31421 .eq, .gt, .gte => return Air.Inst.Ref.bool_false, 31422 }, 31423 } 31424 } 31425 if (lhs_is_float) { 31426 if (lhs_val.floatHasFraction(mod)) { 31427 switch (op) { 31428 .eq => return Air.Inst.Ref.bool_false, 31429 .neq => return Air.Inst.Ref.bool_true, 31430 else => {}, 31431 } 31432 } 31433 31434 var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, mod)); 31435 defer bigint.deinit(); 31436 if (lhs_val.floatHasFraction(mod)) { 31437 if (lhs_is_signed) { 31438 try bigint.addScalar(&bigint, -1); 31439 } else { 31440 try bigint.addScalar(&bigint, 1); 31441 } 31442 } 31443 lhs_bits = bigint.toConst().bitCountTwosComp(); 31444 } else { 31445 lhs_bits = lhs_val.intBitCountTwosComp(mod); 31446 } 31447 lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed); 31448 } else if (lhs_is_float) { 31449 dest_float_type = lhs_ty; 31450 } else { 31451 const int_info = lhs_ty.intInfo(mod); 31452 lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 31453 } 31454 31455 var rhs_bits: usize = undefined; 31456 if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| { 31457 if (rhs_val.isUndef(mod)) 31458 return sema.addConstUndef(Type.bool); 31459 if (rhs_val.isNan(mod)) switch (op) { 31460 .neq => return Air.Inst.Ref.bool_true, 31461 else => return Air.Inst.Ref.bool_false, 31462 }; 31463 if (rhs_val.isInf(mod)) switch (op) { 31464 .neq => return Air.Inst.Ref.bool_true, 31465 .eq => return Air.Inst.Ref.bool_false, 31466 .gt, .gte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false, 31467 .lt, .lte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true, 31468 }; 31469 if (!lhs_is_signed) { 31470 switch (rhs_val.orderAgainstZero(mod)) { 31471 .gt => {}, 31472 .eq => switch (op) { // RHS = 0, LHS is unsigned 31473 .gte => return Air.Inst.Ref.bool_true, 31474 .lt => return Air.Inst.Ref.bool_false, 31475 else => {}, 31476 }, 31477 .lt => switch (op) { // RHS < 0, LHS is unsigned 31478 .neq, .gt, .gte => return Air.Inst.Ref.bool_true, 31479 .eq, .lt, .lte => return Air.Inst.Ref.bool_false, 31480 }, 31481 } 31482 } 31483 if (rhs_is_float) { 31484 if (rhs_val.floatHasFraction(mod)) { 31485 switch (op) { 31486 .eq => return Air.Inst.Ref.bool_false, 31487 .neq => return Air.Inst.Ref.bool_true, 31488 else => {}, 31489 } 31490 } 31491 31492 var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, mod)); 31493 defer bigint.deinit(); 31494 if (rhs_val.floatHasFraction(mod)) { 31495 if (rhs_is_signed) { 31496 try bigint.addScalar(&bigint, -1); 31497 } else { 31498 try bigint.addScalar(&bigint, 1); 31499 } 31500 } 31501 rhs_bits = bigint.toConst().bitCountTwosComp(); 31502 } else { 31503 rhs_bits = rhs_val.intBitCountTwosComp(mod); 31504 } 31505 rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed); 31506 } else if (rhs_is_float) { 31507 dest_float_type = rhs_ty; 31508 } else { 31509 const int_info = rhs_ty.intInfo(mod); 31510 rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 31511 } 31512 31513 const dest_ty = if (dest_float_type) |ft| ft else blk: { 31514 const max_bits = @max(lhs_bits, rhs_bits); 31515 const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}); 31516 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned; 31517 break :blk try mod.intType(signedness, casted_bits); 31518 }; 31519 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 31520 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 31521 31522 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs); 31523 } 31524 31525 /// Asserts that LHS value is an int or comptime int and not undefined, and 31526 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to 31527 /// determine whether `op` has a guaranteed result. 31528 /// If it cannot be determined, returns null. 31529 /// Otherwise returns a bool for the guaranteed comparison operation. 31530 fn compareIntsOnlyPossibleResult( 31531 sema: *Sema, 31532 lhs_val: Value, 31533 op: std.math.CompareOperator, 31534 rhs_ty: Type, 31535 ) Allocator.Error!?bool { 31536 const mod = sema.mod; 31537 const rhs_info = rhs_ty.intInfo(mod); 31538 const vs_zero = lhs_val.orderAgainstZeroAdvanced(mod, sema) catch unreachable; 31539 const is_zero = vs_zero == .eq; 31540 const is_negative = vs_zero == .lt; 31541 const is_positive = vs_zero == .gt; 31542 31543 // Anything vs. zero-sized type has guaranteed outcome. 31544 if (rhs_info.bits == 0) return switch (op) { 31545 .eq, .lte, .gte => is_zero, 31546 .neq, .lt, .gt => !is_zero, 31547 }; 31548 31549 // Special case for i1, which can only be 0 or -1. 31550 // Zero and positive ints have guaranteed outcome. 31551 if (rhs_info.bits == 1 and rhs_info.signedness == .signed) { 31552 if (is_positive) return switch (op) { 31553 .gt, .gte, .neq => true, 31554 .lt, .lte, .eq => false, 31555 }; 31556 if (is_zero) return switch (op) { 31557 .gte => true, 31558 .lt => false, 31559 .gt, .lte, .eq, .neq => null, 31560 }; 31561 } 31562 31563 // Negative vs. unsigned has guaranteed outcome. 31564 if (rhs_info.signedness == .unsigned and is_negative) return switch (op) { 31565 .eq, .gt, .gte => false, 31566 .neq, .lt, .lte => true, 31567 }; 31568 31569 const sign_adj = @intFromBool(!is_negative and rhs_info.signedness == .signed); 31570 const req_bits = lhs_val.intBitCountTwosComp(mod) + sign_adj; 31571 31572 // No sized type can have more than 65535 bits. 31573 // The RHS type operand is either a runtime value or sized (but undefined) constant. 31574 if (req_bits > 65535) return switch (op) { 31575 .lt, .lte => is_negative, 31576 .gt, .gte => is_positive, 31577 .eq => false, 31578 .neq => true, 31579 }; 31580 const fits = req_bits <= rhs_info.bits; 31581 31582 // Oversized int has guaranteed outcome. 31583 switch (op) { 31584 .eq => return if (!fits) false else null, 31585 .neq => return if (!fits) true else null, 31586 .lt, .lte => if (!fits) return is_negative, 31587 .gt, .gte => if (!fits) return !is_negative, 31588 } 31589 31590 // For any other comparison, we need to know if the LHS value is 31591 // equal to the maximum or minimum possible value of the RHS type. 31592 const edge: struct { min: bool, max: bool } = edge: { 31593 if (is_zero and rhs_info.signedness == .unsigned) break :edge .{ 31594 .min = true, 31595 .max = false, 31596 }; 31597 31598 if (req_bits != rhs_info.bits) break :edge .{ 31599 .min = false, 31600 .max = false, 31601 }; 31602 31603 const ty = try mod.intType( 31604 if (is_negative) .signed else .unsigned, 31605 @as(u16, @intCast(req_bits)), 31606 ); 31607 const pop_count = lhs_val.popCount(ty, mod); 31608 31609 if (is_negative) { 31610 break :edge .{ 31611 .min = pop_count == 1, 31612 .max = false, 31613 }; 31614 } else { 31615 break :edge .{ 31616 .min = false, 31617 .max = pop_count == req_bits - sign_adj, 31618 }; 31619 } 31620 }; 31621 31622 assert(fits); 31623 return switch (op) { 31624 .lt => if (edge.max) false else null, 31625 .lte => if (edge.min) true else null, 31626 .gt => if (edge.min) false else null, 31627 .gte => if (edge.max) true else null, 31628 .eq, .neq => unreachable, 31629 }; 31630 } 31631 31632 /// Asserts that lhs and rhs types are both vectors. 31633 fn cmpVector( 31634 sema: *Sema, 31635 block: *Block, 31636 src: LazySrcLoc, 31637 lhs: Air.Inst.Ref, 31638 rhs: Air.Inst.Ref, 31639 op: std.math.CompareOperator, 31640 lhs_src: LazySrcLoc, 31641 rhs_src: LazySrcLoc, 31642 ) CompileError!Air.Inst.Ref { 31643 const mod = sema.mod; 31644 const lhs_ty = sema.typeOf(lhs); 31645 const rhs_ty = sema.typeOf(rhs); 31646 assert(lhs_ty.zigTypeTag(mod) == .Vector); 31647 assert(rhs_ty.zigTypeTag(mod) == .Vector); 31648 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 31649 31650 const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } }); 31651 const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src); 31652 const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src); 31653 31654 const result_ty = try mod.vectorType(.{ 31655 .len = lhs_ty.vectorLen(mod), 31656 .child = .bool_type, 31657 }); 31658 31659 const runtime_src: LazySrcLoc = src: { 31660 if (try sema.resolveMaybeUndefVal(casted_lhs)) |lhs_val| { 31661 if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| { 31662 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 31663 return sema.addConstUndef(result_ty); 31664 } 31665 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty); 31666 return sema.addConstant(cmp_val); 31667 } else { 31668 break :src rhs_src; 31669 } 31670 } else { 31671 break :src lhs_src; 31672 } 31673 }; 31674 31675 try sema.requireRuntimeBlock(block, src, runtime_src); 31676 return block.addCmpVector(casted_lhs, casted_rhs, op); 31677 } 31678 31679 fn wrapOptional( 31680 sema: *Sema, 31681 block: *Block, 31682 dest_ty: Type, 31683 inst: Air.Inst.Ref, 31684 inst_src: LazySrcLoc, 31685 ) !Air.Inst.Ref { 31686 if (try sema.resolveMaybeUndefVal(inst)) |val| { 31687 return sema.addConstant((try sema.mod.intern(.{ .opt = .{ 31688 .ty = dest_ty.toIntern(), 31689 .val = val.toIntern(), 31690 } })).toValue()); 31691 } 31692 31693 try sema.requireRuntimeBlock(block, inst_src, null); 31694 return block.addTyOp(.wrap_optional, dest_ty, inst); 31695 } 31696 31697 fn wrapErrorUnionPayload( 31698 sema: *Sema, 31699 block: *Block, 31700 dest_ty: Type, 31701 inst: Air.Inst.Ref, 31702 inst_src: LazySrcLoc, 31703 ) !Air.Inst.Ref { 31704 const mod = sema.mod; 31705 const dest_payload_ty = dest_ty.errorUnionPayload(mod); 31706 const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); 31707 if (try sema.resolveMaybeUndefVal(coerced)) |val| { 31708 return sema.addConstant((try mod.intern(.{ .error_union = .{ 31709 .ty = dest_ty.toIntern(), 31710 .val = .{ .payload = try val.intern(dest_payload_ty, mod) }, 31711 } })).toValue()); 31712 } 31713 try sema.requireRuntimeBlock(block, inst_src, null); 31714 try sema.queueFullTypeResolution(dest_payload_ty); 31715 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); 31716 } 31717 31718 fn wrapErrorUnionSet( 31719 sema: *Sema, 31720 block: *Block, 31721 dest_ty: Type, 31722 inst: Air.Inst.Ref, 31723 inst_src: LazySrcLoc, 31724 ) !Air.Inst.Ref { 31725 const mod = sema.mod; 31726 const ip = &mod.intern_pool; 31727 const inst_ty = sema.typeOf(inst); 31728 const dest_err_set_ty = dest_ty.errorUnionSet(mod); 31729 if (try sema.resolveMaybeUndefVal(inst)) |val| { 31730 switch (dest_err_set_ty.toIntern()) { 31731 .anyerror_type => {}, 31732 else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) { 31733 .error_set_type => |error_set_type| ok: { 31734 const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; 31735 if (error_set_type.nameIndex(ip, expected_name) != null) break :ok; 31736 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 31737 }, 31738 .inferred_error_set_type => |ies_index| ok: { 31739 const ies = mod.inferredErrorSetPtr(ies_index); 31740 const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; 31741 31742 // We carefully do this in an order that avoids unnecessarily 31743 // resolving the destination error set type. 31744 if (ies.is_anyerror) break :ok; 31745 31746 if (ies.errors.contains(expected_name)) break :ok; 31747 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) break :ok; 31748 31749 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 31750 }, 31751 else => unreachable, 31752 }, 31753 } 31754 return sema.addConstant((try mod.intern(.{ .error_union = .{ 31755 .ty = dest_ty.toIntern(), 31756 .val = .{ 31757 .err_name = mod.intern_pool.indexToKey(try val.intern(dest_err_set_ty, mod)).err.name, 31758 }, 31759 } })).toValue()); 31760 } 31761 31762 try sema.requireRuntimeBlock(block, inst_src, null); 31763 const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src); 31764 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); 31765 } 31766 31767 fn unionToTag( 31768 sema: *Sema, 31769 block: *Block, 31770 enum_ty: Type, 31771 un: Air.Inst.Ref, 31772 un_src: LazySrcLoc, 31773 ) !Air.Inst.Ref { 31774 const mod = sema.mod; 31775 if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| { 31776 return sema.addConstant(opv); 31777 } 31778 if (try sema.resolveMaybeUndefVal(un)) |un_val| { 31779 return sema.addConstant(un_val.unionTag(mod)); 31780 } 31781 try sema.requireRuntimeBlock(block, un_src, null); 31782 return block.addTyOp(.get_union_tag, enum_ty, un); 31783 } 31784 31785 const PeerResolveStrategy = enum { 31786 /// The type is not known. 31787 /// If refined no further, this is equivalent to `exact`. 31788 unknown, 31789 /// The type may be an error set or error union. 31790 /// If refined no further, it is an error set. 31791 error_set, 31792 /// The type must be some error union. 31793 error_union, 31794 /// The type may be @TypeOf(null), an optional or a C pointer. 31795 /// If refined no further, it is @TypeOf(null). 31796 nullable, 31797 /// The type must be some optional or a C pointer. 31798 /// If refined no further, it is an optional. 31799 optional, 31800 /// The type must be either an array or a vector. 31801 /// If refined no further, it is an array. 31802 array, 31803 /// The type must be a vector. 31804 vector, 31805 /// The type must be a C pointer. 31806 c_ptr, 31807 /// The type must be a pointer (C or not). 31808 /// If refined no further, it is a non-C pointer. 31809 ptr, 31810 /// The type must be a function or a pointer to a function. 31811 /// If refined no further, it is a function. 31812 func, 31813 /// The type must be an enum literal, or some specific enum or union. Which one is decided 31814 /// afterwards based on the types in question. 31815 enum_or_union, 31816 /// The type must be some integer or float type. 31817 /// If refined no further, it is `comptime_int`. 31818 comptime_int, 31819 /// The type must be some float type. 31820 /// If refined no further, it is `comptime_float`. 31821 comptime_float, 31822 /// The type must be some float or fixed-width integer type. 31823 /// If refined no further, it is some fixed-width integer type. 31824 fixed_int, 31825 /// The type must be some fixed-width float type. 31826 fixed_float, 31827 /// The type must be a struct literal or tuple type. 31828 coercible_struct, 31829 /// The peers must all be of the same type. 31830 exact, 31831 31832 /// Given two strategies, find a strategy that satisfies both, if one exists. If no such 31833 /// strategy exists, any strategy may be returned; an error will be emitted when the caller 31834 /// attempts to use the strategy to resolve the type. 31835 /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at 31836 /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy. 31837 fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy { 31838 // Our merging should be order-independent. Thus, even though the union order is arbitrary, 31839 // by sorting the tags and switching first on the smaller, we have half as many cases to 31840 // worry about (since we avoid the duplicates). 31841 const s0_is_a = @intFromEnum(a) <= @intFromEnum(b); 31842 const s0 = if (s0_is_a) a else b; 31843 const s1 = if (s0_is_a) b else a; 31844 31845 const ReasonMethod = enum { 31846 all_s0, 31847 all_s1, 31848 either, 31849 }; 31850 31851 const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) { 31852 .unknown => .{ .all_s1, s1 }, 31853 .error_set => switch (s1) { 31854 .error_set => .{ .either, .error_set }, 31855 else => .{ .all_s0, .error_union }, 31856 }, 31857 .error_union => switch (s1) { 31858 .error_union => .{ .either, .error_union }, 31859 else => .{ .all_s0, .error_union }, 31860 }, 31861 .nullable => switch (s1) { 31862 .nullable => .{ .either, .nullable }, 31863 .c_ptr => .{ .all_s1, .c_ptr }, 31864 else => .{ .all_s0, .optional }, 31865 }, 31866 .optional => switch (s1) { 31867 .optional => .{ .either, .optional }, 31868 .c_ptr => .{ .all_s1, .c_ptr }, 31869 else => .{ .all_s0, .optional }, 31870 }, 31871 .array => switch (s1) { 31872 .array => .{ .either, .array }, 31873 .vector => .{ .all_s1, .vector }, 31874 else => .{ .all_s0, .array }, 31875 }, 31876 .vector => switch (s1) { 31877 .vector => .{ .either, .vector }, 31878 else => .{ .all_s0, .vector }, 31879 }, 31880 .c_ptr => switch (s1) { 31881 .c_ptr => .{ .either, .c_ptr }, 31882 else => .{ .all_s0, .c_ptr }, 31883 }, 31884 .ptr => switch (s1) { 31885 .ptr => .{ .either, .ptr }, 31886 else => .{ .all_s0, .ptr }, 31887 }, 31888 .func => switch (s1) { 31889 .func => .{ .either, .func }, 31890 else => .{ .all_s1, s1 }, // doesn't override anything later 31891 }, 31892 .enum_or_union => switch (s1) { 31893 .enum_or_union => .{ .either, .enum_or_union }, 31894 else => .{ .all_s0, .enum_or_union }, 31895 }, 31896 .comptime_int => switch (s1) { 31897 .comptime_int => .{ .either, .comptime_int }, 31898 else => .{ .all_s1, s1 }, // doesn't override anything later 31899 }, 31900 .comptime_float => switch (s1) { 31901 .comptime_float => .{ .either, .comptime_float }, 31902 else => .{ .all_s1, s1 }, // doesn't override anything later 31903 }, 31904 .fixed_int => switch (s1) { 31905 .fixed_int => .{ .either, .fixed_int }, 31906 else => .{ .all_s1, s1 }, // doesn't override anything later 31907 }, 31908 .fixed_float => switch (s1) { 31909 .fixed_float => .{ .either, .fixed_float }, 31910 else => .{ .all_s1, s1 }, // doesn't override anything later 31911 }, 31912 .coercible_struct => switch (s1) { 31913 .exact => .{ .all_s1, .exact }, 31914 else => .{ .all_s0, .coercible_struct }, 31915 }, 31916 .exact => .{ .all_s0, .exact }, 31917 }; 31918 31919 switch (res[0]) { 31920 .all_s0 => { 31921 if (!s0_is_a) { 31922 reason_peer.* = b_peer_idx; 31923 } 31924 }, 31925 .all_s1 => { 31926 if (s0_is_a) { 31927 reason_peer.* = b_peer_idx; 31928 } 31929 }, 31930 .either => { 31931 // Prefer the earliest peer 31932 reason_peer.* = @min(reason_peer.*, b_peer_idx); 31933 }, 31934 } 31935 31936 return res[1]; 31937 } 31938 31939 fn select(ty: Type, mod: *Module) PeerResolveStrategy { 31940 return switch (ty.zigTypeTag(mod)) { 31941 .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact, 31942 .NoReturn, .Undefined => .unknown, 31943 .Null => .nullable, 31944 .ComptimeInt => .comptime_int, 31945 .Int => .fixed_int, 31946 .ComptimeFloat => .comptime_float, 31947 .Float => .fixed_float, 31948 .Pointer => if (ty.ptrInfo(mod).flags.size == .C) .c_ptr else .ptr, 31949 .Array => .array, 31950 .Vector => .vector, 31951 .Optional => .optional, 31952 .ErrorSet => .error_set, 31953 .ErrorUnion => .error_union, 31954 .EnumLiteral, .Enum, .Union => .enum_or_union, 31955 .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact, 31956 .Fn => .func, 31957 }; 31958 } 31959 }; 31960 31961 const PeerResolveResult = union(enum) { 31962 /// The peer type resolution was successful, and resulted in the given type. 31963 success: Type, 31964 /// There was some generic conflict between two peers. 31965 conflict: struct { 31966 peer_idx_a: usize, 31967 peer_idx_b: usize, 31968 }, 31969 /// There was an error when resolving the type of a struct or tuple field. 31970 field_error: struct { 31971 /// The name of the field which caused the failure. 31972 field_name: []const u8, 31973 /// The type of this field in each peer. 31974 field_types: []Type, 31975 /// The error from resolving the field type. Guaranteed not to be `success`. 31976 sub_result: *PeerResolveResult, 31977 }, 31978 31979 fn report( 31980 result: PeerResolveResult, 31981 sema: *Sema, 31982 block: *Block, 31983 src: LazySrcLoc, 31984 instructions: []const Air.Inst.Ref, 31985 candidate_srcs: Module.PeerTypeCandidateSrc, 31986 ) !*Module.ErrorMsg { 31987 const mod = sema.mod; 31988 const decl_ptr = mod.declPtr(block.src_decl); 31989 31990 var opt_msg: ?*Module.ErrorMsg = null; 31991 errdefer if (opt_msg) |msg| msg.destroy(sema.gpa); 31992 31993 // If we mention fields we'll want to include field types, so put peer types in a buffer 31994 var peer_tys = try sema.arena.alloc(Type, instructions.len); 31995 for (peer_tys, instructions) |*ty, inst| { 31996 ty.* = sema.typeOf(inst); 31997 } 31998 31999 var cur = result; 32000 while (true) { 32001 var conflict_idx: [2]usize = undefined; 32002 32003 switch (cur) { 32004 .success => unreachable, 32005 .conflict => |conflict| { 32006 // Fall through to two-peer conflict handling below 32007 conflict_idx = .{ 32008 conflict.peer_idx_a, 32009 conflict.peer_idx_b, 32010 }; 32011 }, 32012 .field_error => |field_error| { 32013 const fmt = "struct field '{s}' has conflicting types"; 32014 const args = .{field_error.field_name}; 32015 if (opt_msg) |msg| { 32016 try sema.errNote(block, src, msg, fmt, args); 32017 } else { 32018 opt_msg = try sema.errMsg(block, src, fmt, args); 32019 } 32020 32021 // Continue on to child error 32022 cur = field_error.sub_result.*; 32023 peer_tys = field_error.field_types; 32024 continue; 32025 }, 32026 } 32027 32028 // This is the path for reporting a generic conflict between two peers. 32029 32030 if (conflict_idx[1] < conflict_idx[0]) { 32031 // b comes first in source, so it's better if it comes first in the error 32032 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]); 32033 } 32034 32035 const conflict_tys: [2]Type = .{ 32036 peer_tys[conflict_idx[0]], 32037 peer_tys[conflict_idx[1]], 32038 }; 32039 const conflict_srcs: [2]?LazySrcLoc = .{ 32040 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]), 32041 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]), 32042 }; 32043 32044 const fmt = "incompatible types: '{}' and '{}'"; 32045 const args = .{ 32046 conflict_tys[0].fmt(mod), 32047 conflict_tys[1].fmt(mod), 32048 }; 32049 const msg = if (opt_msg) |msg| msg: { 32050 try sema.errNote(block, src, msg, fmt, args); 32051 break :msg msg; 32052 } else msg: { 32053 const msg = try sema.errMsg(block, src, fmt, args); 32054 opt_msg = msg; 32055 break :msg msg; 32056 }; 32057 32058 if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)}); 32059 if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)}); 32060 32061 // No child error 32062 break; 32063 } 32064 32065 return opt_msg.?; 32066 } 32067 }; 32068 32069 fn resolvePeerTypes( 32070 sema: *Sema, 32071 block: *Block, 32072 src: LazySrcLoc, 32073 instructions: []const Air.Inst.Ref, 32074 candidate_srcs: Module.PeerTypeCandidateSrc, 32075 ) !Type { 32076 switch (instructions.len) { 32077 0 => return Type.noreturn, 32078 1 => return sema.typeOf(instructions[0]), 32079 else => {}, 32080 } 32081 32082 var peer_tys = try sema.arena.alloc(?Type, instructions.len); 32083 var peer_vals = try sema.arena.alloc(?Value, instructions.len); 32084 32085 for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { 32086 ty.* = sema.typeOf(inst); 32087 val.* = try sema.resolveMaybeUndefVal(inst); 32088 } 32089 32090 switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { 32091 .success => |ty| return ty, 32092 else => |result| { 32093 const msg = try result.report(sema, block, src, instructions, candidate_srcs); 32094 return sema.failWithOwnedErrorMsg(msg); 32095 }, 32096 } 32097 } 32098 32099 fn resolvePeerTypesInner( 32100 sema: *Sema, 32101 block: *Block, 32102 src: LazySrcLoc, 32103 peer_tys: []?Type, 32104 peer_vals: []?Value, 32105 ) !PeerResolveResult { 32106 const mod = sema.mod; 32107 32108 var strat_reason: usize = 0; 32109 var s: PeerResolveStrategy = .unknown; 32110 for (peer_tys, 0..) |opt_ty, i| { 32111 const ty = opt_ty orelse continue; 32112 s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i); 32113 } 32114 32115 if (s == .unknown) { 32116 // The whole thing was noreturn or undefined - try to do an exact match 32117 s = .exact; 32118 } else { 32119 // There was something other than noreturn and undefined, so we can ignore those peers 32120 for (peer_tys) |*ty_ptr| { 32121 const ty = ty_ptr.* orelse continue; 32122 switch (ty.zigTypeTag(mod)) { 32123 .NoReturn, .Undefined => ty_ptr.* = null, 32124 else => {}, 32125 } 32126 } 32127 } 32128 32129 const target = mod.getTarget(); 32130 32131 switch (s) { 32132 .unknown => unreachable, 32133 32134 .error_set => { 32135 var final_set: ?Type = null; 32136 for (peer_tys, 0..) |opt_ty, i| { 32137 const ty = opt_ty orelse continue; 32138 if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{ 32139 .peer_idx_a = strat_reason, 32140 .peer_idx_b = i, 32141 } }; 32142 if (final_set) |cur_set| { 32143 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty); 32144 } else { 32145 final_set = ty; 32146 } 32147 } 32148 return .{ .success = final_set.? }; 32149 }, 32150 32151 .error_union => { 32152 var final_set: ?Type = null; 32153 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 32154 const ty = ty_ptr.* orelse continue; 32155 const set_ty = switch (ty.zigTypeTag(mod)) { 32156 .ErrorSet => blk: { 32157 ty_ptr.* = null; // no payload to decide on 32158 val_ptr.* = null; 32159 break :blk ty; 32160 }, 32161 .ErrorUnion => blk: { 32162 const set_ty = ty.errorUnionSet(mod); 32163 ty_ptr.* = ty.errorUnionPayload(mod); 32164 if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) { 32165 .error_union => |eu| switch (eu.val) { 32166 .payload => |payload_ip| val_ptr.* = payload_ip.toValue(), 32167 .err_name => val_ptr.* = null, 32168 }, 32169 .undef => val_ptr.* = (try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() })).toValue(), 32170 else => unreachable, 32171 }; 32172 break :blk set_ty; 32173 }, 32174 else => continue, // whole type is the payload 32175 }; 32176 if (final_set) |cur_set| { 32177 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty); 32178 } else { 32179 final_set = set_ty; 32180 } 32181 } 32182 assert(final_set != null); 32183 const final_payload = switch (try sema.resolvePeerTypesInner( 32184 block, 32185 src, 32186 peer_tys, 32187 peer_vals, 32188 )) { 32189 .success => |ty| ty, 32190 else => |result| return result, 32191 }; 32192 return .{ .success = try mod.errorUnionType(final_set.?, final_payload) }; 32193 }, 32194 32195 .nullable => { 32196 for (peer_tys, 0..) |opt_ty, i| { 32197 const ty = opt_ty orelse continue; 32198 if (!ty.eql(Type.null, mod)) return .{ .conflict = .{ 32199 .peer_idx_a = strat_reason, 32200 .peer_idx_b = i, 32201 } }; 32202 } 32203 return .{ .success = Type.null }; 32204 }, 32205 32206 .optional => { 32207 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 32208 const ty = ty_ptr.* orelse continue; 32209 switch (ty.zigTypeTag(mod)) { 32210 .Null => { 32211 ty_ptr.* = null; 32212 val_ptr.* = null; 32213 }, 32214 .Optional => { 32215 ty_ptr.* = ty.optionalChild(mod); 32216 if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null; 32217 }, 32218 else => {}, 32219 } 32220 } 32221 const child_ty = switch (try sema.resolvePeerTypesInner( 32222 block, 32223 src, 32224 peer_tys, 32225 peer_vals, 32226 )) { 32227 .success => |ty| ty, 32228 else => |result| return result, 32229 }; 32230 return .{ .success = try mod.optionalType(child_ty.toIntern()) }; 32231 }, 32232 32233 .array => { 32234 // Index of the first non-null peer 32235 var opt_first_idx: ?usize = null; 32236 // Index of the first array or vector peer (i.e. not a tuple) 32237 var opt_first_arr_idx: ?usize = null; 32238 // Set to non-null once we see any peer, even a tuple 32239 var len: u64 = undefined; 32240 var sentinel: ?Value = undefined; 32241 // Only set once we see a non-tuple peer 32242 var elem_ty: Type = undefined; 32243 32244 for (peer_tys, 0..) |*ty_ptr, i| { 32245 const ty = ty_ptr.* orelse continue; 32246 32247 if (!ty.isArrayOrVector(mod)) { 32248 // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced. 32249 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 32250 .peer_idx_a = strat_reason, 32251 .peer_idx_b = i, 32252 } }; 32253 32254 if (opt_first_idx) |first_idx| { 32255 if (arr_like.len != len) return .{ .conflict = .{ 32256 .peer_idx_a = first_idx, 32257 .peer_idx_b = i, 32258 } }; 32259 } else { 32260 opt_first_idx = i; 32261 len = arr_like.len; 32262 } 32263 32264 sentinel = null; 32265 32266 continue; 32267 } 32268 32269 const first_arr_idx = opt_first_arr_idx orelse { 32270 if (opt_first_idx == null) { 32271 opt_first_idx = i; 32272 len = ty.arrayLen(mod); 32273 sentinel = ty.sentinel(mod); 32274 } 32275 opt_first_arr_idx = i; 32276 elem_ty = ty.childType(mod); 32277 continue; 32278 }; 32279 32280 if (ty.arrayLen(mod) != len) return .{ .conflict = .{ 32281 .peer_idx_a = first_arr_idx, 32282 .peer_idx_b = i, 32283 } }; 32284 32285 if (!ty.childType(mod).eql(elem_ty, mod)) { 32286 return .{ .conflict = .{ 32287 .peer_idx_a = first_arr_idx, 32288 .peer_idx_b = i, 32289 } }; 32290 } 32291 32292 if (sentinel) |cur_sent| { 32293 if (ty.sentinel(mod)) |peer_sent| { 32294 if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null; 32295 } else { 32296 sentinel = null; 32297 } 32298 } 32299 } 32300 32301 // There should always be at least one array or vector peer 32302 assert(opt_first_arr_idx != null); 32303 32304 return .{ .success = try mod.arrayType(.{ 32305 .len = len, 32306 .child = elem_ty.toIntern(), 32307 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none, 32308 }) }; 32309 }, 32310 32311 .vector => { 32312 var len: ?u64 = null; 32313 var first_idx: usize = undefined; 32314 for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| { 32315 const ty = ty_ptr.* orelse continue; 32316 32317 if (!ty.isArrayOrVector(mod)) { 32318 // Allow tuples of the correct length 32319 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 32320 .peer_idx_a = strat_reason, 32321 .peer_idx_b = i, 32322 } }; 32323 32324 if (len) |expect_len| { 32325 if (arr_like.len != expect_len) return .{ .conflict = .{ 32326 .peer_idx_a = first_idx, 32327 .peer_idx_b = i, 32328 } }; 32329 } else { 32330 len = arr_like.len; 32331 first_idx = i; 32332 } 32333 32334 // Tuples won't participate in the child type resolution. We'll resolve without 32335 // them, and if the tuples have a bad type, we'll get a coercion error later. 32336 ty_ptr.* = null; 32337 val_ptr.* = null; 32338 32339 continue; 32340 } 32341 32342 if (len) |expect_len| { 32343 if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{ 32344 .peer_idx_a = first_idx, 32345 .peer_idx_b = i, 32346 } }; 32347 } else { 32348 len = ty.arrayLen(mod); 32349 first_idx = i; 32350 } 32351 32352 ty_ptr.* = ty.childType(mod); 32353 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR 32354 } 32355 32356 const child_ty = switch (try sema.resolvePeerTypesInner( 32357 block, 32358 src, 32359 peer_tys, 32360 peer_vals, 32361 )) { 32362 .success => |ty| ty, 32363 else => |result| return result, 32364 }; 32365 32366 return .{ .success = try mod.vectorType(.{ 32367 .len = @as(u32, @intCast(len.?)), 32368 .child = child_ty.toIntern(), 32369 }) }; 32370 }, 32371 32372 .c_ptr => { 32373 var opt_ptr_info: ?InternPool.Key.PtrType = null; 32374 var first_idx: usize = undefined; 32375 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 32376 const ty = opt_ty orelse continue; 32377 switch (ty.zigTypeTag(mod)) { 32378 .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers 32379 .Int => { 32380 if (opt_val != null) { 32381 // Always allow the coercion for comptime-known ints 32382 continue; 32383 } else { 32384 // Runtime-known, so check if the type is no bigger than a usize 32385 const ptr_bits = target.ptrBitWidth(); 32386 const bits = ty.intInfo(mod).bits; 32387 if (bits <= ptr_bits) continue; 32388 } 32389 }, 32390 .Null => continue, 32391 else => {}, 32392 } 32393 32394 if (!ty.isPtrAtRuntime(mod)) return .{ .conflict = .{ 32395 .peer_idx_a = strat_reason, 32396 .peer_idx_b = i, 32397 } }; 32398 32399 // Goes through optionals 32400 const peer_info = ty.ptrInfo(mod); 32401 32402 var ptr_info = opt_ptr_info orelse { 32403 opt_ptr_info = peer_info; 32404 opt_ptr_info.?.flags.size = .C; 32405 first_idx = i; 32406 continue; 32407 }; 32408 32409 // Try peer -> cur, then cur -> peer 32410 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) orelse { 32411 return .{ .conflict = .{ 32412 .peer_idx_a = first_idx, 32413 .peer_idx_b = i, 32414 } }; 32415 }).toIntern(); 32416 32417 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) { 32418 const peer_sent = try mod.intern_pool.getCoerced(sema.gpa, ptr_info.sentinel, ptr_info.child); 32419 const ptr_sent = try mod.intern_pool.getCoerced(sema.gpa, peer_info.sentinel, ptr_info.child); 32420 if (ptr_sent == peer_sent) { 32421 ptr_info.sentinel = ptr_sent; 32422 } else { 32423 ptr_info.sentinel = .none; 32424 } 32425 } else { 32426 ptr_info.sentinel = .none; 32427 } 32428 32429 // Note that the align can be always non-zero; Module.ptrType will canonicalize it 32430 ptr_info.flags.alignment = Alignment.fromByteUnits(@min( 32431 ptr_info.flags.alignment.toByteUnitsOptional() orelse 32432 ptr_info.child.toType().abiAlignment(mod), 32433 peer_info.flags.alignment.toByteUnitsOptional() orelse 32434 peer_info.child.toType().abiAlignment(mod), 32435 )); 32436 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 32437 return .{ .conflict = .{ 32438 .peer_idx_a = first_idx, 32439 .peer_idx_b = i, 32440 } }; 32441 } 32442 32443 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 32444 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 32445 { 32446 return .{ .conflict = .{ 32447 .peer_idx_a = first_idx, 32448 .peer_idx_b = i, 32449 } }; 32450 } 32451 32452 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 32453 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 32454 32455 opt_ptr_info = ptr_info; 32456 } 32457 return .{ .success = try mod.ptrType(opt_ptr_info.?) }; 32458 }, 32459 32460 .ptr => { 32461 // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only 32462 // if there were no actual slices. Else, we want the slice index to report a conflict. 32463 var opt_slice_idx: ?usize = null; 32464 32465 var opt_ptr_info: ?InternPool.Key.PtrType = null; 32466 var first_idx: usize = undefined; 32467 var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error 32468 32469 for (peer_tys, 0..) |opt_ty, i| { 32470 const ty = opt_ty orelse continue; 32471 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(mod)) { 32472 .Pointer => ty.ptrInfo(mod), 32473 .Fn => .{ 32474 .child = ty.toIntern(), 32475 .flags = .{ 32476 .address_space = target_util.defaultAddressSpace(target, .global_constant), 32477 }, 32478 }, 32479 else => return .{ .conflict = .{ 32480 .peer_idx_a = strat_reason, 32481 .peer_idx_b = i, 32482 } }, 32483 }; 32484 32485 switch (peer_info.flags.size) { 32486 .One, .Many => {}, 32487 .Slice => opt_slice_idx = i, 32488 .C => return .{ .conflict = .{ 32489 .peer_idx_a = strat_reason, 32490 .peer_idx_b = i, 32491 } }, 32492 } 32493 32494 var ptr_info = opt_ptr_info orelse { 32495 opt_ptr_info = peer_info; 32496 first_idx = i; 32497 continue; 32498 }; 32499 32500 other_idx = i; 32501 32502 // We want to return this in a lot of cases, so alias it here for convenience 32503 const generic_err: PeerResolveResult = .{ .conflict = .{ 32504 .peer_idx_a = first_idx, 32505 .peer_idx_b = i, 32506 } }; 32507 32508 // Note that the align can be always non-zero; Type.ptr will canonicalize it 32509 ptr_info.flags.alignment = Alignment.fromByteUnits(@min( 32510 ptr_info.flags.alignment.toByteUnitsOptional() orelse 32511 ptr_info.child.toType().abiAlignment(mod), 32512 peer_info.flags.alignment.toByteUnitsOptional() orelse 32513 peer_info.child.toType().abiAlignment(mod), 32514 )); 32515 32516 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 32517 return generic_err; 32518 } 32519 32520 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 32521 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 32522 { 32523 return generic_err; 32524 } 32525 32526 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 32527 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 32528 32529 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) { 32530 .One => switch (mod.intern_pool.indexToKey(peer_info.child)) { 32531 .array_type => |array_type| array_type.sentinel, 32532 else => .none, 32533 }, 32534 .Many, .Slice => peer_info.sentinel, 32535 .C => unreachable, 32536 }; 32537 32538 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) { 32539 .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) { 32540 .array_type => |array_type| array_type.sentinel, 32541 else => .none, 32542 }, 32543 .Many, .Slice => ptr_info.sentinel, 32544 .C => unreachable, 32545 }; 32546 32547 // We abstract array handling slightly so that tuple pointers can work like array pointers 32548 const peer_pointee_array = sema.typeIsArrayLike(peer_info.child.toType()); 32549 const cur_pointee_array = sema.typeIsArrayLike(ptr_info.child.toType()); 32550 32551 // This switch is just responsible for deciding the size and pointee (not including 32552 // single-pointer array sentinel). 32553 good: { 32554 switch (peer_info.flags.size) { 32555 .One => switch (ptr_info.flags.size) { 32556 .One => { 32557 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| { 32558 ptr_info.child = pointee.toIntern(); 32559 break :good; 32560 } 32561 32562 const cur_arr = cur_pointee_array orelse return generic_err; 32563 const peer_arr = peer_pointee_array orelse return generic_err; 32564 32565 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| { 32566 // *[n:x]T + *[n:y]T = *[n]T 32567 if (cur_arr.len == peer_arr.len) { 32568 ptr_info.child = (try mod.arrayType(.{ 32569 .len = cur_arr.len, 32570 .child = elem_ty.toIntern(), 32571 })).toIntern(); 32572 break :good; 32573 } 32574 // *[a]T + *[b]T = []T 32575 ptr_info.flags.size = .Slice; 32576 ptr_info.child = elem_ty.toIntern(); 32577 break :good; 32578 } 32579 32580 if (peer_arr.elem_ty.toIntern() == .noreturn_type) { 32581 // *struct{} + *[a]T = []T 32582 ptr_info.flags.size = .Slice; 32583 ptr_info.child = cur_arr.elem_ty.toIntern(); 32584 break :good; 32585 } 32586 32587 if (cur_arr.elem_ty.toIntern() == .noreturn_type) { 32588 // *[a]T + *struct{} = []T 32589 ptr_info.flags.size = .Slice; 32590 ptr_info.child = peer_arr.elem_ty.toIntern(); 32591 break :good; 32592 } 32593 32594 return generic_err; 32595 }, 32596 .Many => { 32597 // Only works for *[n]T + [*]T -> [*]T 32598 const arr = peer_pointee_array orelse return generic_err; 32599 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), arr.elem_ty)) |pointee| { 32600 ptr_info.child = pointee.toIntern(); 32601 break :good; 32602 } 32603 if (arr.elem_ty.toIntern() == .noreturn_type) { 32604 // *struct{} + [*]T -> [*]T 32605 break :good; 32606 } 32607 return generic_err; 32608 }, 32609 .Slice => { 32610 // Only works for *[n]T + []T -> []T 32611 const arr = peer_pointee_array orelse return generic_err; 32612 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), arr.elem_ty)) |pointee| { 32613 ptr_info.child = pointee.toIntern(); 32614 break :good; 32615 } 32616 if (arr.elem_ty.toIntern() == .noreturn_type) { 32617 // *struct{} + []T -> []T 32618 break :good; 32619 } 32620 return generic_err; 32621 }, 32622 .C => unreachable, 32623 }, 32624 .Many => switch (ptr_info.flags.size) { 32625 .One => { 32626 // Only works for [*]T + *[n]T -> [*]T 32627 const arr = cur_pointee_array orelse return generic_err; 32628 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.child.toType())) |pointee| { 32629 ptr_info.flags.size = .Many; 32630 ptr_info.child = pointee.toIntern(); 32631 break :good; 32632 } 32633 if (arr.elem_ty.toIntern() == .noreturn_type) { 32634 // [*]T + *struct{} -> [*]T 32635 ptr_info.flags.size = .Many; 32636 ptr_info.child = peer_info.child; 32637 break :good; 32638 } 32639 return generic_err; 32640 }, 32641 .Many => { 32642 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| { 32643 ptr_info.child = pointee.toIntern(); 32644 break :good; 32645 } 32646 return generic_err; 32647 }, 32648 .Slice => { 32649 // Only works if no peers are actually slices 32650 if (opt_slice_idx) |slice_idx| { 32651 return .{ .conflict = .{ 32652 .peer_idx_a = slice_idx, 32653 .peer_idx_b = i, 32654 } }; 32655 } 32656 // Okay, then works for [*]T + "[]T" -> [*]T 32657 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| { 32658 ptr_info.flags.size = .Many; 32659 ptr_info.child = pointee.toIntern(); 32660 break :good; 32661 } 32662 return generic_err; 32663 }, 32664 .C => unreachable, 32665 }, 32666 .Slice => switch (ptr_info.flags.size) { 32667 .One => { 32668 // Only works for []T + *[n]T -> []T 32669 const arr = cur_pointee_array orelse return generic_err; 32670 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.child.toType())) |pointee| { 32671 ptr_info.flags.size = .Slice; 32672 ptr_info.child = pointee.toIntern(); 32673 break :good; 32674 } 32675 if (arr.elem_ty.toIntern() == .noreturn_type) { 32676 // []T + *struct{} -> []T 32677 ptr_info.flags.size = .Slice; 32678 ptr_info.child = peer_info.child; 32679 break :good; 32680 } 32681 return generic_err; 32682 }, 32683 .Many => { 32684 // Impossible! (current peer is an actual slice) 32685 return generic_err; 32686 }, 32687 .Slice => { 32688 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| { 32689 ptr_info.child = pointee.toIntern(); 32690 break :good; 32691 } 32692 return generic_err; 32693 }, 32694 .C => unreachable, 32695 }, 32696 .C => unreachable, 32697 } 32698 } 32699 32700 const sentinel_ty = switch (ptr_info.flags.size) { 32701 .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) { 32702 .array_type => |array_type| array_type.child, 32703 else => ptr_info.child, 32704 }, 32705 .Many, .Slice, .C => ptr_info.child, 32706 }; 32707 32708 sentinel: { 32709 no_sentinel: { 32710 if (peer_sentinel == .none) break :no_sentinel; 32711 if (cur_sentinel == .none) break :no_sentinel; 32712 const peer_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, peer_sentinel, sentinel_ty); 32713 const cur_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, cur_sentinel, sentinel_ty); 32714 if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel; 32715 // Sentinels match 32716 if (ptr_info.flags.size == .One) switch (mod.intern_pool.indexToKey(ptr_info.child)) { 32717 .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{ 32718 .len = array_type.len, 32719 .child = array_type.child, 32720 .sentinel = cur_sent_coerced, 32721 })).toIntern(), 32722 else => unreachable, 32723 } else { 32724 ptr_info.sentinel = cur_sent_coerced; 32725 } 32726 break :sentinel; 32727 } 32728 // Clear existing sentinel 32729 ptr_info.sentinel = .none; 32730 switch (mod.intern_pool.indexToKey(ptr_info.child)) { 32731 .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{ 32732 .len = array_type.len, 32733 .child = array_type.child, 32734 .sentinel = .none, 32735 })).toIntern(), 32736 else => {}, 32737 } 32738 } 32739 32740 opt_ptr_info = ptr_info; 32741 } 32742 32743 // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance) 32744 // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to 32745 // coerce the empty struct to a specific type, but no peer provided one. We need to 32746 // detect this case and emit an error. 32747 const pointee = opt_ptr_info.?.child; 32748 switch (pointee) { 32749 .noreturn_type => return .{ .conflict = .{ 32750 .peer_idx_a = first_idx, 32751 .peer_idx_b = other_idx, 32752 } }, 32753 else => switch (mod.intern_pool.indexToKey(pointee)) { 32754 .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{ 32755 .peer_idx_a = first_idx, 32756 .peer_idx_b = other_idx, 32757 } }, 32758 else => {}, 32759 }, 32760 } 32761 32762 return .{ .success = try mod.ptrType(opt_ptr_info.?) }; 32763 }, 32764 32765 .func => { 32766 var opt_cur_ty: ?Type = null; 32767 var first_idx: usize = undefined; 32768 for (peer_tys, 0..) |opt_ty, i| { 32769 const ty = opt_ty orelse continue; 32770 const cur_ty = opt_cur_ty orelse { 32771 opt_cur_ty = ty; 32772 first_idx = i; 32773 continue; 32774 }; 32775 if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{ 32776 .peer_idx_a = strat_reason, 32777 .peer_idx_b = i, 32778 } }; 32779 // ty -> cur_ty 32780 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) { 32781 continue; 32782 } 32783 // cur_ty -> ty 32784 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) { 32785 opt_cur_ty = ty; 32786 continue; 32787 } 32788 return .{ .conflict = .{ 32789 .peer_idx_a = first_idx, 32790 .peer_idx_b = i, 32791 } }; 32792 } 32793 return .{ .success = opt_cur_ty.? }; 32794 }, 32795 32796 .enum_or_union => { 32797 var opt_cur_ty: ?Type = null; 32798 // The peer index which gave the current type 32799 var cur_ty_idx: usize = undefined; 32800 32801 for (peer_tys, 0..) |opt_ty, i| { 32802 const ty = opt_ty orelse continue; 32803 switch (ty.zigTypeTag(mod)) { 32804 .EnumLiteral, .Enum, .Union => {}, 32805 else => return .{ .conflict = .{ 32806 .peer_idx_a = strat_reason, 32807 .peer_idx_b = i, 32808 } }, 32809 } 32810 const cur_ty = opt_cur_ty orelse { 32811 opt_cur_ty = ty; 32812 cur_ty_idx = i; 32813 continue; 32814 }; 32815 32816 // We want to return this in a lot of cases, so alias it here for convenience 32817 const generic_err: PeerResolveResult = .{ .conflict = .{ 32818 .peer_idx_a = cur_ty_idx, 32819 .peer_idx_b = i, 32820 } }; 32821 32822 switch (cur_ty.zigTypeTag(mod)) { 32823 .EnumLiteral => { 32824 opt_cur_ty = ty; 32825 cur_ty_idx = i; 32826 }, 32827 .Enum => switch (ty.zigTypeTag(mod)) { 32828 .EnumLiteral => {}, 32829 .Enum => { 32830 if (!ty.eql(cur_ty, mod)) return generic_err; 32831 }, 32832 .Union => { 32833 const tag_ty = ty.unionTagTypeHypothetical(mod); 32834 if (!tag_ty.eql(cur_ty, mod)) return generic_err; 32835 opt_cur_ty = ty; 32836 cur_ty_idx = i; 32837 }, 32838 else => unreachable, 32839 }, 32840 .Union => switch (ty.zigTypeTag(mod)) { 32841 .EnumLiteral => {}, 32842 .Enum => { 32843 const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod); 32844 if (!ty.eql(cur_tag_ty, mod)) return generic_err; 32845 }, 32846 .Union => { 32847 if (!ty.eql(cur_ty, mod)) return generic_err; 32848 }, 32849 else => unreachable, 32850 }, 32851 else => unreachable, 32852 } 32853 } 32854 return .{ .success = opt_cur_ty.? }; 32855 }, 32856 32857 .comptime_int => { 32858 for (peer_tys, 0..) |opt_ty, i| { 32859 const ty = opt_ty orelse continue; 32860 switch (ty.zigTypeTag(mod)) { 32861 .ComptimeInt => {}, 32862 else => return .{ .conflict = .{ 32863 .peer_idx_a = strat_reason, 32864 .peer_idx_b = i, 32865 } }, 32866 } 32867 } 32868 return .{ .success = Type.comptime_int }; 32869 }, 32870 32871 .comptime_float => { 32872 for (peer_tys, 0..) |opt_ty, i| { 32873 const ty = opt_ty orelse continue; 32874 switch (ty.zigTypeTag(mod)) { 32875 .ComptimeInt, .ComptimeFloat => {}, 32876 else => return .{ .conflict = .{ 32877 .peer_idx_a = strat_reason, 32878 .peer_idx_b = i, 32879 } }, 32880 } 32881 } 32882 return .{ .success = Type.comptime_float }; 32883 }, 32884 32885 .fixed_int => { 32886 var idx_unsigned: ?usize = null; 32887 var idx_signed: ?usize = null; 32888 32889 // TODO: this is for compatibility with legacy behavior. See beneath the loop. 32890 var any_comptime_known = false; 32891 32892 for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| { 32893 const ty = opt_ty orelse continue; 32894 const opt_val = ptr_opt_val.*; 32895 32896 const peer_tag = ty.zigTypeTag(mod); 32897 switch (peer_tag) { 32898 .ComptimeInt => { 32899 // If the value is undefined, we can't refine to a fixed-width int 32900 if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .conflict = .{ 32901 .peer_idx_a = strat_reason, 32902 .peer_idx_b = i, 32903 } }; 32904 any_comptime_known = true; 32905 ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?); 32906 continue; 32907 }, 32908 .Int => {}, 32909 else => return .{ .conflict = .{ 32910 .peer_idx_a = strat_reason, 32911 .peer_idx_b = i, 32912 } }, 32913 } 32914 32915 if (opt_val != null) any_comptime_known = true; 32916 32917 const info = ty.intInfo(mod); 32918 32919 const idx_ptr = switch (info.signedness) { 32920 .unsigned => &idx_unsigned, 32921 .signed => &idx_signed, 32922 }; 32923 32924 const largest_idx = idx_ptr.* orelse { 32925 idx_ptr.* = i; 32926 continue; 32927 }; 32928 32929 const cur_info = peer_tys[largest_idx].?.intInfo(mod); 32930 if (info.bits > cur_info.bits) { 32931 idx_ptr.* = i; 32932 } 32933 } 32934 32935 if (idx_signed == null) { 32936 return .{ .success = peer_tys[idx_unsigned.?].? }; 32937 } 32938 32939 if (idx_unsigned == null) { 32940 return .{ .success = peer_tys[idx_signed.?].? }; 32941 } 32942 32943 const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod); 32944 const signed_info = peer_tys[idx_signed.?].?.intInfo(mod); 32945 if (signed_info.bits > unsigned_info.bits) { 32946 return .{ .success = peer_tys[idx_signed.?].? }; 32947 } 32948 32949 // TODO: this is for compatibility with legacy behavior. Before this version of PTR was 32950 // implemented, the algorithm very often returned false positives, with the expectation 32951 // that you'd just hit a coercion error later. One of these was that for integers, the 32952 // largest type would always be returned, even if it couldn't fit everything. This had 32953 // an unintentional consequence to semantics, which is that if values were known at 32954 // comptime, they would be coerced down to the smallest type where possible. This 32955 // behavior is unintuitive and order-dependent, so in my opinion should be eliminated, 32956 // but for now we'll retain compatibility. 32957 if (any_comptime_known) { 32958 if (unsigned_info.bits > signed_info.bits) { 32959 return .{ .success = peer_tys[idx_unsigned.?].? }; 32960 } 32961 const idx = @min(idx_unsigned.?, idx_signed.?); 32962 return .{ .success = peer_tys[idx].? }; 32963 } 32964 32965 return .{ .conflict = .{ 32966 .peer_idx_a = idx_unsigned.?, 32967 .peer_idx_b = idx_signed.?, 32968 } }; 32969 }, 32970 32971 .fixed_float => { 32972 var opt_cur_ty: ?Type = null; 32973 32974 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 32975 const ty = opt_ty orelse continue; 32976 switch (ty.zigTypeTag(mod)) { 32977 .ComptimeFloat, .ComptimeInt => {}, 32978 .Int => { 32979 if (opt_val == null) return .{ .conflict = .{ 32980 .peer_idx_a = strat_reason, 32981 .peer_idx_b = i, 32982 } }; 32983 }, 32984 .Float => { 32985 if (opt_cur_ty) |cur_ty| { 32986 if (cur_ty.eql(ty, mod)) continue; 32987 // Recreate the type so we eliminate any c_longdouble 32988 const bits = @max(cur_ty.floatBits(target), ty.floatBits(target)); 32989 opt_cur_ty = switch (bits) { 32990 16 => Type.f16, 32991 32 => Type.f32, 32992 64 => Type.f64, 32993 80 => Type.f80, 32994 128 => Type.f128, 32995 else => unreachable, 32996 }; 32997 } else { 32998 opt_cur_ty = ty; 32999 } 33000 }, 33001 else => return .{ .conflict = .{ 33002 .peer_idx_a = strat_reason, 33003 .peer_idx_b = i, 33004 } }, 33005 } 33006 } 33007 33008 // Note that fixed_float is only chosen if there is at least one fixed-width float peer, 33009 // so opt_cur_ty must be non-null. 33010 return .{ .success = opt_cur_ty.? }; 33011 }, 33012 33013 .coercible_struct => { 33014 // First, check that every peer has the same approximate structure (field count and names) 33015 33016 var opt_first_idx: ?usize = null; 33017 var is_tuple: bool = undefined; 33018 var field_count: usize = undefined; 33019 // Only defined for non-tuples. 33020 var field_names: []InternPool.NullTerminatedString = undefined; 33021 33022 for (peer_tys, 0..) |opt_ty, i| { 33023 const ty = opt_ty orelse continue; 33024 33025 if (!ty.isTupleOrAnonStruct(mod)) { 33026 return .{ .conflict = .{ 33027 .peer_idx_a = strat_reason, 33028 .peer_idx_b = i, 33029 } }; 33030 } 33031 33032 const first_idx = opt_first_idx orelse { 33033 opt_first_idx = i; 33034 is_tuple = ty.isTuple(mod); 33035 field_count = ty.structFieldCount(mod); 33036 if (!is_tuple) { 33037 const names = mod.intern_pool.indexToKey(ty.toIntern()).anon_struct_type.names; 33038 field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names); 33039 } 33040 continue; 33041 }; 33042 33043 if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) { 33044 return .{ .conflict = .{ 33045 .peer_idx_a = first_idx, 33046 .peer_idx_b = i, 33047 } }; 33048 } 33049 33050 if (!is_tuple) { 33051 for (field_names, 0..) |expected, field_idx| { 33052 const actual = ty.structFieldName(field_idx, mod); 33053 if (actual == expected) continue; 33054 return .{ .conflict = .{ 33055 .peer_idx_a = first_idx, 33056 .peer_idx_b = i, 33057 } }; 33058 } 33059 } 33060 } 33061 33062 assert(opt_first_idx != null); 33063 33064 // Now, we'll recursively resolve the field types 33065 const field_types = try sema.arena.alloc(InternPool.Index, field_count); 33066 // Values for `comptime` fields - `.none` used for non-comptime fields 33067 const field_vals = try sema.arena.alloc(InternPool.Index, field_count); 33068 const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); 33069 const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); 33070 33071 for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| { 33072 // Fill buffers with types and values of the field 33073 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { 33074 const ty = opt_ty orelse { 33075 peer_field_ty.* = null; 33076 peer_field_val.* = null; 33077 continue; 33078 }; 33079 peer_field_ty.* = ty.structFieldType(field_idx, mod); 33080 peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null; 33081 } 33082 33083 // Resolve field type recursively 33084 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) { 33085 .success => |ty| ty.toIntern(), 33086 else => |result| { 33087 const result_buf = try sema.arena.create(PeerResolveResult); 33088 result_buf.* = result; 33089 const field_name = if (is_tuple) name: { 33090 break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx}); 33091 } else try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(field_names[field_idx])); 33092 33093 // The error info needs the field types, but we can't reuse sub_peer_tys 33094 // since the recursive call may have clobbered it. 33095 const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len); 33096 for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| { 33097 // Already-resolved types won't be referenced by the error so it's fine 33098 // to leave them undefined. 33099 const ty = opt_ty orelse continue; 33100 peer_field_ty.* = ty.structFieldType(field_idx, mod); 33101 } 33102 33103 return .{ .field_error = .{ 33104 .field_name = field_name, 33105 .field_types = peer_field_tys, 33106 .sub_result = result_buf, 33107 } }; 33108 }, 33109 }; 33110 33111 // Decide if this is a comptime field. If it is comptime in all peers, and the 33112 // coerced comptime values are all the same, we say it is comptime, else not. 33113 33114 var comptime_val: ?Value = null; 33115 for (peer_tys) |opt_ty| { 33116 const struct_ty = opt_ty orelse continue; 33117 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse { 33118 comptime_val = null; 33119 break; 33120 }; 33121 const uncoerced_field = try sema.addConstant(uncoerced_field_val); 33122 const coerced_inst = sema.coerceExtra(block, field_ty.toType(), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) { 33123 // 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 33124 error.NotCoercible => { 33125 comptime_val = null; 33126 break; 33127 }, 33128 else => |e| return e, 33129 }; 33130 const coerced_val = (try sema.resolveMaybeUndefVal(coerced_inst)) orelse continue; 33131 const existing = comptime_val orelse { 33132 comptime_val = coerced_val; 33133 continue; 33134 }; 33135 if (!coerced_val.eql(existing, field_ty.toType(), mod)) { 33136 comptime_val = null; 33137 break; 33138 } 33139 } 33140 33141 field_val.* = if (comptime_val) |v| v.toIntern() else .none; 33142 } 33143 33144 const final_ty = try mod.intern(.{ .anon_struct_type = .{ 33145 .types = field_types, 33146 .names = if (is_tuple) &.{} else field_names, 33147 .values = field_vals, 33148 } }); 33149 33150 return .{ .success = final_ty.toType() }; 33151 }, 33152 33153 .exact => { 33154 var expect_ty: ?Type = null; 33155 var first_idx: usize = undefined; 33156 for (peer_tys, 0..) |opt_ty, i| { 33157 const ty = opt_ty orelse continue; 33158 if (expect_ty) |expect| { 33159 if (!ty.eql(expect, mod)) return .{ .conflict = .{ 33160 .peer_idx_a = first_idx, 33161 .peer_idx_b = i, 33162 } }; 33163 } else { 33164 expect_ty = ty; 33165 first_idx = i; 33166 } 33167 } 33168 return .{ .success = expect_ty.? }; 33169 }, 33170 } 33171 } 33172 33173 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { 33174 // e0 -> e1 33175 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { 33176 return e1; 33177 } 33178 33179 // e1 -> e0 33180 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { 33181 return e0; 33182 } 33183 33184 return sema.errorSetMerge(e0, e1); 33185 } 33186 33187 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { 33188 // ty_b -> ty_a 33189 if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) { 33190 return ty_a; 33191 } 33192 33193 // ty_a -> ty_b 33194 if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) { 33195 return ty_b; 33196 } 33197 33198 return null; 33199 } 33200 33201 const ArrayLike = struct { 33202 len: u64, 33203 /// `noreturn` indicates that this type is `struct{}` so can coerce to anything 33204 elem_ty: Type, 33205 }; 33206 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { 33207 const mod = sema.mod; 33208 return switch (ty.zigTypeTag(mod)) { 33209 .Array => .{ 33210 .len = ty.arrayLen(mod), 33211 .elem_ty = ty.childType(mod), 33212 }, 33213 .Struct => { 33214 const field_count = ty.structFieldCount(mod); 33215 if (field_count == 0) return .{ 33216 .len = 0, 33217 .elem_ty = Type.noreturn, 33218 }; 33219 if (!ty.isTuple(mod)) return null; 33220 const elem_ty = ty.structFieldType(0, mod); 33221 for (1..field_count) |i| { 33222 if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) { 33223 return null; 33224 } 33225 } 33226 return .{ 33227 .len = field_count, 33228 .elem_ty = elem_ty, 33229 }; 33230 }, 33231 else => null, 33232 }; 33233 } 33234 33235 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { 33236 const mod = sema.mod; 33237 try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.return_type.toType()); 33238 33239 if (mod.comp.bin_file.options.error_return_tracing and mod.typeToFunc(fn_ty).?.return_type.toType().isError(mod)) { 33240 // Ensure the type exists so that backends can assume that. 33241 _ = try sema.getBuiltinType("StackTrace"); 33242 } 33243 33244 for (0..mod.typeToFunc(fn_ty).?.param_types.len) |i| { 33245 try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.param_types[i].toType()); 33246 } 33247 } 33248 33249 /// Make it so that calling hash() and eql() on `val` will not assert due 33250 /// to a type not having its layout resolved. 33251 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { 33252 const mod = sema.mod; 33253 switch (mod.intern_pool.indexToKey(val.toIntern())) { 33254 .int => |int| switch (int.storage) { 33255 .u64, .i64, .big_int => return val, 33256 .lazy_align, .lazy_size => return (try mod.intern(.{ .int = .{ 33257 .ty = int.ty, 33258 .storage = .{ .u64 = (try val.getUnsignedIntAdvanced(mod, sema)).? }, 33259 } })).toValue(), 33260 }, 33261 .ptr => |ptr| { 33262 const resolved_len = switch (ptr.len) { 33263 .none => .none, 33264 else => (try sema.resolveLazyValue(ptr.len.toValue())).toIntern(), 33265 }; 33266 switch (ptr.addr) { 33267 .decl, .mut_decl => return if (resolved_len == ptr.len) 33268 val 33269 else 33270 (try mod.intern(.{ .ptr = .{ 33271 .ty = ptr.ty, 33272 .addr = switch (ptr.addr) { 33273 .decl => |decl| .{ .decl = decl }, 33274 .mut_decl => |mut_decl| .{ .mut_decl = mut_decl }, 33275 else => unreachable, 33276 }, 33277 .len = resolved_len, 33278 } })).toValue(), 33279 .comptime_field => |field_val| { 33280 const resolved_field_val = 33281 (try sema.resolveLazyValue(field_val.toValue())).toIntern(); 33282 return if (resolved_field_val == field_val and resolved_len == ptr.len) 33283 val 33284 else 33285 (try mod.intern(.{ .ptr = .{ 33286 .ty = ptr.ty, 33287 .addr = .{ .comptime_field = resolved_field_val }, 33288 .len = resolved_len, 33289 } })).toValue(); 33290 }, 33291 .int => |int| { 33292 const resolved_int = (try sema.resolveLazyValue(int.toValue())).toIntern(); 33293 return if (resolved_int == int and resolved_len == ptr.len) 33294 val 33295 else 33296 (try mod.intern(.{ .ptr = .{ 33297 .ty = ptr.ty, 33298 .addr = .{ .int = resolved_int }, 33299 .len = resolved_len, 33300 } })).toValue(); 33301 }, 33302 .eu_payload, .opt_payload => |base| { 33303 const resolved_base = (try sema.resolveLazyValue(base.toValue())).toIntern(); 33304 return if (resolved_base == base and resolved_len == ptr.len) 33305 val 33306 else 33307 (try mod.intern(.{ .ptr = .{ 33308 .ty = ptr.ty, 33309 .addr = switch (ptr.addr) { 33310 .eu_payload => .{ .eu_payload = resolved_base }, 33311 .opt_payload => .{ .opt_payload = resolved_base }, 33312 else => unreachable, 33313 }, 33314 .len = ptr.len, 33315 } })).toValue(); 33316 }, 33317 .elem, .field => |base_index| { 33318 const resolved_base = (try sema.resolveLazyValue(base_index.base.toValue())).toIntern(); 33319 return if (resolved_base == base_index.base and resolved_len == ptr.len) 33320 val 33321 else 33322 (try mod.intern(.{ .ptr = .{ 33323 .ty = ptr.ty, 33324 .addr = switch (ptr.addr) { 33325 .elem => .{ .elem = .{ 33326 .base = resolved_base, 33327 .index = base_index.index, 33328 } }, 33329 .field => .{ .field = .{ 33330 .base = resolved_base, 33331 .index = base_index.index, 33332 } }, 33333 else => unreachable, 33334 }, 33335 .len = ptr.len, 33336 } })).toValue(); 33337 }, 33338 } 33339 }, 33340 .aggregate => |aggregate| switch (aggregate.storage) { 33341 .bytes => return val, 33342 .elems => |elems| { 33343 var resolved_elems: []InternPool.Index = &.{}; 33344 for (elems, 0..) |elem, i| { 33345 const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern(); 33346 if (resolved_elems.len == 0 and resolved_elem != elem) { 33347 resolved_elems = try sema.arena.alloc(InternPool.Index, elems.len); 33348 @memcpy(resolved_elems[0..i], elems[0..i]); 33349 } 33350 if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem; 33351 } 33352 return if (resolved_elems.len == 0) val else (try mod.intern(.{ .aggregate = .{ 33353 .ty = aggregate.ty, 33354 .storage = .{ .elems = resolved_elems }, 33355 } })).toValue(); 33356 }, 33357 .repeated_elem => |elem| { 33358 const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern(); 33359 return if (resolved_elem == elem) val else (try mod.intern(.{ .aggregate = .{ 33360 .ty = aggregate.ty, 33361 .storage = .{ .repeated_elem = resolved_elem }, 33362 } })).toValue(); 33363 }, 33364 }, 33365 .un => |un| { 33366 const resolved_tag = (try sema.resolveLazyValue(un.tag.toValue())).toIntern(); 33367 const resolved_val = (try sema.resolveLazyValue(un.val.toValue())).toIntern(); 33368 return if (resolved_tag == un.tag and resolved_val == un.val) 33369 val 33370 else 33371 (try mod.intern(.{ .un = .{ 33372 .ty = un.ty, 33373 .tag = resolved_tag, 33374 .val = resolved_val, 33375 } })).toValue(); 33376 }, 33377 else => return val, 33378 } 33379 } 33380 33381 pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void { 33382 const mod = sema.mod; 33383 switch (ty.zigTypeTag(mod)) { 33384 .Struct => return sema.resolveStructLayout(ty), 33385 .Union => return sema.resolveUnionLayout(ty), 33386 .Array => { 33387 if (ty.arrayLenIncludingSentinel(mod) == 0) return; 33388 const elem_ty = ty.childType(mod); 33389 return sema.resolveTypeLayout(elem_ty); 33390 }, 33391 .Optional => { 33392 const payload_ty = ty.optionalChild(mod); 33393 // In case of querying the ABI alignment of this optional, we will ask 33394 // for hasRuntimeBits() of the payload type, so we need "requires comptime" 33395 // to be known already before this function returns. 33396 _ = try sema.typeRequiresComptime(payload_ty); 33397 return sema.resolveTypeLayout(payload_ty); 33398 }, 33399 .ErrorUnion => { 33400 const payload_ty = ty.errorUnionPayload(mod); 33401 return sema.resolveTypeLayout(payload_ty); 33402 }, 33403 .Fn => { 33404 const info = mod.typeToFunc(ty).?; 33405 if (info.is_generic) { 33406 // Resolving of generic function types is deferred to when 33407 // the function is instantiated. 33408 return; 33409 } 33410 for (info.param_types) |param_ty| { 33411 try sema.resolveTypeLayout(param_ty.toType()); 33412 } 33413 try sema.resolveTypeLayout(info.return_type.toType()); 33414 }, 33415 else => {}, 33416 } 33417 } 33418 33419 fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { 33420 const mod = sema.mod; 33421 const resolved_ty = try sema.resolveTypeFields(ty); 33422 if (mod.typeToStruct(resolved_ty)) |struct_obj| { 33423 switch (struct_obj.status) { 33424 .none, .have_field_types => {}, 33425 .field_types_wip, .layout_wip => { 33426 const msg = try Module.ErrorMsg.create( 33427 sema.gpa, 33428 struct_obj.srcLoc(mod), 33429 "struct '{}' depends on itself", 33430 .{ty.fmt(mod)}, 33431 ); 33432 return sema.failWithOwnedErrorMsg(msg); 33433 }, 33434 .have_layout, .fully_resolved_wip, .fully_resolved => return, 33435 } 33436 const prev_status = struct_obj.status; 33437 errdefer if (struct_obj.status == .layout_wip) { 33438 struct_obj.status = prev_status; 33439 }; 33440 33441 struct_obj.status = .layout_wip; 33442 for (struct_obj.fields.values(), 0..) |field, i| { 33443 sema.resolveTypeLayout(field.ty) catch |err| switch (err) { 33444 error.AnalysisFail => { 33445 const msg = sema.err orelse return err; 33446 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); 33447 return err; 33448 }, 33449 else => return err, 33450 }; 33451 } 33452 33453 if (struct_obj.layout == .Packed) { 33454 try semaBackingIntType(mod, struct_obj); 33455 } 33456 33457 struct_obj.status = .have_layout; 33458 _ = try sema.resolveTypeRequiresComptime(resolved_ty); 33459 33460 if (struct_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) { 33461 const msg = try Module.ErrorMsg.create( 33462 sema.gpa, 33463 struct_obj.srcLoc(mod), 33464 "struct layout depends on it having runtime bits", 33465 .{}, 33466 ); 33467 return sema.failWithOwnedErrorMsg(msg); 33468 } 33469 33470 if (struct_obj.layout == .Auto and mod.backendSupportsFeature(.field_reordering)) { 33471 const optimized_order = try mod.tmp_hack_arena.allocator().alloc(u32, struct_obj.fields.count()); 33472 33473 for (struct_obj.fields.values(), 0..) |field, i| { 33474 optimized_order[i] = if (try sema.typeHasRuntimeBits(field.ty)) 33475 @as(u32, @intCast(i)) 33476 else 33477 Module.Struct.omitted_field; 33478 } 33479 33480 const AlignSortContext = struct { 33481 struct_obj: *Module.Struct, 33482 sema: *Sema, 33483 33484 fn lessThan(ctx: @This(), a: u32, b: u32) bool { 33485 const m = ctx.sema.mod; 33486 if (a == Module.Struct.omitted_field) return false; 33487 if (b == Module.Struct.omitted_field) return true; 33488 return ctx.struct_obj.fields.values()[a].ty.abiAlignment(m) > 33489 ctx.struct_obj.fields.values()[b].ty.abiAlignment(m); 33490 } 33491 }; 33492 mem.sort(u32, optimized_order, AlignSortContext{ 33493 .struct_obj = struct_obj, 33494 .sema = sema, 33495 }, AlignSortContext.lessThan); 33496 struct_obj.optimized_order = optimized_order.ptr; 33497 } 33498 } 33499 // otherwise it's a tuple; no need to resolve anything 33500 } 33501 33502 fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!void { 33503 const gpa = mod.gpa; 33504 33505 var fields_bit_sum: u64 = 0; 33506 for (struct_obj.fields.values()) |field| { 33507 fields_bit_sum += field.ty.bitSize(mod); 33508 } 33509 33510 const decl_index = struct_obj.owner_decl; 33511 const decl = mod.declPtr(decl_index); 33512 33513 const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir; 33514 const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; 33515 assert(extended.opcode == .struct_decl); 33516 const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small)); 33517 33518 if (small.has_backing_int) { 33519 var extra_index: usize = extended.operand; 33520 extra_index += @intFromBool(small.has_src_node); 33521 extra_index += @intFromBool(small.has_fields_len); 33522 extra_index += @intFromBool(small.has_decls_len); 33523 33524 const backing_int_body_len = zir.extra[extra_index]; 33525 extra_index += 1; 33526 33527 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 33528 defer analysis_arena.deinit(); 33529 33530 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); 33531 defer comptime_mutable_decls.deinit(); 33532 33533 var sema: Sema = .{ 33534 .mod = mod, 33535 .gpa = gpa, 33536 .arena = analysis_arena.allocator(), 33537 .code = zir, 33538 .owner_decl = decl, 33539 .owner_decl_index = decl_index, 33540 .func = null, 33541 .func_index = .none, 33542 .fn_ret_ty = Type.void, 33543 .owner_func = null, 33544 .owner_func_index = .none, 33545 .comptime_mutable_decls = &comptime_mutable_decls, 33546 }; 33547 defer sema.deinit(); 33548 33549 var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); 33550 defer wip_captures.deinit(); 33551 33552 var block: Block = .{ 33553 .parent = null, 33554 .sema = &sema, 33555 .src_decl = decl_index, 33556 .namespace = struct_obj.namespace, 33557 .wip_capture_scope = wip_captures.scope, 33558 .instructions = .{}, 33559 .inlining = null, 33560 .is_comptime = true, 33561 }; 33562 defer { 33563 assert(block.instructions.items.len == 0); 33564 block.params.deinit(gpa); 33565 } 33566 33567 const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 }; 33568 const backing_int_ty = blk: { 33569 if (backing_int_body_len == 0) { 33570 const backing_int_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 33571 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref); 33572 } else { 33573 const body = zir.extra[extra_index..][0..backing_int_body_len]; 33574 const ty_ref = try sema.resolveBody(&block, body, struct_obj.zir_index); 33575 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref); 33576 } 33577 }; 33578 33579 try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); 33580 struct_obj.backing_int_ty = backing_int_ty; 33581 try wip_captures.finalize(); 33582 for (comptime_mutable_decls.items) |ct_decl_index| { 33583 const ct_decl = mod.declPtr(ct_decl_index); 33584 try ct_decl.intern(mod); 33585 } 33586 } else { 33587 if (fields_bit_sum > std.math.maxInt(u16)) { 33588 var sema: Sema = .{ 33589 .mod = mod, 33590 .gpa = gpa, 33591 .arena = undefined, 33592 .code = zir, 33593 .owner_decl = decl, 33594 .owner_decl_index = decl_index, 33595 .func = null, 33596 .func_index = .none, 33597 .fn_ret_ty = Type.void, 33598 .owner_func = null, 33599 .owner_func_index = .none, 33600 .comptime_mutable_decls = undefined, 33601 }; 33602 defer sema.deinit(); 33603 33604 var block: Block = .{ 33605 .parent = null, 33606 .sema = &sema, 33607 .src_decl = decl_index, 33608 .namespace = struct_obj.namespace, 33609 .wip_capture_scope = undefined, 33610 .instructions = .{}, 33611 .inlining = null, 33612 .is_comptime = true, 33613 }; 33614 return sema.fail(&block, LazySrcLoc.nodeOffset(0), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum}); 33615 } 33616 struct_obj.backing_int_ty = try mod.intType(.unsigned, @as(u16, @intCast(fields_bit_sum))); 33617 } 33618 } 33619 33620 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { 33621 const mod = sema.mod; 33622 33623 if (!backing_int_ty.isInt(mod)) { 33624 return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)}); 33625 } 33626 if (backing_int_ty.bitSize(mod) != fields_bit_sum) { 33627 return sema.fail( 33628 block, 33629 src, 33630 "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}", 33631 .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(mod), fields_bit_sum }, 33632 ); 33633 } 33634 } 33635 33636 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 33637 const mod = sema.mod; 33638 if (!ty.isIndexable(mod)) { 33639 const msg = msg: { 33640 const msg = try sema.errMsg(block, src, "type '{}' does not support indexing", .{ty.fmt(sema.mod)}); 33641 errdefer msg.destroy(sema.gpa); 33642 try sema.errNote(block, src, msg, "operand must be an array, slice, tuple, or vector", .{}); 33643 break :msg msg; 33644 }; 33645 return sema.failWithOwnedErrorMsg(msg); 33646 } 33647 } 33648 33649 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 33650 const mod = sema.mod; 33651 if (ty.zigTypeTag(mod) == .Pointer) { 33652 switch (ty.ptrSize(mod)) { 33653 .Slice, .Many, .C => return, 33654 .One => { 33655 const elem_ty = ty.childType(mod); 33656 if (elem_ty.zigTypeTag(mod) == .Array) return; 33657 // TODO https://github.com/ziglang/zig/issues/15479 33658 // if (elem_ty.isTuple()) return; 33659 }, 33660 } 33661 } 33662 const msg = msg: { 33663 const msg = try sema.errMsg(block, src, "type '{}' is not an indexable pointer", .{ty.fmt(sema.mod)}); 33664 errdefer msg.destroy(sema.gpa); 33665 try sema.errNote(block, src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); 33666 break :msg msg; 33667 }; 33668 return sema.failWithOwnedErrorMsg(msg); 33669 } 33670 33671 fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { 33672 const mod = sema.mod; 33673 const resolved_ty = try sema.resolveTypeFields(ty); 33674 const union_obj = mod.typeToUnion(resolved_ty).?; 33675 switch (union_obj.status) { 33676 .none, .have_field_types => {}, 33677 .field_types_wip, .layout_wip => { 33678 const msg = try Module.ErrorMsg.create( 33679 sema.gpa, 33680 union_obj.srcLoc(sema.mod), 33681 "union '{}' depends on itself", 33682 .{ty.fmt(sema.mod)}, 33683 ); 33684 return sema.failWithOwnedErrorMsg(msg); 33685 }, 33686 .have_layout, .fully_resolved_wip, .fully_resolved => return, 33687 } 33688 const prev_status = union_obj.status; 33689 errdefer if (union_obj.status == .layout_wip) { 33690 union_obj.status = prev_status; 33691 }; 33692 33693 union_obj.status = .layout_wip; 33694 for (union_obj.fields.values(), 0..) |field, i| { 33695 sema.resolveTypeLayout(field.ty) catch |err| switch (err) { 33696 error.AnalysisFail => { 33697 const msg = sema.err orelse return err; 33698 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); 33699 return err; 33700 }, 33701 else => return err, 33702 }; 33703 } 33704 union_obj.status = .have_layout; 33705 _ = try sema.resolveTypeRequiresComptime(resolved_ty); 33706 33707 if (union_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) { 33708 const msg = try Module.ErrorMsg.create( 33709 sema.gpa, 33710 union_obj.srcLoc(sema.mod), 33711 "union layout depends on it having runtime bits", 33712 .{}, 33713 ); 33714 return sema.failWithOwnedErrorMsg(msg); 33715 } 33716 } 33717 33718 // In case of querying the ABI alignment of this struct, we will ask 33719 // for hasRuntimeBits() of each field, so we need "requires comptime" 33720 // to be known already before this function returns. 33721 pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { 33722 const mod = sema.mod; 33723 33724 return switch (ty.toIntern()) { 33725 .empty_struct_type => false, 33726 else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 33727 .int_type => false, 33728 .ptr_type => |ptr_type| { 33729 const child_ty = ptr_type.child.toType(); 33730 if (child_ty.zigTypeTag(mod) == .Fn) { 33731 return mod.typeToFunc(child_ty).?.is_generic; 33732 } else { 33733 return sema.resolveTypeRequiresComptime(child_ty); 33734 } 33735 }, 33736 .anyframe_type => |child| { 33737 if (child == .none) return false; 33738 return sema.resolveTypeRequiresComptime(child.toType()); 33739 }, 33740 .array_type => |array_type| return sema.resolveTypeRequiresComptime(array_type.child.toType()), 33741 .vector_type => |vector_type| return sema.resolveTypeRequiresComptime(vector_type.child.toType()), 33742 .opt_type => |child| return sema.resolveTypeRequiresComptime(child.toType()), 33743 .error_union_type => |error_union_type| return sema.resolveTypeRequiresComptime(error_union_type.payload_type.toType()), 33744 .error_set_type, .inferred_error_set_type => false, 33745 33746 .func_type => true, 33747 33748 .simple_type => |t| switch (t) { 33749 .f16, 33750 .f32, 33751 .f64, 33752 .f80, 33753 .f128, 33754 .usize, 33755 .isize, 33756 .c_char, 33757 .c_short, 33758 .c_ushort, 33759 .c_int, 33760 .c_uint, 33761 .c_long, 33762 .c_ulong, 33763 .c_longlong, 33764 .c_ulonglong, 33765 .c_longdouble, 33766 .anyopaque, 33767 .bool, 33768 .void, 33769 .anyerror, 33770 .noreturn, 33771 .generic_poison, 33772 .atomic_order, 33773 .atomic_rmw_op, 33774 .calling_convention, 33775 .address_space, 33776 .float_mode, 33777 .reduce_op, 33778 .call_modifier, 33779 .prefetch_options, 33780 .export_options, 33781 .extern_options, 33782 => false, 33783 33784 .type, 33785 .comptime_int, 33786 .comptime_float, 33787 .null, 33788 .undefined, 33789 .enum_literal, 33790 .type_info, 33791 => true, 33792 }, 33793 .struct_type => |struct_type| { 33794 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; 33795 switch (struct_obj.requires_comptime) { 33796 .no, .wip => return false, 33797 .yes => return true, 33798 .unknown => { 33799 var requires_comptime = false; 33800 struct_obj.requires_comptime = .wip; 33801 for (struct_obj.fields.values()) |field| { 33802 if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; 33803 } 33804 if (requires_comptime) { 33805 struct_obj.requires_comptime = .yes; 33806 } else { 33807 struct_obj.requires_comptime = .no; 33808 } 33809 return requires_comptime; 33810 }, 33811 } 33812 }, 33813 33814 .anon_struct_type => |tuple| { 33815 for (tuple.types, tuple.values) |field_ty, field_val| { 33816 const have_comptime_val = field_val != .none; 33817 if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty.toType())) { 33818 return true; 33819 } 33820 } 33821 return false; 33822 }, 33823 33824 .union_type => |union_type| { 33825 const union_obj = mod.unionPtr(union_type.index); 33826 switch (union_obj.requires_comptime) { 33827 .no, .wip => return false, 33828 .yes => return true, 33829 .unknown => { 33830 var requires_comptime = false; 33831 union_obj.requires_comptime = .wip; 33832 for (union_obj.fields.values()) |field| { 33833 if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; 33834 } 33835 if (requires_comptime) { 33836 union_obj.requires_comptime = .yes; 33837 } else { 33838 union_obj.requires_comptime = .no; 33839 } 33840 return requires_comptime; 33841 }, 33842 } 33843 }, 33844 33845 .opaque_type => false, 33846 33847 .enum_type => |enum_type| try sema.resolveTypeRequiresComptime(enum_type.tag_ty.toType()), 33848 33849 // values, not types 33850 .undef, 33851 .runtime_value, 33852 .simple_value, 33853 .variable, 33854 .extern_func, 33855 .func, 33856 .int, 33857 .err, 33858 .error_union, 33859 .enum_literal, 33860 .enum_tag, 33861 .empty_enum_value, 33862 .float, 33863 .ptr, 33864 .opt, 33865 .aggregate, 33866 .un, 33867 // memoization, not types 33868 .memoized_call, 33869 => unreachable, 33870 }, 33871 }; 33872 } 33873 33874 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to 33875 /// be resolved. 33876 pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { 33877 const mod = sema.mod; 33878 switch (ty.zigTypeTag(mod)) { 33879 .Pointer => { 33880 const child_ty = try sema.resolveTypeFields(ty.childType(mod)); 33881 return sema.resolveTypeFully(child_ty); 33882 }, 33883 .Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 33884 .struct_type => return sema.resolveStructFully(ty), 33885 .anon_struct_type => |tuple| { 33886 for (tuple.types) |field_ty| { 33887 try sema.resolveTypeFully(field_ty.toType()); 33888 } 33889 }, 33890 else => {}, 33891 }, 33892 .Union => return sema.resolveUnionFully(ty), 33893 .Array => return sema.resolveTypeFully(ty.childType(mod)), 33894 .Optional => { 33895 return sema.resolveTypeFully(ty.optionalChild(mod)); 33896 }, 33897 .ErrorUnion => return sema.resolveTypeFully(ty.errorUnionPayload(mod)), 33898 .Fn => { 33899 const info = mod.typeToFunc(ty).?; 33900 if (info.is_generic) { 33901 // Resolving of generic function types is deferred to when 33902 // the function is instantiated. 33903 return; 33904 } 33905 for (info.param_types) |param_ty| { 33906 try sema.resolveTypeFully(param_ty.toType()); 33907 } 33908 try sema.resolveTypeFully(info.return_type.toType()); 33909 }, 33910 else => {}, 33911 } 33912 } 33913 33914 fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { 33915 try sema.resolveStructLayout(ty); 33916 33917 const mod = sema.mod; 33918 const resolved_ty = try sema.resolveTypeFields(ty); 33919 const struct_obj = mod.typeToStruct(resolved_ty).?; 33920 33921 switch (struct_obj.status) { 33922 .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, 33923 .fully_resolved_wip, .fully_resolved => return, 33924 } 33925 33926 { 33927 // After we have resolve struct layout we have to go over the fields again to 33928 // make sure pointer fields get their child types resolved as well. 33929 // See also similar code for unions. 33930 const prev_status = struct_obj.status; 33931 errdefer struct_obj.status = prev_status; 33932 33933 struct_obj.status = .fully_resolved_wip; 33934 for (struct_obj.fields.values()) |field| { 33935 try sema.resolveTypeFully(field.ty); 33936 } 33937 struct_obj.status = .fully_resolved; 33938 } 33939 33940 // And let's not forget comptime-only status. 33941 _ = try sema.typeRequiresComptime(ty); 33942 } 33943 33944 fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { 33945 try sema.resolveUnionLayout(ty); 33946 33947 const mod = sema.mod; 33948 const resolved_ty = try sema.resolveTypeFields(ty); 33949 const union_obj = mod.typeToUnion(resolved_ty).?; 33950 switch (union_obj.status) { 33951 .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, 33952 .fully_resolved_wip, .fully_resolved => return, 33953 } 33954 33955 { 33956 // After we have resolve union layout we have to go over the fields again to 33957 // make sure pointer fields get their child types resolved as well. 33958 // See also similar code for structs. 33959 const prev_status = union_obj.status; 33960 errdefer union_obj.status = prev_status; 33961 33962 union_obj.status = .fully_resolved_wip; 33963 for (union_obj.fields.values()) |field| { 33964 try sema.resolveTypeFully(field.ty); 33965 } 33966 union_obj.status = .fully_resolved; 33967 } 33968 33969 // And let's not forget comptime-only status. 33970 _ = try sema.typeRequiresComptime(ty); 33971 } 33972 33973 pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type { 33974 const mod = sema.mod; 33975 33976 switch (ty.toIntern()) { 33977 .var_args_param_type => unreachable, 33978 33979 .none => unreachable, 33980 33981 .u0_type, 33982 .i0_type, 33983 .u1_type, 33984 .u8_type, 33985 .i8_type, 33986 .u16_type, 33987 .i16_type, 33988 .u29_type, 33989 .u32_type, 33990 .i32_type, 33991 .u64_type, 33992 .i64_type, 33993 .u80_type, 33994 .u128_type, 33995 .i128_type, 33996 .usize_type, 33997 .isize_type, 33998 .c_char_type, 33999 .c_short_type, 34000 .c_ushort_type, 34001 .c_int_type, 34002 .c_uint_type, 34003 .c_long_type, 34004 .c_ulong_type, 34005 .c_longlong_type, 34006 .c_ulonglong_type, 34007 .c_longdouble_type, 34008 .f16_type, 34009 .f32_type, 34010 .f64_type, 34011 .f80_type, 34012 .f128_type, 34013 .anyopaque_type, 34014 .bool_type, 34015 .void_type, 34016 .type_type, 34017 .anyerror_type, 34018 .comptime_int_type, 34019 .comptime_float_type, 34020 .noreturn_type, 34021 .anyframe_type, 34022 .null_type, 34023 .undefined_type, 34024 .enum_literal_type, 34025 .manyptr_u8_type, 34026 .manyptr_const_u8_type, 34027 .manyptr_const_u8_sentinel_0_type, 34028 .single_const_pointer_to_comptime_int_type, 34029 .slice_const_u8_type, 34030 .slice_const_u8_sentinel_0_type, 34031 .optional_noreturn_type, 34032 .anyerror_void_error_union_type, 34033 .generic_poison_type, 34034 .empty_struct_type, 34035 => return ty, 34036 34037 .undef => unreachable, 34038 .zero => unreachable, 34039 .zero_usize => unreachable, 34040 .zero_u8 => unreachable, 34041 .one => unreachable, 34042 .one_usize => unreachable, 34043 .one_u8 => unreachable, 34044 .four_u8 => unreachable, 34045 .negative_one => unreachable, 34046 .calling_convention_c => unreachable, 34047 .calling_convention_inline => unreachable, 34048 .void_value => unreachable, 34049 .unreachable_value => unreachable, 34050 .null_value => unreachable, 34051 .bool_true => unreachable, 34052 .bool_false => unreachable, 34053 .empty_struct => unreachable, 34054 .generic_poison => unreachable, 34055 34056 .type_info_type => return sema.getBuiltinType("Type"), 34057 .extern_options_type => return sema.getBuiltinType("ExternOptions"), 34058 .export_options_type => return sema.getBuiltinType("ExportOptions"), 34059 .atomic_order_type => return sema.getBuiltinType("AtomicOrder"), 34060 .atomic_rmw_op_type => return sema.getBuiltinType("AtomicRmwOp"), 34061 .calling_convention_type => return sema.getBuiltinType("CallingConvention"), 34062 .address_space_type => return sema.getBuiltinType("AddressSpace"), 34063 .float_mode_type => return sema.getBuiltinType("FloatMode"), 34064 .reduce_op_type => return sema.getBuiltinType("ReduceOp"), 34065 .call_modifier_type => return sema.getBuiltinType("CallModifier"), 34066 .prefetch_options_type => return sema.getBuiltinType("PrefetchOptions"), 34067 34068 _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) { 34069 .type_struct, 34070 .type_struct_ns, 34071 .type_union_tagged, 34072 .type_union_untagged, 34073 .type_union_safety, 34074 => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 34075 .struct_type => |struct_type| { 34076 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return ty; 34077 try sema.resolveTypeFieldsStruct(ty, struct_obj); 34078 return ty; 34079 }, 34080 .union_type => |union_type| { 34081 const union_obj = mod.unionPtr(union_type.index); 34082 try sema.resolveTypeFieldsUnion(ty, union_obj); 34083 return ty; 34084 }, 34085 else => unreachable, 34086 }, 34087 else => return ty, 34088 }, 34089 } 34090 } 34091 34092 fn resolveTypeFieldsStruct( 34093 sema: *Sema, 34094 ty: Type, 34095 struct_obj: *Module.Struct, 34096 ) CompileError!void { 34097 switch (sema.mod.declPtr(struct_obj.owner_decl).analysis) { 34098 .file_failure, 34099 .dependency_failure, 34100 .sema_failure, 34101 .sema_failure_retryable, 34102 => { 34103 sema.owner_decl.analysis = .dependency_failure; 34104 sema.owner_decl.generation = sema.mod.generation; 34105 return error.AnalysisFail; 34106 }, 34107 else => {}, 34108 } 34109 switch (struct_obj.status) { 34110 .none => {}, 34111 .field_types_wip => { 34112 const msg = try Module.ErrorMsg.create( 34113 sema.gpa, 34114 struct_obj.srcLoc(sema.mod), 34115 "struct '{}' depends on itself", 34116 .{ty.fmt(sema.mod)}, 34117 ); 34118 return sema.failWithOwnedErrorMsg(msg); 34119 }, 34120 .have_field_types, 34121 .have_layout, 34122 .layout_wip, 34123 .fully_resolved_wip, 34124 .fully_resolved, 34125 => return, 34126 } 34127 34128 struct_obj.status = .field_types_wip; 34129 errdefer struct_obj.status = .none; 34130 try semaStructFields(sema.mod, struct_obj); 34131 } 34132 34133 fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void { 34134 switch (sema.mod.declPtr(union_obj.owner_decl).analysis) { 34135 .file_failure, 34136 .dependency_failure, 34137 .sema_failure, 34138 .sema_failure_retryable, 34139 => { 34140 sema.owner_decl.analysis = .dependency_failure; 34141 sema.owner_decl.generation = sema.mod.generation; 34142 return error.AnalysisFail; 34143 }, 34144 else => {}, 34145 } 34146 switch (union_obj.status) { 34147 .none => {}, 34148 .field_types_wip => { 34149 const msg = try Module.ErrorMsg.create( 34150 sema.gpa, 34151 union_obj.srcLoc(sema.mod), 34152 "union '{}' depends on itself", 34153 .{ty.fmt(sema.mod)}, 34154 ); 34155 return sema.failWithOwnedErrorMsg(msg); 34156 }, 34157 .have_field_types, 34158 .have_layout, 34159 .layout_wip, 34160 .fully_resolved_wip, 34161 .fully_resolved, 34162 => return, 34163 } 34164 34165 union_obj.status = .field_types_wip; 34166 errdefer union_obj.status = .none; 34167 try semaUnionFields(sema.mod, union_obj); 34168 union_obj.status = .have_field_types; 34169 } 34170 34171 fn resolveInferredErrorSet( 34172 sema: *Sema, 34173 block: *Block, 34174 src: LazySrcLoc, 34175 ies_index: Module.Fn.InferredErrorSet.Index, 34176 ) CompileError!void { 34177 const mod = sema.mod; 34178 const ies = mod.inferredErrorSetPtr(ies_index); 34179 34180 if (ies.is_resolved) return; 34181 34182 const func = mod.funcPtr(ies.func); 34183 if (func.state == .in_progress) { 34184 return sema.fail(block, src, "unable to resolve inferred error set", .{}); 34185 } 34186 34187 // In order to ensure that all dependencies are properly added to the set, we 34188 // need to ensure the function body is analyzed of the inferred error set. 34189 // However, in the case of comptime/inline function calls with inferred error sets, 34190 // each call gets a new InferredErrorSet object, which contains the same 34191 // `Module.Fn.Index`. Not only is the function not relevant to the inferred error set 34192 // in this case, it may be a generic function which would cause an assertion failure 34193 // if we called `ensureFuncBodyAnalyzed` on it here. 34194 const ies_func_owner_decl = mod.declPtr(func.owner_decl); 34195 const ies_func_info = mod.typeToFunc(ies_func_owner_decl.ty).?; 34196 // if ies declared by a inline function with generic return type, the return_type should be generic_poison, 34197 // because inline function does not create a new declaration, and the ies has been filled with analyzeCall, 34198 // so here we can simply skip this case. 34199 if (ies_func_info.return_type == .generic_poison_type) { 34200 assert(ies_func_info.cc == .Inline); 34201 } else if (mod.typeToInferredErrorSet(ies_func_info.return_type.toType().errorUnionSet(mod)).? == ies) { 34202 if (ies_func_info.is_generic) { 34203 const msg = msg: { 34204 const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{}); 34205 errdefer msg.destroy(sema.gpa); 34206 34207 try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(mod), msg, "generic function declared here", .{}); 34208 break :msg msg; 34209 }; 34210 return sema.failWithOwnedErrorMsg(msg); 34211 } 34212 // In this case we are dealing with the actual InferredErrorSet object that 34213 // corresponds to the function, not one created to track an inline/comptime call. 34214 try sema.ensureFuncBodyAnalyzed(ies.func); 34215 } 34216 34217 ies.is_resolved = true; 34218 34219 for (ies.inferred_error_sets.keys()) |other_ies_index| { 34220 if (ies_index == other_ies_index) continue; 34221 try sema.resolveInferredErrorSet(block, src, other_ies_index); 34222 34223 const other_ies = mod.inferredErrorSetPtr(other_ies_index); 34224 for (other_ies.errors.keys()) |key| { 34225 try ies.errors.put(sema.gpa, key, {}); 34226 } 34227 if (other_ies.is_anyerror) 34228 ies.is_anyerror = true; 34229 } 34230 } 34231 34232 fn resolveInferredErrorSetTy( 34233 sema: *Sema, 34234 block: *Block, 34235 src: LazySrcLoc, 34236 ty: Type, 34237 ) CompileError!void { 34238 const mod = sema.mod; 34239 if (mod.typeToInferredErrorSetIndex(ty).unwrap()) |ies_index| { 34240 try sema.resolveInferredErrorSet(block, src, ies_index); 34241 } 34242 } 34243 34244 fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void { 34245 const gpa = mod.gpa; 34246 const ip = &mod.intern_pool; 34247 const decl_index = struct_obj.owner_decl; 34248 const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir; 34249 const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; 34250 assert(extended.opcode == .struct_decl); 34251 const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small)); 34252 var extra_index: usize = extended.operand; 34253 34254 const src = LazySrcLoc.nodeOffset(0); 34255 extra_index += @intFromBool(small.has_src_node); 34256 34257 const fields_len = if (small.has_fields_len) blk: { 34258 const fields_len = zir.extra[extra_index]; 34259 extra_index += 1; 34260 break :blk fields_len; 34261 } else 0; 34262 34263 const decls_len = if (small.has_decls_len) decls_len: { 34264 const decls_len = zir.extra[extra_index]; 34265 extra_index += 1; 34266 break :decls_len decls_len; 34267 } else 0; 34268 34269 // The backing integer cannot be handled until `resolveStructLayout()`. 34270 if (small.has_backing_int) { 34271 const backing_int_body_len = zir.extra[extra_index]; 34272 extra_index += 1; // backing_int_body_len 34273 if (backing_int_body_len == 0) { 34274 extra_index += 1; // backing_int_ref 34275 } else { 34276 extra_index += backing_int_body_len; // backing_int_body_inst 34277 } 34278 } 34279 34280 // Skip over decls. 34281 var decls_it = zir.declIteratorInner(extra_index, decls_len); 34282 while (decls_it.next()) |_| {} 34283 extra_index = decls_it.extra_index; 34284 34285 if (fields_len == 0) { 34286 if (struct_obj.layout == .Packed) { 34287 try semaBackingIntType(mod, struct_obj); 34288 } 34289 struct_obj.status = .have_layout; 34290 return; 34291 } 34292 34293 const decl = mod.declPtr(decl_index); 34294 34295 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 34296 defer analysis_arena.deinit(); 34297 34298 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); 34299 defer comptime_mutable_decls.deinit(); 34300 34301 var sema: Sema = .{ 34302 .mod = mod, 34303 .gpa = gpa, 34304 .arena = analysis_arena.allocator(), 34305 .code = zir, 34306 .owner_decl = decl, 34307 .owner_decl_index = decl_index, 34308 .func = null, 34309 .func_index = .none, 34310 .fn_ret_ty = Type.void, 34311 .owner_func = null, 34312 .owner_func_index = .none, 34313 .comptime_mutable_decls = &comptime_mutable_decls, 34314 }; 34315 defer sema.deinit(); 34316 34317 var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); 34318 defer wip_captures.deinit(); 34319 34320 var block_scope: Block = .{ 34321 .parent = null, 34322 .sema = &sema, 34323 .src_decl = decl_index, 34324 .namespace = struct_obj.namespace, 34325 .wip_capture_scope = wip_captures.scope, 34326 .instructions = .{}, 34327 .inlining = null, 34328 .is_comptime = true, 34329 }; 34330 defer { 34331 assert(block_scope.instructions.items.len == 0); 34332 block_scope.params.deinit(gpa); 34333 } 34334 34335 struct_obj.fields = .{}; 34336 try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); 34337 34338 const Field = struct { 34339 type_body_len: u32 = 0, 34340 align_body_len: u32 = 0, 34341 init_body_len: u32 = 0, 34342 type_ref: Zir.Inst.Ref = .none, 34343 }; 34344 const fields = try sema.arena.alloc(Field, fields_len); 34345 var any_inits = false; 34346 34347 { 34348 const bits_per_field = 4; 34349 const fields_per_u32 = 32 / bits_per_field; 34350 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 34351 const flags_index = extra_index; 34352 var bit_bag_index: usize = flags_index; 34353 extra_index += bit_bags_count; 34354 var cur_bit_bag: u32 = undefined; 34355 var field_i: u32 = 0; 34356 while (field_i < fields_len) : (field_i += 1) { 34357 if (field_i % fields_per_u32 == 0) { 34358 cur_bit_bag = zir.extra[bit_bag_index]; 34359 bit_bag_index += 1; 34360 } 34361 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 34362 cur_bit_bag >>= 1; 34363 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; 34364 cur_bit_bag >>= 1; 34365 const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0; 34366 cur_bit_bag >>= 1; 34367 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; 34368 cur_bit_bag >>= 1; 34369 34370 var field_name_zir: ?[:0]const u8 = null; 34371 if (!small.is_tuple) { 34372 field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); 34373 extra_index += 1; 34374 } 34375 extra_index += 1; // doc_comment 34376 34377 fields[field_i] = .{}; 34378 34379 if (has_type_body) { 34380 fields[field_i].type_body_len = zir.extra[extra_index]; 34381 } else { 34382 fields[field_i].type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34383 } 34384 extra_index += 1; 34385 34386 // This string needs to outlive the ZIR code. 34387 const field_name = try ip.getOrPutString(gpa, if (field_name_zir) |s| 34388 s 34389 else 34390 try std.fmt.allocPrint(sema.arena, "{d}", .{field_i})); 34391 34392 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); 34393 if (gop.found_existing) { 34394 const msg = msg: { 34395 const field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i }).lazy; 34396 const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{}'", .{field_name.fmt(ip)}); 34397 errdefer msg.destroy(gpa); 34398 34399 const prev_field_index = struct_obj.fields.getIndex(field_name).?; 34400 const prev_field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = prev_field_index }); 34401 try mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{}); 34402 try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); 34403 break :msg msg; 34404 }; 34405 return sema.failWithOwnedErrorMsg(msg); 34406 } 34407 gop.value_ptr.* = .{ 34408 .ty = Type.noreturn, 34409 .abi_align = .none, 34410 .default_val = .none, 34411 .is_comptime = is_comptime, 34412 .offset = undefined, 34413 }; 34414 34415 if (has_align) { 34416 fields[field_i].align_body_len = zir.extra[extra_index]; 34417 extra_index += 1; 34418 } 34419 if (has_init) { 34420 fields[field_i].init_body_len = zir.extra[extra_index]; 34421 extra_index += 1; 34422 any_inits = true; 34423 } 34424 } 34425 } 34426 34427 // Next we do only types and alignments, saving the inits for a second pass, 34428 // so that init values may depend on type layout. 34429 const bodies_index = extra_index; 34430 34431 for (fields, 0..) |zir_field, field_i| { 34432 const field_ty: Type = ty: { 34433 if (zir_field.type_ref != .none) { 34434 break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) { 34435 error.NeededSourceLocation => { 34436 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34437 .index = field_i, 34438 .range = .type, 34439 }).lazy; 34440 _ = try sema.resolveType(&block_scope, ty_src, zir_field.type_ref); 34441 unreachable; 34442 }, 34443 else => |e| return e, 34444 }; 34445 } 34446 assert(zir_field.type_body_len != 0); 34447 const body = zir.extra[extra_index..][0..zir_field.type_body_len]; 34448 extra_index += body.len; 34449 const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); 34450 break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) { 34451 error.NeededSourceLocation => { 34452 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34453 .index = field_i, 34454 .range = .type, 34455 }).lazy; 34456 _ = try sema.analyzeAsType(&block_scope, ty_src, ty_ref); 34457 unreachable; 34458 }, 34459 else => |e| return e, 34460 }; 34461 }; 34462 if (field_ty.isGenericPoison()) { 34463 return error.GenericPoison; 34464 } 34465 34466 const field = &struct_obj.fields.values()[field_i]; 34467 field.ty = field_ty; 34468 34469 if (field_ty.zigTypeTag(mod) == .Opaque) { 34470 const msg = msg: { 34471 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34472 .index = field_i, 34473 .range = .type, 34474 }).lazy; 34475 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 34476 errdefer msg.destroy(sema.gpa); 34477 34478 try sema.addDeclaredHereNote(msg, field_ty); 34479 break :msg msg; 34480 }; 34481 return sema.failWithOwnedErrorMsg(msg); 34482 } 34483 if (field_ty.zigTypeTag(mod) == .NoReturn) { 34484 const msg = msg: { 34485 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34486 .index = field_i, 34487 .range = .type, 34488 }).lazy; 34489 const msg = try sema.errMsg(&block_scope, ty_src, "struct fields cannot be 'noreturn'", .{}); 34490 errdefer msg.destroy(sema.gpa); 34491 34492 try sema.addDeclaredHereNote(msg, field_ty); 34493 break :msg msg; 34494 }; 34495 return sema.failWithOwnedErrorMsg(msg); 34496 } 34497 if (struct_obj.layout == .Extern and !try sema.validateExternType(field.ty, .struct_field)) { 34498 const msg = msg: { 34499 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34500 .index = field_i, 34501 .range = .type, 34502 }); 34503 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)}); 34504 errdefer msg.destroy(sema.gpa); 34505 34506 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field.ty, .struct_field); 34507 34508 try sema.addDeclaredHereNote(msg, field.ty); 34509 break :msg msg; 34510 }; 34511 return sema.failWithOwnedErrorMsg(msg); 34512 } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty, mod))) { 34513 const msg = msg: { 34514 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34515 .index = field_i, 34516 .range = .type, 34517 }); 34518 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)}); 34519 errdefer msg.destroy(sema.gpa); 34520 34521 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field.ty); 34522 34523 try sema.addDeclaredHereNote(msg, field.ty); 34524 break :msg msg; 34525 }; 34526 return sema.failWithOwnedErrorMsg(msg); 34527 } 34528 34529 if (zir_field.align_body_len > 0) { 34530 const body = zir.extra[extra_index..][0..zir_field.align_body_len]; 34531 extra_index += body.len; 34532 const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); 34533 field.abi_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { 34534 error.NeededSourceLocation => { 34535 const align_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34536 .index = field_i, 34537 .range = .alignment, 34538 }).lazy; 34539 _ = try sema.analyzeAsAlign(&block_scope, align_src, align_ref); 34540 unreachable; 34541 }, 34542 else => |e| return e, 34543 }; 34544 } 34545 34546 extra_index += zir_field.init_body_len; 34547 } 34548 34549 struct_obj.status = .have_field_types; 34550 34551 if (any_inits) { 34552 extra_index = bodies_index; 34553 for (fields, 0..) |zir_field, field_i| { 34554 extra_index += zir_field.type_body_len; 34555 extra_index += zir_field.align_body_len; 34556 if (zir_field.init_body_len > 0) { 34557 const body = zir.extra[extra_index..][0..zir_field.init_body_len]; 34558 extra_index += body.len; 34559 const init = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); 34560 const field = &struct_obj.fields.values()[field_i]; 34561 const coerced = sema.coerce(&block_scope, field.ty, init, .unneeded) catch |err| switch (err) { 34562 error.NeededSourceLocation => { 34563 const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34564 .index = field_i, 34565 .range = .value, 34566 }).lazy; 34567 _ = try sema.coerce(&block_scope, field.ty, init, init_src); 34568 unreachable; 34569 }, 34570 else => |e| return e, 34571 }; 34572 const default_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { 34573 const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34574 .index = field_i, 34575 .range = .value, 34576 }).lazy; 34577 return sema.failWithNeededComptime(&block_scope, init_src, "struct field default value must be comptime-known"); 34578 }; 34579 field.default_val = try default_val.intern(field.ty, mod); 34580 } 34581 } 34582 } 34583 try wip_captures.finalize(); 34584 for (comptime_mutable_decls.items) |ct_decl_index| { 34585 const ct_decl = mod.declPtr(ct_decl_index); 34586 try ct_decl.intern(mod); 34587 } 34588 34589 struct_obj.have_field_inits = true; 34590 } 34591 34592 fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { 34593 const tracy = trace(@src()); 34594 defer tracy.end(); 34595 34596 const gpa = mod.gpa; 34597 const ip = &mod.intern_pool; 34598 const decl_index = union_obj.owner_decl; 34599 const zir = mod.namespacePtr(union_obj.namespace).file_scope.zir; 34600 const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; 34601 assert(extended.opcode == .union_decl); 34602 const small = @as(Zir.Inst.UnionDecl.Small, @bitCast(extended.small)); 34603 var extra_index: usize = extended.operand; 34604 34605 const src = LazySrcLoc.nodeOffset(0); 34606 extra_index += @intFromBool(small.has_src_node); 34607 34608 const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { 34609 const ty_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34610 extra_index += 1; 34611 break :blk ty_ref; 34612 } else .none; 34613 34614 const body_len = if (small.has_body_len) blk: { 34615 const body_len = zir.extra[extra_index]; 34616 extra_index += 1; 34617 break :blk body_len; 34618 } else 0; 34619 34620 const fields_len = if (small.has_fields_len) blk: { 34621 const fields_len = zir.extra[extra_index]; 34622 extra_index += 1; 34623 break :blk fields_len; 34624 } else 0; 34625 34626 const decls_len = if (small.has_decls_len) decls_len: { 34627 const decls_len = zir.extra[extra_index]; 34628 extra_index += 1; 34629 break :decls_len decls_len; 34630 } else 0; 34631 34632 // Skip over decls. 34633 var decls_it = zir.declIteratorInner(extra_index, decls_len); 34634 while (decls_it.next()) |_| {} 34635 extra_index = decls_it.extra_index; 34636 34637 const body = zir.extra[extra_index..][0..body_len]; 34638 extra_index += body.len; 34639 34640 const decl = mod.declPtr(decl_index); 34641 34642 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 34643 defer analysis_arena.deinit(); 34644 34645 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); 34646 defer comptime_mutable_decls.deinit(); 34647 34648 var sema: Sema = .{ 34649 .mod = mod, 34650 .gpa = gpa, 34651 .arena = analysis_arena.allocator(), 34652 .code = zir, 34653 .owner_decl = decl, 34654 .owner_decl_index = decl_index, 34655 .func = null, 34656 .func_index = .none, 34657 .fn_ret_ty = Type.void, 34658 .owner_func = null, 34659 .owner_func_index = .none, 34660 .comptime_mutable_decls = &comptime_mutable_decls, 34661 }; 34662 defer sema.deinit(); 34663 34664 var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); 34665 defer wip_captures.deinit(); 34666 34667 var block_scope: Block = .{ 34668 .parent = null, 34669 .sema = &sema, 34670 .src_decl = decl_index, 34671 .namespace = union_obj.namespace, 34672 .wip_capture_scope = wip_captures.scope, 34673 .instructions = .{}, 34674 .inlining = null, 34675 .is_comptime = true, 34676 }; 34677 defer { 34678 assert(block_scope.instructions.items.len == 0); 34679 block_scope.params.deinit(gpa); 34680 } 34681 34682 if (body.len != 0) { 34683 try sema.analyzeBody(&block_scope, body); 34684 } 34685 34686 try wip_captures.finalize(); 34687 for (comptime_mutable_decls.items) |ct_decl_index| { 34688 const ct_decl = mod.declPtr(ct_decl_index); 34689 try ct_decl.intern(mod); 34690 } 34691 34692 try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); 34693 34694 var int_tag_ty: Type = undefined; 34695 var enum_field_names: []InternPool.NullTerminatedString = &.{}; 34696 var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}; 34697 var explicit_tags_seen: []bool = &.{}; 34698 if (tag_type_ref != .none) { 34699 const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; 34700 const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); 34701 if (small.auto_enum_tag) { 34702 // The provided type is an integer type and we must construct the enum tag type here. 34703 int_tag_ty = provided_ty; 34704 if (int_tag_ty.zigTypeTag(mod) != .Int and int_tag_ty.zigTypeTag(mod) != .ComptimeInt) { 34705 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(mod)}); 34706 } 34707 34708 if (fields_len > 0) { 34709 const field_count_val = try mod.intValue(Type.comptime_int, fields_len - 1); 34710 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) { 34711 const msg = msg: { 34712 const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{}); 34713 errdefer msg.destroy(sema.gpa); 34714 try sema.errNote(&block_scope, tag_ty_src, msg, "type '{}' cannot fit values in range 0...{d}", .{ 34715 int_tag_ty.fmt(mod), 34716 fields_len - 1, 34717 }); 34718 break :msg msg; 34719 }; 34720 return sema.failWithOwnedErrorMsg(msg); 34721 } 34722 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 34723 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len); 34724 } 34725 } else { 34726 // The provided type is the enum tag type. 34727 union_obj.tag_ty = provided_ty; 34728 const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) { 34729 .enum_type => |x| x, 34730 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(mod)}), 34731 }; 34732 // The fields of the union must match the enum exactly. 34733 // A flag per field is used to check for missing and extraneous fields. 34734 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); 34735 @memset(explicit_tags_seen, false); 34736 } 34737 } else { 34738 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis 34739 // purposes, we still auto-generate an enum tag type the same way. That the union is 34740 // untagged is represented by the Type tag (union vs union_tagged). 34741 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 34742 } 34743 34744 const bits_per_field = 4; 34745 const fields_per_u32 = 32 / bits_per_field; 34746 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 34747 var bit_bag_index: usize = extra_index; 34748 extra_index += bit_bags_count; 34749 var cur_bit_bag: u32 = undefined; 34750 var field_i: u32 = 0; 34751 var last_tag_val: ?Value = null; 34752 while (field_i < fields_len) : (field_i += 1) { 34753 if (field_i % fields_per_u32 == 0) { 34754 cur_bit_bag = zir.extra[bit_bag_index]; 34755 bit_bag_index += 1; 34756 } 34757 const has_type = @as(u1, @truncate(cur_bit_bag)) != 0; 34758 cur_bit_bag >>= 1; 34759 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 34760 cur_bit_bag >>= 1; 34761 const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0; 34762 cur_bit_bag >>= 1; 34763 const unused = @as(u1, @truncate(cur_bit_bag)) != 0; 34764 cur_bit_bag >>= 1; 34765 _ = unused; 34766 34767 const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); 34768 extra_index += 1; 34769 34770 // doc_comment 34771 extra_index += 1; 34772 34773 const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { 34774 const field_type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34775 extra_index += 1; 34776 break :blk field_type_ref; 34777 } else .none; 34778 34779 const align_ref: Zir.Inst.Ref = if (has_align) blk: { 34780 const align_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34781 extra_index += 1; 34782 break :blk align_ref; 34783 } else .none; 34784 34785 const tag_ref: Air.Inst.Ref = if (has_tag) blk: { 34786 const tag_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34787 extra_index += 1; 34788 break :blk try sema.resolveInst(tag_ref); 34789 } else .none; 34790 34791 if (enum_field_vals.capacity() > 0) { 34792 const enum_tag_val = if (tag_ref != .none) blk: { 34793 const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) { 34794 error.NeededSourceLocation => { 34795 const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34796 .index = field_i, 34797 .range = .value, 34798 }).lazy; 34799 _ = try sema.semaUnionFieldVal(&block_scope, val_src, int_tag_ty, tag_ref); 34800 unreachable; 34801 }, 34802 else => |e| return e, 34803 }; 34804 last_tag_val = val; 34805 34806 break :blk val; 34807 } else blk: { 34808 const val = if (last_tag_val) |val| 34809 try sema.intAdd(val, Value.one_comptime_int, int_tag_ty, undefined) 34810 else 34811 try mod.intValue(int_tag_ty, 0); 34812 last_tag_val = val; 34813 34814 break :blk val; 34815 }; 34816 const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern()); 34817 if (gop.found_existing) { 34818 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; 34819 const other_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = gop.index }).lazy; 34820 const msg = msg: { 34821 const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)}); 34822 errdefer msg.destroy(gpa); 34823 try sema.errNote(&block_scope, other_field_src, msg, "other occurrence here", .{}); 34824 break :msg msg; 34825 }; 34826 return sema.failWithOwnedErrorMsg(msg); 34827 } 34828 } 34829 34830 // This string needs to outlive the ZIR code. 34831 const field_name = try ip.getOrPutString(gpa, field_name_zir); 34832 if (enum_field_names.len != 0) { 34833 enum_field_names[field_i] = field_name; 34834 } 34835 34836 const field_ty: Type = if (!has_type) 34837 Type.void 34838 else if (field_type_ref == .none) 34839 Type.noreturn 34840 else 34841 sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) { 34842 error.NeededSourceLocation => { 34843 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34844 .index = field_i, 34845 .range = .type, 34846 }).lazy; 34847 _ = try sema.resolveType(&block_scope, ty_src, field_type_ref); 34848 unreachable; 34849 }, 34850 else => |e| return e, 34851 }; 34852 34853 if (field_ty.isGenericPoison()) { 34854 return error.GenericPoison; 34855 } 34856 34857 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); 34858 if (gop.found_existing) { 34859 const msg = msg: { 34860 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; 34861 const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{}'", .{ 34862 field_name.fmt(ip), 34863 }); 34864 errdefer msg.destroy(gpa); 34865 34866 const prev_field_index = union_obj.fields.getIndex(field_name).?; 34867 const prev_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = prev_field_index }).lazy; 34868 try mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{}); 34869 try sema.errNote(&block_scope, src, msg, "union declared here", .{}); 34870 break :msg msg; 34871 }; 34872 return sema.failWithOwnedErrorMsg(msg); 34873 } 34874 34875 if (explicit_tags_seen.len > 0) { 34876 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; 34877 const enum_index = tag_info.nameIndex(ip, field_name) orelse { 34878 const msg = msg: { 34879 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34880 .index = field_i, 34881 .range = .type, 34882 }).lazy; 34883 const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{}' in enum '{}'", .{ 34884 field_name.fmt(ip), union_obj.tag_ty.fmt(mod), 34885 }); 34886 errdefer msg.destroy(sema.gpa); 34887 try sema.addDeclaredHereNote(msg, union_obj.tag_ty); 34888 break :msg msg; 34889 }; 34890 return sema.failWithOwnedErrorMsg(msg); 34891 }; 34892 // No check for duplicate because the check already happened in order 34893 // to create the enum type in the first place. 34894 assert(!explicit_tags_seen[enum_index]); 34895 explicit_tags_seen[enum_index] = true; 34896 } 34897 34898 if (field_ty.zigTypeTag(mod) == .Opaque) { 34899 const msg = msg: { 34900 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34901 .index = field_i, 34902 .range = .type, 34903 }).lazy; 34904 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 34905 errdefer msg.destroy(sema.gpa); 34906 34907 try sema.addDeclaredHereNote(msg, field_ty); 34908 break :msg msg; 34909 }; 34910 return sema.failWithOwnedErrorMsg(msg); 34911 } 34912 if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { 34913 const msg = msg: { 34914 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34915 .index = field_i, 34916 .range = .type, 34917 }); 34918 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 34919 errdefer msg.destroy(sema.gpa); 34920 34921 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .union_field); 34922 34923 try sema.addDeclaredHereNote(msg, field_ty); 34924 break :msg msg; 34925 }; 34926 return sema.failWithOwnedErrorMsg(msg); 34927 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { 34928 const msg = msg: { 34929 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34930 .index = field_i, 34931 .range = .type, 34932 }); 34933 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 34934 errdefer msg.destroy(sema.gpa); 34935 34936 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty); 34937 34938 try sema.addDeclaredHereNote(msg, field_ty); 34939 break :msg msg; 34940 }; 34941 return sema.failWithOwnedErrorMsg(msg); 34942 } 34943 34944 gop.value_ptr.* = .{ 34945 .ty = field_ty, 34946 .abi_align = .none, 34947 }; 34948 34949 if (align_ref != .none) { 34950 gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { 34951 error.NeededSourceLocation => { 34952 const align_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34953 .index = field_i, 34954 .range = .alignment, 34955 }).lazy; 34956 _ = try sema.resolveAlign(&block_scope, align_src, align_ref); 34957 unreachable; 34958 }, 34959 else => |e| return e, 34960 }; 34961 } else { 34962 gop.value_ptr.abi_align = .none; 34963 } 34964 } 34965 34966 if (explicit_tags_seen.len > 0) { 34967 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; 34968 if (tag_info.names.len > fields_len) { 34969 const msg = msg: { 34970 const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); 34971 errdefer msg.destroy(sema.gpa); 34972 34973 const enum_ty = union_obj.tag_ty; 34974 for (tag_info.names, 0..) |field_name, field_index| { 34975 if (explicit_tags_seen[field_index]) continue; 34976 try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{ 34977 field_name.fmt(ip), 34978 }); 34979 } 34980 try sema.addDeclaredHereNote(msg, union_obj.tag_ty); 34981 break :msg msg; 34982 }; 34983 return sema.failWithOwnedErrorMsg(msg); 34984 } 34985 } else if (enum_field_vals.count() > 0) { 34986 union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_obj); 34987 } else { 34988 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_obj); 34989 } 34990 } 34991 34992 fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Type, tag_ref: Air.Inst.Ref) CompileError!Value { 34993 const coerced = try sema.coerce(block, int_tag_ty, tag_ref, src); 34994 return sema.resolveConstValue(block, src, coerced, "enum tag value must be comptime-known"); 34995 } 34996 34997 fn generateUnionTagTypeNumbered( 34998 sema: *Sema, 34999 block: *Block, 35000 enum_field_names: []const InternPool.NullTerminatedString, 35001 enum_field_vals: []const InternPool.Index, 35002 union_obj: *Module.Union, 35003 ) !Type { 35004 const mod = sema.mod; 35005 const gpa = sema.gpa; 35006 35007 const src_decl = mod.declPtr(block.src_decl); 35008 const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); 35009 errdefer mod.destroyDecl(new_decl_index); 35010 const fqn = try union_obj.getFullyQualifiedName(mod); 35011 const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)}); 35012 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ 35013 .ty = Type.noreturn, 35014 .val = Value.@"unreachable", 35015 }, name); 35016 errdefer mod.abortAnonDecl(new_decl_index); 35017 35018 const new_decl = mod.declPtr(new_decl_index); 35019 new_decl.name_fully_qualified = true; 35020 new_decl.owns_tv = true; 35021 new_decl.name_fully_qualified = true; 35022 35023 const enum_ty = try mod.intern(.{ .enum_type = .{ 35024 .decl = new_decl_index, 35025 .namespace = .none, 35026 .tag_ty = if (enum_field_vals.len == 0) 35027 (try mod.intType(.unsigned, 0)).toIntern() 35028 else 35029 mod.intern_pool.typeOf(enum_field_vals[0]), 35030 .names = enum_field_names, 35031 .values = enum_field_vals, 35032 .tag_mode = .explicit, 35033 } }); 35034 35035 new_decl.ty = Type.type; 35036 new_decl.val = enum_ty.toValue(); 35037 35038 try mod.finalizeAnonDecl(new_decl_index); 35039 return enum_ty.toType(); 35040 } 35041 35042 fn generateUnionTagTypeSimple( 35043 sema: *Sema, 35044 block: *Block, 35045 enum_field_names: []const InternPool.NullTerminatedString, 35046 maybe_union_obj: ?*Module.Union, 35047 ) !Type { 35048 const mod = sema.mod; 35049 const gpa = sema.gpa; 35050 35051 const new_decl_index = new_decl_index: { 35052 const union_obj = maybe_union_obj orelse { 35053 break :new_decl_index try mod.createAnonymousDecl(block, .{ 35054 .ty = Type.noreturn, 35055 .val = Value.@"unreachable", 35056 }); 35057 }; 35058 const src_decl = mod.declPtr(block.src_decl); 35059 const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); 35060 errdefer mod.destroyDecl(new_decl_index); 35061 const fqn = try union_obj.getFullyQualifiedName(mod); 35062 const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)}); 35063 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ 35064 .ty = Type.noreturn, 35065 .val = Value.@"unreachable", 35066 }, name); 35067 mod.declPtr(new_decl_index).name_fully_qualified = true; 35068 break :new_decl_index new_decl_index; 35069 }; 35070 errdefer mod.abortAnonDecl(new_decl_index); 35071 35072 const enum_ty = try mod.intern(.{ .enum_type = .{ 35073 .decl = new_decl_index, 35074 .namespace = .none, 35075 .tag_ty = if (enum_field_names.len == 0) 35076 (try mod.intType(.unsigned, 0)).toIntern() 35077 else 35078 (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(), 35079 .names = enum_field_names, 35080 .values = &.{}, 35081 .tag_mode = .auto, 35082 } }); 35083 35084 const new_decl = mod.declPtr(new_decl_index); 35085 new_decl.owns_tv = true; 35086 new_decl.ty = Type.type; 35087 new_decl.val = enum_ty.toValue(); 35088 35089 try mod.finalizeAnonDecl(new_decl_index); 35090 return enum_ty.toType(); 35091 } 35092 35093 fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { 35094 const gpa = sema.gpa; 35095 const src = LazySrcLoc.nodeOffset(0); 35096 35097 var wip_captures = try WipCaptureScope.init(gpa, sema.owner_decl.src_scope); 35098 defer wip_captures.deinit(); 35099 35100 var block: Block = .{ 35101 .parent = null, 35102 .sema = sema, 35103 .src_decl = sema.owner_decl_index, 35104 .namespace = sema.owner_decl.src_namespace, 35105 .wip_capture_scope = wip_captures.scope, 35106 .instructions = .{}, 35107 .inlining = null, 35108 .is_comptime = true, 35109 }; 35110 defer { 35111 block.instructions.deinit(gpa); 35112 block.params.deinit(gpa); 35113 } 35114 35115 const decl_index = try getBuiltinDecl(sema, &block, name); 35116 return sema.analyzeDeclVal(&block, src, decl_index); 35117 } 35118 35119 fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Module.Decl.Index { 35120 const gpa = sema.gpa; 35121 35122 const src = LazySrcLoc.nodeOffset(0); 35123 35124 const mod = sema.mod; 35125 const ip = &mod.intern_pool; 35126 const std_pkg = mod.main_pkg.table.get("std").?; 35127 const std_file = (mod.importPkg(std_pkg) catch unreachable).file; 35128 const opt_builtin_inst = (try sema.namespaceLookupRef( 35129 block, 35130 src, 35131 mod.declPtr(std_file.root_decl.unwrap().?).src_namespace, 35132 try ip.getOrPutString(gpa, "builtin"), 35133 )) orelse @panic("lib/std.zig is corrupt and missing 'builtin'"); 35134 const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst, src); 35135 const builtin_ty = sema.analyzeAsType(block, src, builtin_inst) catch |err| switch (err) { 35136 error.AnalysisFail => std.debug.panic("std.builtin is corrupt", .{}), 35137 else => |e| return e, 35138 }; 35139 const decl_index = (try sema.namespaceLookup( 35140 block, 35141 src, 35142 builtin_ty.getNamespaceIndex(mod).unwrap().?, 35143 try ip.getOrPutString(gpa, name), 35144 )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name}); 35145 return decl_index; 35146 } 35147 35148 fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type { 35149 const ty_inst = try sema.getBuiltin(name); 35150 35151 var wip_captures = try WipCaptureScope.init(sema.gpa, sema.owner_decl.src_scope); 35152 defer wip_captures.deinit(); 35153 35154 var block: Block = .{ 35155 .parent = null, 35156 .sema = sema, 35157 .src_decl = sema.owner_decl_index, 35158 .namespace = sema.owner_decl.src_namespace, 35159 .wip_capture_scope = wip_captures.scope, 35160 .instructions = .{}, 35161 .inlining = null, 35162 .is_comptime = true, 35163 }; 35164 defer { 35165 block.instructions.deinit(sema.gpa); 35166 block.params.deinit(sema.gpa); 35167 } 35168 const src = LazySrcLoc.nodeOffset(0); 35169 35170 const result_ty = sema.analyzeAsType(&block, src, ty_inst) catch |err| switch (err) { 35171 error.AnalysisFail => std.debug.panic("std.builtin.{s} is corrupt", .{name}), 35172 else => |e| return e, 35173 }; 35174 try sema.resolveTypeFully(result_ty); // Should not fail 35175 return result_ty; 35176 } 35177 35178 /// There is another implementation of this in `Type.onePossibleValue`. This one 35179 /// in `Sema` is for calling during semantic analysis, and performs field resolution 35180 /// to get the answer. The one in `Type` is for calling during codegen and asserts 35181 /// that the types are already resolved. 35182 /// TODO assert the return value matches `ty.onePossibleValue` 35183 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { 35184 const mod = sema.mod; 35185 return switch (ty.toIntern()) { 35186 .u0_type, 35187 .i0_type, 35188 => try mod.intValue(ty, 0), 35189 .u1_type, 35190 .u8_type, 35191 .i8_type, 35192 .u16_type, 35193 .i16_type, 35194 .u29_type, 35195 .u32_type, 35196 .i32_type, 35197 .u64_type, 35198 .i64_type, 35199 .u80_type, 35200 .u128_type, 35201 .i128_type, 35202 .usize_type, 35203 .isize_type, 35204 .c_char_type, 35205 .c_short_type, 35206 .c_ushort_type, 35207 .c_int_type, 35208 .c_uint_type, 35209 .c_long_type, 35210 .c_ulong_type, 35211 .c_longlong_type, 35212 .c_ulonglong_type, 35213 .c_longdouble_type, 35214 .f16_type, 35215 .f32_type, 35216 .f64_type, 35217 .f80_type, 35218 .f128_type, 35219 .anyopaque_type, 35220 .bool_type, 35221 .type_type, 35222 .anyerror_type, 35223 .comptime_int_type, 35224 .comptime_float_type, 35225 .enum_literal_type, 35226 .atomic_order_type, 35227 .atomic_rmw_op_type, 35228 .calling_convention_type, 35229 .address_space_type, 35230 .float_mode_type, 35231 .reduce_op_type, 35232 .call_modifier_type, 35233 .prefetch_options_type, 35234 .export_options_type, 35235 .extern_options_type, 35236 .type_info_type, 35237 .manyptr_u8_type, 35238 .manyptr_const_u8_type, 35239 .manyptr_const_u8_sentinel_0_type, 35240 .single_const_pointer_to_comptime_int_type, 35241 .slice_const_u8_type, 35242 .slice_const_u8_sentinel_0_type, 35243 .anyerror_void_error_union_type, 35244 => null, 35245 .void_type => Value.void, 35246 .noreturn_type => Value.@"unreachable", 35247 .anyframe_type => unreachable, 35248 .null_type => Value.null, 35249 .undefined_type => Value.undef, 35250 .optional_noreturn_type => try mod.nullValue(ty), 35251 .generic_poison_type => error.GenericPoison, 35252 .empty_struct_type => Value.empty_struct, 35253 // values, not types 35254 .undef, 35255 .zero, 35256 .zero_usize, 35257 .zero_u8, 35258 .one, 35259 .one_usize, 35260 .one_u8, 35261 .four_u8, 35262 .negative_one, 35263 .calling_convention_c, 35264 .calling_convention_inline, 35265 .void_value, 35266 .unreachable_value, 35267 .null_value, 35268 .bool_true, 35269 .bool_false, 35270 .empty_struct, 35271 .generic_poison, 35272 // invalid 35273 .var_args_param_type, 35274 .none, 35275 => unreachable, 35276 _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) { 35277 .type_int_signed, // i0 handled above 35278 .type_int_unsigned, // u0 handled above 35279 .type_pointer, 35280 .type_slice, 35281 .type_optional, // ?noreturn handled above 35282 .type_anyframe, 35283 .type_error_union, 35284 .type_error_set, 35285 .type_inferred_error_set, 35286 .type_opaque, 35287 .type_function, 35288 => null, 35289 .simple_type, // handled above 35290 // values, not types 35291 .undef, 35292 .runtime_value, 35293 .simple_value, 35294 .ptr_decl, 35295 .ptr_mut_decl, 35296 .ptr_comptime_field, 35297 .ptr_int, 35298 .ptr_eu_payload, 35299 .ptr_opt_payload, 35300 .ptr_elem, 35301 .ptr_field, 35302 .ptr_slice, 35303 .opt_payload, 35304 .opt_null, 35305 .int_u8, 35306 .int_u16, 35307 .int_u32, 35308 .int_i32, 35309 .int_usize, 35310 .int_comptime_int_u32, 35311 .int_comptime_int_i32, 35312 .int_small, 35313 .int_positive, 35314 .int_negative, 35315 .int_lazy_align, 35316 .int_lazy_size, 35317 .error_set_error, 35318 .error_union_error, 35319 .error_union_payload, 35320 .enum_literal, 35321 .enum_tag, 35322 .float_f16, 35323 .float_f32, 35324 .float_f64, 35325 .float_f80, 35326 .float_f128, 35327 .float_c_longdouble_f80, 35328 .float_c_longdouble_f128, 35329 .float_comptime_float, 35330 .variable, 35331 .extern_func, 35332 .func, 35333 .only_possible_value, 35334 .union_value, 35335 .bytes, 35336 .aggregate, 35337 .repeated, 35338 // memoized value, not types 35339 .memoized_call, 35340 => unreachable, 35341 .type_array_big, 35342 .type_array_small, 35343 .type_vector, 35344 .type_enum_auto, 35345 .type_enum_explicit, 35346 .type_enum_nonexhaustive, 35347 .type_struct, 35348 .type_struct_ns, 35349 .type_struct_anon, 35350 .type_tuple_anon, 35351 .type_union_tagged, 35352 .type_union_untagged, 35353 .type_union_safety, 35354 => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 35355 inline .array_type, .vector_type => |seq_type, seq_tag| { 35356 const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; 35357 if (seq_type.len + @intFromBool(has_sentinel) == 0) return (try mod.intern(.{ .aggregate = .{ 35358 .ty = ty.toIntern(), 35359 .storage = .{ .elems = &.{} }, 35360 } })).toValue(); 35361 35362 if (try sema.typeHasOnePossibleValue(seq_type.child.toType())) |opv| { 35363 return (try mod.intern(.{ .aggregate = .{ 35364 .ty = ty.toIntern(), 35365 .storage = .{ .repeated_elem = opv.toIntern() }, 35366 } })).toValue(); 35367 } 35368 return null; 35369 }, 35370 35371 .struct_type => |struct_type| { 35372 const resolved_ty = try sema.resolveTypeFields(ty); 35373 if (mod.structPtrUnwrap(struct_type.index)) |s| { 35374 const field_vals = try sema.arena.alloc(InternPool.Index, s.fields.count()); 35375 for (field_vals, s.fields.values(), 0..) |*field_val, field, i| { 35376 if (field.is_comptime) { 35377 field_val.* = field.default_val; 35378 continue; 35379 } 35380 if (field.ty.eql(resolved_ty, sema.mod)) { 35381 const msg = try Module.ErrorMsg.create( 35382 sema.gpa, 35383 s.srcLoc(sema.mod), 35384 "struct '{}' depends on itself", 35385 .{ty.fmt(sema.mod)}, 35386 ); 35387 try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{}); 35388 return sema.failWithOwnedErrorMsg(msg); 35389 } 35390 if (try sema.typeHasOnePossibleValue(field.ty)) |field_opv| { 35391 field_val.* = try field_opv.intern(field.ty, mod); 35392 } else return null; 35393 } 35394 35395 // In this case the struct has no runtime-known fields and 35396 // therefore has one possible value. 35397 return (try mod.intern(.{ .aggregate = .{ 35398 .ty = ty.toIntern(), 35399 .storage = .{ .elems = field_vals }, 35400 } })).toValue(); 35401 } 35402 35403 // In this case the struct has no fields at all and 35404 // therefore has one possible value. 35405 return (try mod.intern(.{ .aggregate = .{ 35406 .ty = ty.toIntern(), 35407 .storage = .{ .elems = &.{} }, 35408 } })).toValue(); 35409 }, 35410 35411 .anon_struct_type => |tuple| { 35412 for (tuple.values) |val| { 35413 if (val == .none) return null; 35414 } 35415 // In this case the struct has all comptime-known fields and 35416 // therefore has one possible value. 35417 // TODO: write something like getCoercedInts to avoid needing to dupe 35418 return (try mod.intern(.{ .aggregate = .{ 35419 .ty = ty.toIntern(), 35420 .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values) }, 35421 } })).toValue(); 35422 }, 35423 35424 .union_type => |union_type| { 35425 const resolved_ty = try sema.resolveTypeFields(ty); 35426 const union_obj = mod.unionPtr(union_type.index); 35427 const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse 35428 return null; 35429 const fields = union_obj.fields.values(); 35430 if (fields.len == 0) { 35431 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); 35432 return only.toValue(); 35433 } 35434 const only_field = fields[0]; 35435 if (only_field.ty.eql(resolved_ty, sema.mod)) { 35436 const msg = try Module.ErrorMsg.create( 35437 sema.gpa, 35438 union_obj.srcLoc(sema.mod), 35439 "union '{}' depends on itself", 35440 .{ty.fmt(sema.mod)}, 35441 ); 35442 try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{}); 35443 return sema.failWithOwnedErrorMsg(msg); 35444 } 35445 const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse 35446 return null; 35447 const only = try mod.intern(.{ .un = .{ 35448 .ty = resolved_ty.toIntern(), 35449 .tag = tag_val.toIntern(), 35450 .val = val_val.toIntern(), 35451 } }); 35452 return only.toValue(); 35453 }, 35454 35455 .enum_type => |enum_type| switch (enum_type.tag_mode) { 35456 .nonexhaustive => { 35457 if (enum_type.tag_ty == .comptime_int_type) return null; 35458 35459 if (try sema.typeHasOnePossibleValue(enum_type.tag_ty.toType())) |int_opv| { 35460 const only = try mod.intern(.{ .enum_tag = .{ 35461 .ty = ty.toIntern(), 35462 .int = int_opv.toIntern(), 35463 } }); 35464 return only.toValue(); 35465 } 35466 35467 return null; 35468 }, 35469 .auto, .explicit => { 35470 if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null; 35471 35472 switch (enum_type.names.len) { 35473 0 => { 35474 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); 35475 return only.toValue(); 35476 }, 35477 1 => return try mod.getCoerced((if (enum_type.values.len == 0) 35478 try mod.intern(.{ .int = .{ 35479 .ty = enum_type.tag_ty, 35480 .storage = .{ .u64 = 0 }, 35481 } }) 35482 else 35483 enum_type.values[0]).toValue(), ty), 35484 else => return null, 35485 } 35486 }, 35487 }, 35488 35489 else => unreachable, 35490 }, 35491 }, 35492 }; 35493 } 35494 35495 /// Returns the type of the AIR instruction. 35496 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type { 35497 return sema.getTmpAir().typeOf(inst, &sema.mod.intern_pool); 35498 } 35499 35500 pub fn getTmpAir(sema: Sema) Air { 35501 return .{ 35502 .instructions = sema.air_instructions.slice(), 35503 .extra = sema.air_extra.items, 35504 }; 35505 } 35506 35507 // TODO: make this non-fallible or remove it entirely 35508 pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { 35509 _ = sema; 35510 return Air.internedToRef(ty.toIntern()); 35511 } 35512 35513 fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref { 35514 const mod = sema.mod; 35515 return sema.addConstant(try mod.intValue(ty, int)); 35516 } 35517 35518 fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { 35519 return sema.addConstant((try sema.mod.intern(.{ .undef = ty.toIntern() })).toValue()); 35520 } 35521 35522 // TODO: make this non-fallible or remove it entirely 35523 pub fn addConstant(sema: *Sema, val: Value) !Air.Inst.Ref { 35524 _ = sema; 35525 return Air.internedToRef(val.toIntern()); 35526 } 35527 35528 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 { 35529 const fields = std.meta.fields(@TypeOf(extra)); 35530 try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len); 35531 return sema.addExtraAssumeCapacity(extra); 35532 } 35533 35534 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { 35535 const fields = std.meta.fields(@TypeOf(extra)); 35536 const result = @as(u32, @intCast(sema.air_extra.items.len)); 35537 inline for (fields) |field| { 35538 sema.air_extra.appendAssumeCapacity(switch (field.type) { 35539 u32 => @field(extra, field.name), 35540 Air.Inst.Ref => @intFromEnum(@field(extra, field.name)), 35541 i32 => @as(u32, @bitCast(@field(extra, field.name))), 35542 InternPool.Index => @intFromEnum(@field(extra, field.name)), 35543 else => @compileError("bad field type: " ++ @typeName(field.type)), 35544 }); 35545 } 35546 return result; 35547 } 35548 35549 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void { 35550 const coerced = @as([]const u32, @ptrCast(refs)); 35551 sema.air_extra.appendSliceAssumeCapacity(coerced); 35552 } 35553 35554 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index { 35555 const air_datas = sema.air_instructions.items(.data); 35556 const air_tags = sema.air_instructions.items(.tag); 35557 switch (air_tags[inst_index]) { 35558 .br => return air_datas[inst_index].br.block_inst, 35559 else => return null, 35560 } 35561 } 35562 35563 fn isComptimeKnown( 35564 sema: *Sema, 35565 inst: Air.Inst.Ref, 35566 ) !bool { 35567 return (try sema.resolveMaybeUndefVal(inst)) != null; 35568 } 35569 35570 fn analyzeComptimeAlloc( 35571 sema: *Sema, 35572 block: *Block, 35573 var_type: Type, 35574 alignment: Alignment, 35575 ) CompileError!Air.Inst.Ref { 35576 const mod = sema.mod; 35577 35578 // Needed to make an anon decl with type `var_type` (the `finish()` call below). 35579 _ = try sema.typeHasOnePossibleValue(var_type); 35580 35581 const ptr_type = try mod.ptrType(.{ 35582 .child = var_type.toIntern(), 35583 .flags = .{ 35584 .alignment = alignment, 35585 .address_space = target_util.defaultAddressSpace(mod.getTarget(), .global_constant), 35586 }, 35587 }); 35588 35589 var anon_decl = try block.startAnonDecl(); 35590 defer anon_decl.deinit(); 35591 35592 const decl_index = try anon_decl.finish( 35593 var_type, 35594 // There will be stores before the first load, but they may be to sub-elements or 35595 // sub-fields. So we need to initialize with undef to allow the mechanism to expand 35596 // into fields/elements and have those overridden with stored values. 35597 (try mod.intern(.{ .undef = var_type.toIntern() })).toValue(), 35598 alignment, 35599 ); 35600 const decl = mod.declPtr(decl_index); 35601 decl.alignment = alignment; 35602 35603 try sema.comptime_mutable_decls.append(decl_index); 35604 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 35605 return sema.addConstant((try mod.intern(.{ .ptr = .{ 35606 .ty = ptr_type.toIntern(), 35607 .addr = .{ .mut_decl = .{ 35608 .decl = decl_index, 35609 .runtime_index = block.runtime_index, 35610 } }, 35611 } })).toValue()); 35612 } 35613 35614 /// The places where a user can specify an address space attribute 35615 pub const AddressSpaceContext = enum { 35616 /// A function is specified to be placed in a certain address space. 35617 function, 35618 35619 /// A (global) variable is specified to be placed in a certain address space. 35620 /// In contrast to .constant, these values (and thus the address space they will be 35621 /// placed in) are required to be mutable. 35622 variable, 35623 35624 /// A (global) constant value is specified to be placed in a certain address space. 35625 /// In contrast to .variable, values placed in this address space are not required to be mutable. 35626 constant, 35627 35628 /// A pointer is ascripted to point into a certain address space. 35629 pointer, 35630 }; 35631 35632 pub fn analyzeAddressSpace( 35633 sema: *Sema, 35634 block: *Block, 35635 src: LazySrcLoc, 35636 zir_ref: Zir.Inst.Ref, 35637 ctx: AddressSpaceContext, 35638 ) !std.builtin.AddressSpace { 35639 const mod = sema.mod; 35640 const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, "addresspace must be comptime-known"); 35641 const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); 35642 const target = sema.mod.getTarget(); 35643 const arch = target.cpu.arch; 35644 35645 const is_nv = arch == .nvptx or arch == .nvptx64; 35646 const is_amd = arch == .amdgcn; 35647 const is_spirv = arch == .spirv32 or arch == .spirv64; 35648 const is_gpu = is_nv or is_amd or is_spirv; 35649 35650 const supported = switch (address_space) { 35651 // TODO: on spir-v only when os is opencl. 35652 .generic => true, 35653 .gs, .fs, .ss => (arch == .x86 or arch == .x86_64) and ctx == .pointer, 35654 // TODO: check that .shared and .local are left uninitialized 35655 .param => is_nv, 35656 .global, .shared, .local => is_gpu, 35657 .constant => is_gpu and (ctx == .constant), 35658 // TODO this should also check how many flash banks the cpu has 35659 .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr, 35660 }; 35661 35662 if (!supported) { 35663 // TODO error messages could be made more elaborate here 35664 const entity = switch (ctx) { 35665 .function => "functions", 35666 .variable => "mutable values", 35667 .constant => "constant values", 35668 .pointer => "pointers", 35669 }; 35670 return sema.fail( 35671 block, 35672 src, 35673 "{s} with address space '{s}' are not supported on {s}", 35674 .{ entity, @tagName(address_space), arch.genericName() }, 35675 ); 35676 } 35677 35678 return address_space; 35679 } 35680 35681 /// Asserts the value is a pointer and dereferences it. 35682 /// Returns `null` if the pointer contents cannot be loaded at comptime. 35683 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { 35684 const mod = sema.mod; 35685 const load_ty = ptr_ty.childType(mod); 35686 const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty); 35687 switch (res) { 35688 .runtime_load => return null, 35689 .val => |v| return v, 35690 .needed_well_defined => |ty| return sema.fail( 35691 block, 35692 src, 35693 "comptime dereference requires '{}' to have a well-defined layout, but it does not.", 35694 .{ty.fmt(sema.mod)}, 35695 ), 35696 .out_of_bounds => |ty| return sema.fail( 35697 block, 35698 src, 35699 "dereference of '{}' exceeds bounds of containing decl of type '{}'", 35700 .{ ptr_ty.fmt(sema.mod), ty.fmt(sema.mod) }, 35701 ), 35702 } 35703 } 35704 35705 const DerefResult = union(enum) { 35706 runtime_load, 35707 val: Value, 35708 needed_well_defined: Type, 35709 out_of_bounds: Type, 35710 }; 35711 35712 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type) CompileError!DerefResult { 35713 const mod = sema.mod; 35714 const target = mod.getTarget(); 35715 const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) { 35716 error.RuntimeLoad => return DerefResult{ .runtime_load = {} }, 35717 else => |e| return e, 35718 }; 35719 35720 if (deref.pointee) |tv| { 35721 const coerce_in_mem_ok = 35722 (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or 35723 (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok; 35724 if (coerce_in_mem_ok) { 35725 // We have a Value that lines up in virtual memory exactly with what we want to load, 35726 // and it is in-memory coercible to load_ty. It may be returned without modifications. 35727 // Move mutable decl values to the InternPool and assert other decls are already in 35728 // the InternPool. 35729 const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern(); 35730 const coerced_val = try mod.getCoerced(uncoerced_val.toValue(), load_ty); 35731 return .{ .val = coerced_val }; 35732 } 35733 } 35734 35735 // The type is not in-memory coercible or the direct dereference failed, so it must 35736 // be bitcast according to the pointer type we are performing the load through. 35737 if (!load_ty.hasWellDefinedLayout(mod)) { 35738 return DerefResult{ .needed_well_defined = load_ty }; 35739 } 35740 35741 const load_sz = try sema.typeAbiSize(load_ty); 35742 35743 // Try the smaller bit-cast first, since that's more efficient than using the larger `parent` 35744 if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty)) 35745 return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load }; 35746 35747 // If that fails, try to bit-cast from the largest parent value with a well-defined layout 35748 if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty)) 35749 return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load }; 35750 35751 if (deref.ty_without_well_defined_layout) |bad_ty| { 35752 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem 35753 // is that some type we encountered when de-referencing does not have a well-defined layout. 35754 return DerefResult{ .needed_well_defined = bad_ty }; 35755 } else { 35756 // If all encountered types had well-defined layouts, the parent is the root decl and it just 35757 // wasn't big enough for the load. 35758 return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty }; 35759 } 35760 } 35761 35762 /// Used to convert a u64 value to a usize value, emitting a compile error if the number 35763 /// is too big to fit. 35764 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { 35765 if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; 35766 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}); 35767 } 35768 35769 /// For pointer-like optionals, it returns the pointer type. For pointers, 35770 /// the type is returned unmodified. 35771 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether 35772 /// a type has zero bits, which can cause a "foo depends on itself" compile error. 35773 /// This logic must be kept in sync with `Type.isPtrLikeOptional`. 35774 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { 35775 const mod = sema.mod; 35776 return switch (mod.intern_pool.indexToKey(ty.toIntern())) { 35777 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 35778 .One, .Many, .C => ty, 35779 .Slice => null, 35780 }, 35781 .opt_type => |opt_child| switch (mod.intern_pool.indexToKey(opt_child)) { 35782 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 35783 .Slice, .C => null, 35784 .Many, .One => { 35785 if (ptr_type.flags.is_allowzero) return null; 35786 35787 // optionals of zero sized types behave like bools, not pointers 35788 const payload_ty = opt_child.toType(); 35789 if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) { 35790 return null; 35791 } 35792 35793 return payload_ty; 35794 }, 35795 }, 35796 else => null, 35797 }, 35798 else => null, 35799 }; 35800 } 35801 35802 /// `generic_poison` will return false. 35803 /// This function returns false negatives when structs and unions are having their 35804 /// field types resolved. 35805 /// TODO assert the return value matches `ty.comptimeOnly` 35806 /// TODO merge these implementations together with the "advanced"/opt_sema pattern seen 35807 /// elsewhere in value.zig 35808 pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { 35809 const mod = sema.mod; 35810 return switch (ty.toIntern()) { 35811 .empty_struct_type => false, 35812 35813 else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 35814 .int_type => return false, 35815 .ptr_type => |ptr_type| { 35816 const child_ty = ptr_type.child.toType(); 35817 if (child_ty.zigTypeTag(mod) == .Fn) { 35818 return mod.typeToFunc(child_ty).?.is_generic; 35819 } else { 35820 return sema.typeRequiresComptime(child_ty); 35821 } 35822 }, 35823 .anyframe_type => |child| { 35824 if (child == .none) return false; 35825 return sema.typeRequiresComptime(child.toType()); 35826 }, 35827 .array_type => |array_type| return sema.typeRequiresComptime(array_type.child.toType()), 35828 .vector_type => |vector_type| return sema.typeRequiresComptime(vector_type.child.toType()), 35829 .opt_type => |child| return sema.typeRequiresComptime(child.toType()), 35830 35831 .error_union_type => |error_union_type| { 35832 return sema.typeRequiresComptime(error_union_type.payload_type.toType()); 35833 }, 35834 35835 .error_set_type, .inferred_error_set_type => false, 35836 35837 .func_type => true, 35838 35839 .simple_type => |t| return switch (t) { 35840 .f16, 35841 .f32, 35842 .f64, 35843 .f80, 35844 .f128, 35845 .usize, 35846 .isize, 35847 .c_char, 35848 .c_short, 35849 .c_ushort, 35850 .c_int, 35851 .c_uint, 35852 .c_long, 35853 .c_ulong, 35854 .c_longlong, 35855 .c_ulonglong, 35856 .c_longdouble, 35857 .anyopaque, 35858 .bool, 35859 .void, 35860 .anyerror, 35861 .noreturn, 35862 .generic_poison, 35863 .atomic_order, 35864 .atomic_rmw_op, 35865 .calling_convention, 35866 .address_space, 35867 .float_mode, 35868 .reduce_op, 35869 .call_modifier, 35870 .prefetch_options, 35871 .export_options, 35872 .extern_options, 35873 => false, 35874 35875 .type, 35876 .comptime_int, 35877 .comptime_float, 35878 .null, 35879 .undefined, 35880 .enum_literal, 35881 .type_info, 35882 => true, 35883 }, 35884 .struct_type => |struct_type| { 35885 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; 35886 switch (struct_obj.requires_comptime) { 35887 .no, .wip => return false, 35888 .yes => return true, 35889 .unknown => { 35890 if (struct_obj.status == .field_types_wip) 35891 return false; 35892 35893 try sema.resolveTypeFieldsStruct(ty, struct_obj); 35894 35895 struct_obj.requires_comptime = .wip; 35896 for (struct_obj.fields.values()) |field| { 35897 if (field.is_comptime) continue; 35898 if (try sema.typeRequiresComptime(field.ty)) { 35899 struct_obj.requires_comptime = .yes; 35900 return true; 35901 } 35902 } 35903 struct_obj.requires_comptime = .no; 35904 return false; 35905 }, 35906 } 35907 }, 35908 .anon_struct_type => |tuple| { 35909 for (tuple.types, tuple.values) |field_ty, val| { 35910 const have_comptime_val = val != .none; 35911 if (!have_comptime_val and try sema.typeRequiresComptime(field_ty.toType())) { 35912 return true; 35913 } 35914 } 35915 return false; 35916 }, 35917 35918 .union_type => |union_type| { 35919 const union_obj = mod.unionPtr(union_type.index); 35920 switch (union_obj.requires_comptime) { 35921 .no, .wip => return false, 35922 .yes => return true, 35923 .unknown => { 35924 if (union_obj.status == .field_types_wip) 35925 return false; 35926 35927 try sema.resolveTypeFieldsUnion(ty, union_obj); 35928 35929 union_obj.requires_comptime = .wip; 35930 for (union_obj.fields.values()) |field| { 35931 if (try sema.typeRequiresComptime(field.ty)) { 35932 union_obj.requires_comptime = .yes; 35933 return true; 35934 } 35935 } 35936 union_obj.requires_comptime = .no; 35937 return false; 35938 }, 35939 } 35940 }, 35941 35942 .opaque_type => false, 35943 .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()), 35944 35945 // values, not types 35946 .undef, 35947 .runtime_value, 35948 .simple_value, 35949 .variable, 35950 .extern_func, 35951 .func, 35952 .int, 35953 .err, 35954 .error_union, 35955 .enum_literal, 35956 .enum_tag, 35957 .empty_enum_value, 35958 .float, 35959 .ptr, 35960 .opt, 35961 .aggregate, 35962 .un, 35963 // memoization, not types 35964 .memoized_call, 35965 => unreachable, 35966 }, 35967 }; 35968 } 35969 35970 pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { 35971 const mod = sema.mod; 35972 return ty.hasRuntimeBitsAdvanced(mod, false, .{ .sema = sema }) catch |err| switch (err) { 35973 error.NeedLazy => unreachable, 35974 else => |e| return e, 35975 }; 35976 } 35977 35978 fn typeAbiSize(sema: *Sema, ty: Type) !u64 { 35979 try sema.resolveTypeLayout(ty); 35980 return ty.abiSize(sema.mod); 35981 } 35982 35983 fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!u32 { 35984 return (try ty.abiAlignmentAdvanced(sema.mod, .{ .sema = sema })).scalar; 35985 } 35986 35987 /// Not valid to call for packed unions. 35988 /// Keep implementation in sync with `Module.Union.Field.normalAlignment`. 35989 fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 { 35990 return @as(u32, @intCast(if (field.ty.isNoReturn(sema.mod)) 35991 0 35992 else 35993 field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty))); 35994 } 35995 35996 /// Keep implementation in sync with `Module.Struct.Field.alignment`. 35997 fn structFieldAlignment(sema: *Sema, field: Module.Struct.Field, layout: std.builtin.Type.ContainerLayout) !u32 { 35998 const mod = sema.mod; 35999 if (field.abi_align.toByteUnitsOptional()) |a| { 36000 assert(layout != .Packed); 36001 return @as(u32, @intCast(a)); 36002 } 36003 switch (layout) { 36004 .Packed => return 0, 36005 .Auto => if (mod.getTarget().ofmt != .c) { 36006 return sema.typeAbiAlignment(field.ty); 36007 }, 36008 .Extern => {}, 36009 } 36010 // extern 36011 const ty_abi_align = try sema.typeAbiAlignment(field.ty); 36012 if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) { 36013 return @max(ty_abi_align, 16); 36014 } 36015 return ty_abi_align; 36016 } 36017 36018 /// Synchronize logic with `Type.isFnOrHasRuntimeBits`. 36019 pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { 36020 const mod = sema.mod; 36021 const fn_info = mod.typeToFunc(ty).?; 36022 if (fn_info.is_generic) return false; 36023 if (fn_info.is_var_args) return true; 36024 switch (fn_info.cc) { 36025 // If there was a comptime calling convention, it should also return false here. 36026 .Inline => return false, 36027 else => {}, 36028 } 36029 if (try sema.typeRequiresComptime(fn_info.return_type.toType())) { 36030 return false; 36031 } 36032 return true; 36033 } 36034 36035 fn unionFieldIndex( 36036 sema: *Sema, 36037 block: *Block, 36038 unresolved_union_ty: Type, 36039 field_name: InternPool.NullTerminatedString, 36040 field_src: LazySrcLoc, 36041 ) !u32 { 36042 const mod = sema.mod; 36043 const union_ty = try sema.resolveTypeFields(unresolved_union_ty); 36044 const union_obj = mod.typeToUnion(union_ty).?; 36045 const field_index_usize = union_obj.fields.getIndex(field_name) orelse 36046 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); 36047 return @as(u32, @intCast(field_index_usize)); 36048 } 36049 36050 fn structFieldIndex( 36051 sema: *Sema, 36052 block: *Block, 36053 unresolved_struct_ty: Type, 36054 field_name: InternPool.NullTerminatedString, 36055 field_src: LazySrcLoc, 36056 ) !u32 { 36057 const mod = sema.mod; 36058 const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); 36059 if (struct_ty.isAnonStruct(mod)) { 36060 return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src); 36061 } else { 36062 const struct_obj = mod.typeToStruct(struct_ty).?; 36063 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse 36064 return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); 36065 return @as(u32, @intCast(field_index_usize)); 36066 } 36067 } 36068 36069 fn anonStructFieldIndex( 36070 sema: *Sema, 36071 block: *Block, 36072 struct_ty: Type, 36073 field_name: InternPool.NullTerminatedString, 36074 field_src: LazySrcLoc, 36075 ) !u32 { 36076 const mod = sema.mod; 36077 switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) { 36078 .anon_struct_type => |anon_struct_type| for (anon_struct_type.names, 0..) |name, i| { 36079 if (name == field_name) return @as(u32, @intCast(i)); 36080 }, 36081 .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| { 36082 for (struct_obj.fields.keys(), 0..) |name, i| { 36083 if (name == field_name) { 36084 return @as(u32, @intCast(i)); 36085 } 36086 } 36087 }, 36088 else => unreachable, 36089 } 36090 return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{ 36091 field_name.fmt(&mod.intern_pool), struct_ty.fmt(sema.mod), 36092 }); 36093 } 36094 36095 fn queueFullTypeResolution(sema: *Sema, ty: Type) !void { 36096 try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {}); 36097 } 36098 36099 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting 36100 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar). 36101 fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value { 36102 var overflow: usize = undefined; 36103 return sema.intAddInner(lhs, rhs, ty, &overflow) catch |err| switch (err) { 36104 error.Overflow => { 36105 const is_vec = ty.isVector(sema.mod); 36106 overflow_idx.* = if (is_vec) overflow else 0; 36107 const safe_ty = if (is_vec) try sema.mod.vectorType(.{ 36108 .len = ty.vectorLen(sema.mod), 36109 .child = .comptime_int_type, 36110 }) else Type.comptime_int; 36111 return sema.intAddInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) { 36112 error.Overflow => unreachable, 36113 else => |e| return e, 36114 }; 36115 }, 36116 else => |e| return e, 36117 }; 36118 } 36119 36120 fn intAddInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value { 36121 const mod = sema.mod; 36122 if (ty.zigTypeTag(mod) == .Vector) { 36123 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 36124 const scalar_ty = ty.scalarType(mod); 36125 for (result_data, 0..) |*scalar, i| { 36126 const lhs_elem = try lhs.elemValue(mod, i); 36127 const rhs_elem = try rhs.elemValue(mod, i); 36128 const val = sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) { 36129 error.Overflow => { 36130 overflow_idx.* = i; 36131 return error.Overflow; 36132 }, 36133 else => |e| return e, 36134 }; 36135 scalar.* = try val.intern(scalar_ty, mod); 36136 } 36137 return (try mod.intern(.{ .aggregate = .{ 36138 .ty = ty.toIntern(), 36139 .storage = .{ .elems = result_data }, 36140 } })).toValue(); 36141 } 36142 return sema.intAddScalar(lhs, rhs, ty); 36143 } 36144 36145 fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value { 36146 const mod = sema.mod; 36147 if (scalar_ty.toIntern() != .comptime_int_type) { 36148 const res = try sema.intAddWithOverflowScalar(lhs, rhs, scalar_ty); 36149 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow; 36150 return res.wrapped_result; 36151 } 36152 // TODO is this a performance issue? maybe we should try the operation without 36153 // resorting to BigInt first. 36154 var lhs_space: Value.BigIntSpace = undefined; 36155 var rhs_space: Value.BigIntSpace = undefined; 36156 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 36157 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 36158 const limbs = try sema.arena.alloc( 36159 std.math.big.Limb, 36160 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, 36161 ); 36162 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 36163 result_bigint.add(lhs_bigint, rhs_bigint); 36164 return mod.intValue_big(scalar_ty, result_bigint.toConst()); 36165 } 36166 36167 /// Supports both floats and ints; handles undefined. 36168 fn numberAddWrapScalar( 36169 sema: *Sema, 36170 lhs: Value, 36171 rhs: Value, 36172 ty: Type, 36173 ) !Value { 36174 const mod = sema.mod; 36175 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; 36176 36177 if (ty.zigTypeTag(mod) == .ComptimeInt) { 36178 return sema.intAdd(lhs, rhs, ty, undefined); 36179 } 36180 36181 if (ty.isAnyFloat()) { 36182 return Value.floatAdd(lhs, rhs, ty, sema.arena, mod); 36183 } 36184 36185 const overflow_result = try sema.intAddWithOverflow(lhs, rhs, ty); 36186 return overflow_result.wrapped_result; 36187 } 36188 36189 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting 36190 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar). 36191 fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value { 36192 var overflow: usize = undefined; 36193 return sema.intSubInner(lhs, rhs, ty, &overflow) catch |err| switch (err) { 36194 error.Overflow => { 36195 const is_vec = ty.isVector(sema.mod); 36196 overflow_idx.* = if (is_vec) overflow else 0; 36197 const safe_ty = if (is_vec) try sema.mod.vectorType(.{ 36198 .len = ty.vectorLen(sema.mod), 36199 .child = .comptime_int_type, 36200 }) else Type.comptime_int; 36201 return sema.intSubInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) { 36202 error.Overflow => unreachable, 36203 else => |e| return e, 36204 }; 36205 }, 36206 else => |e| return e, 36207 }; 36208 } 36209 36210 fn intSubInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value { 36211 const mod = sema.mod; 36212 if (ty.zigTypeTag(mod) == .Vector) { 36213 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 36214 const scalar_ty = ty.scalarType(mod); 36215 for (result_data, 0..) |*scalar, i| { 36216 const lhs_elem = try lhs.elemValue(sema.mod, i); 36217 const rhs_elem = try rhs.elemValue(sema.mod, i); 36218 const val = sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) { 36219 error.Overflow => { 36220 overflow_idx.* = i; 36221 return error.Overflow; 36222 }, 36223 else => |e| return e, 36224 }; 36225 scalar.* = try val.intern(scalar_ty, mod); 36226 } 36227 return (try mod.intern(.{ .aggregate = .{ 36228 .ty = ty.toIntern(), 36229 .storage = .{ .elems = result_data }, 36230 } })).toValue(); 36231 } 36232 return sema.intSubScalar(lhs, rhs, ty); 36233 } 36234 36235 fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value { 36236 const mod = sema.mod; 36237 if (scalar_ty.toIntern() != .comptime_int_type) { 36238 const res = try sema.intSubWithOverflowScalar(lhs, rhs, scalar_ty); 36239 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow; 36240 return res.wrapped_result; 36241 } 36242 // TODO is this a performance issue? maybe we should try the operation without 36243 // resorting to BigInt first. 36244 var lhs_space: Value.BigIntSpace = undefined; 36245 var rhs_space: Value.BigIntSpace = undefined; 36246 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 36247 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 36248 const limbs = try sema.arena.alloc( 36249 std.math.big.Limb, 36250 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, 36251 ); 36252 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 36253 result_bigint.sub(lhs_bigint, rhs_bigint); 36254 return mod.intValue_big(scalar_ty, result_bigint.toConst()); 36255 } 36256 36257 /// Supports both floats and ints; handles undefined. 36258 fn numberSubWrapScalar( 36259 sema: *Sema, 36260 lhs: Value, 36261 rhs: Value, 36262 ty: Type, 36263 ) !Value { 36264 const mod = sema.mod; 36265 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; 36266 36267 if (ty.zigTypeTag(mod) == .ComptimeInt) { 36268 return sema.intSub(lhs, rhs, ty, undefined); 36269 } 36270 36271 if (ty.isAnyFloat()) { 36272 return Value.floatSub(lhs, rhs, ty, sema.arena, mod); 36273 } 36274 36275 const overflow_result = try sema.intSubWithOverflow(lhs, rhs, ty); 36276 return overflow_result.wrapped_result; 36277 } 36278 36279 fn intSubWithOverflow( 36280 sema: *Sema, 36281 lhs: Value, 36282 rhs: Value, 36283 ty: Type, 36284 ) !Value.OverflowArithmeticResult { 36285 const mod = sema.mod; 36286 if (ty.zigTypeTag(mod) == .Vector) { 36287 const vec_len = ty.vectorLen(mod); 36288 const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len); 36289 const result_data = try sema.arena.alloc(InternPool.Index, vec_len); 36290 const scalar_ty = ty.scalarType(mod); 36291 for (overflowed_data, result_data, 0..) |*of, *scalar, i| { 36292 const lhs_elem = try lhs.elemValue(sema.mod, i); 36293 const rhs_elem = try rhs.elemValue(sema.mod, i); 36294 const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty); 36295 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod); 36296 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod); 36297 } 36298 return Value.OverflowArithmeticResult{ 36299 .overflow_bit = (try mod.intern(.{ .aggregate = .{ 36300 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(), 36301 .storage = .{ .elems = overflowed_data }, 36302 } })).toValue(), 36303 .wrapped_result = (try mod.intern(.{ .aggregate = .{ 36304 .ty = ty.toIntern(), 36305 .storage = .{ .elems = result_data }, 36306 } })).toValue(), 36307 }; 36308 } 36309 return sema.intSubWithOverflowScalar(lhs, rhs, ty); 36310 } 36311 36312 fn intSubWithOverflowScalar( 36313 sema: *Sema, 36314 lhs: Value, 36315 rhs: Value, 36316 ty: Type, 36317 ) !Value.OverflowArithmeticResult { 36318 const mod = sema.mod; 36319 const info = ty.intInfo(mod); 36320 36321 var lhs_space: Value.BigIntSpace = undefined; 36322 var rhs_space: Value.BigIntSpace = undefined; 36323 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 36324 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 36325 const limbs = try sema.arena.alloc( 36326 std.math.big.Limb, 36327 std.math.big.int.calcTwosCompLimbCount(info.bits), 36328 ); 36329 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 36330 const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); 36331 const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()); 36332 return Value.OverflowArithmeticResult{ 36333 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)), 36334 .wrapped_result = wrapped_result, 36335 }; 36336 } 36337 36338 fn intFromFloat( 36339 sema: *Sema, 36340 block: *Block, 36341 src: LazySrcLoc, 36342 val: Value, 36343 float_ty: Type, 36344 int_ty: Type, 36345 ) CompileError!Value { 36346 const mod = sema.mod; 36347 if (float_ty.zigTypeTag(mod) == .Vector) { 36348 const elem_ty = float_ty.scalarType(mod); 36349 const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(mod)); 36350 const scalar_ty = int_ty.scalarType(mod); 36351 for (result_data, 0..) |*scalar, i| { 36352 const elem_val = try val.elemValue(sema.mod, i); 36353 scalar.* = try (try sema.intFromFloatScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod))).intern(scalar_ty, mod); 36354 } 36355 return (try mod.intern(.{ .aggregate = .{ 36356 .ty = int_ty.toIntern(), 36357 .storage = .{ .elems = result_data }, 36358 } })).toValue(); 36359 } 36360 return sema.intFromFloatScalar(block, src, val, float_ty, int_ty); 36361 } 36362 36363 // float is expected to be finite and non-NaN 36364 fn float128IntPartToBigInt( 36365 arena: Allocator, 36366 float: f128, 36367 ) !std.math.big.int.Managed { 36368 const is_negative = std.math.signbit(float); 36369 const floored = @floor(@fabs(float)); 36370 36371 var rational = try std.math.big.Rational.init(arena); 36372 defer rational.q.deinit(); 36373 rational.setFloat(f128, floored) catch |err| switch (err) { 36374 error.NonFiniteFloat => unreachable, 36375 error.OutOfMemory => return error.OutOfMemory, 36376 }; 36377 36378 // The float is reduced in rational.setFloat, so we assert that denominator is equal to one 36379 const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; 36380 assert(rational.q.toConst().eqlAbs(big_one)); 36381 36382 if (is_negative) { 36383 rational.negate(); 36384 } 36385 return rational.p; 36386 } 36387 36388 fn intFromFloatScalar( 36389 sema: *Sema, 36390 block: *Block, 36391 src: LazySrcLoc, 36392 val: Value, 36393 float_ty: Type, 36394 int_ty: Type, 36395 ) CompileError!Value { 36396 const mod = sema.mod; 36397 36398 const float = val.toFloat(f128, mod); 36399 if (std.math.isNan(float)) { 36400 return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ 36401 int_ty.fmt(sema.mod), 36402 }); 36403 } 36404 if (std.math.isInf(float)) { 36405 return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{ 36406 int_ty.fmt(sema.mod), 36407 }); 36408 } 36409 36410 var big_int = try float128IntPartToBigInt(sema.arena, float); 36411 defer big_int.deinit(); 36412 36413 const cti_result = try mod.intValue_big(Type.comptime_int, big_int.toConst()); 36414 36415 if (!(try sema.intFitsInType(cti_result, int_ty, null))) { 36416 return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{ 36417 val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod), 36418 }); 36419 } 36420 return mod.getCoerced(cti_result, int_ty); 36421 } 36422 36423 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. 36424 /// Vectors are also accepted. Vector results are reduced with AND. 36425 /// 36426 /// If provided, `vector_index` reports the first element that failed the range check. 36427 fn intFitsInType( 36428 sema: *Sema, 36429 val: Value, 36430 ty: Type, 36431 vector_index: ?*usize, 36432 ) CompileError!bool { 36433 const mod = sema.mod; 36434 if (ty.toIntern() == .comptime_int_type) return true; 36435 const info = ty.intInfo(mod); 36436 switch (val.toIntern()) { 36437 .zero_usize, .zero_u8 => return true, 36438 else => switch (mod.intern_pool.indexToKey(val.toIntern())) { 36439 .undef => return true, 36440 .variable, .extern_func, .func, .ptr => { 36441 const target = mod.getTarget(); 36442 const ptr_bits = target.ptrBitWidth(); 36443 return switch (info.signedness) { 36444 .signed => info.bits > ptr_bits, 36445 .unsigned => info.bits >= ptr_bits, 36446 }; 36447 }, 36448 .int => |int| switch (int.storage) { 36449 .u64, .i64, .big_int => { 36450 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; 36451 const big_int = int.storage.toBigInt(&buffer); 36452 return big_int.fitsInTwosComp(info.signedness, info.bits); 36453 }, 36454 .lazy_align => |lazy_ty| { 36455 const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed); 36456 // If it is u16 or bigger we know the alignment fits without resolving it. 36457 if (info.bits >= max_needed_bits) return true; 36458 const x = try sema.typeAbiAlignment(lazy_ty.toType()); 36459 if (x == 0) return true; 36460 const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed); 36461 return info.bits >= actual_needed_bits; 36462 }, 36463 .lazy_size => |lazy_ty| { 36464 const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed); 36465 // If it is u64 or bigger we know the size fits without resolving it. 36466 if (info.bits >= max_needed_bits) return true; 36467 const x = try sema.typeAbiSize(lazy_ty.toType()); 36468 if (x == 0) return true; 36469 const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed); 36470 return info.bits >= actual_needed_bits; 36471 }, 36472 }, 36473 .aggregate => |aggregate| { 36474 assert(ty.zigTypeTag(mod) == .Vector); 36475 return switch (aggregate.storage) { 36476 .bytes => |bytes| for (bytes, 0..) |byte, i| { 36477 if (byte == 0) continue; 36478 const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); 36479 if (info.bits >= actual_needed_bits) continue; 36480 if (vector_index) |vi| vi.* = i; 36481 break false; 36482 } else true, 36483 .elems, .repeated_elem => for (switch (aggregate.storage) { 36484 .bytes => unreachable, 36485 .elems => |elems| elems, 36486 .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), 36487 }, 0..) |elem, i| { 36488 if (try sema.intFitsInType(elem.toValue(), ty.scalarType(mod), null)) continue; 36489 if (vector_index) |vi| vi.* = i; 36490 break false; 36491 } else true, 36492 }; 36493 }, 36494 else => unreachable, 36495 }, 36496 } 36497 } 36498 36499 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { 36500 const mod = sema.mod; 36501 if (!(try int_val.compareAllWithZeroAdvanced(.gte, sema))) return false; 36502 const end_val = try mod.intValue(tag_ty, end); 36503 if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false; 36504 return true; 36505 } 36506 36507 /// Asserts the type is an enum. 36508 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { 36509 const mod = sema.mod; 36510 const enum_type = mod.intern_pool.indexToKey(ty.toIntern()).enum_type; 36511 assert(enum_type.tag_mode != .nonexhaustive); 36512 // The `tagValueIndex` function call below relies on the type being the integer tag type. 36513 // `getCoerced` assumes the value will fit the new type. 36514 if (!(try sema.intFitsInType(int, enum_type.tag_ty.toType(), null))) return false; 36515 const int_coerced = try mod.getCoerced(int, enum_type.tag_ty.toType()); 36516 36517 return enum_type.tagValueIndex(&mod.intern_pool, int_coerced.toIntern()) != null; 36518 } 36519 36520 fn intAddWithOverflow( 36521 sema: *Sema, 36522 lhs: Value, 36523 rhs: Value, 36524 ty: Type, 36525 ) !Value.OverflowArithmeticResult { 36526 const mod = sema.mod; 36527 if (ty.zigTypeTag(mod) == .Vector) { 36528 const vec_len = ty.vectorLen(mod); 36529 const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len); 36530 const result_data = try sema.arena.alloc(InternPool.Index, vec_len); 36531 const scalar_ty = ty.scalarType(mod); 36532 for (overflowed_data, result_data, 0..) |*of, *scalar, i| { 36533 const lhs_elem = try lhs.elemValue(sema.mod, i); 36534 const rhs_elem = try rhs.elemValue(sema.mod, i); 36535 const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty); 36536 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod); 36537 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod); 36538 } 36539 return Value.OverflowArithmeticResult{ 36540 .overflow_bit = (try mod.intern(.{ .aggregate = .{ 36541 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(), 36542 .storage = .{ .elems = overflowed_data }, 36543 } })).toValue(), 36544 .wrapped_result = (try mod.intern(.{ .aggregate = .{ 36545 .ty = ty.toIntern(), 36546 .storage = .{ .elems = result_data }, 36547 } })).toValue(), 36548 }; 36549 } 36550 return sema.intAddWithOverflowScalar(lhs, rhs, ty); 36551 } 36552 36553 fn intAddWithOverflowScalar( 36554 sema: *Sema, 36555 lhs: Value, 36556 rhs: Value, 36557 ty: Type, 36558 ) !Value.OverflowArithmeticResult { 36559 const mod = sema.mod; 36560 const info = ty.intInfo(mod); 36561 36562 var lhs_space: Value.BigIntSpace = undefined; 36563 var rhs_space: Value.BigIntSpace = undefined; 36564 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 36565 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 36566 const limbs = try sema.arena.alloc( 36567 std.math.big.Limb, 36568 std.math.big.int.calcTwosCompLimbCount(info.bits), 36569 ); 36570 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 36571 const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); 36572 const result = try mod.intValue_big(ty, result_bigint.toConst()); 36573 return Value.OverflowArithmeticResult{ 36574 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)), 36575 .wrapped_result = result, 36576 }; 36577 } 36578 36579 /// Asserts the values are comparable. Both operands have type `ty`. 36580 /// For vectors, returns true if the comparison is true for ALL elements. 36581 /// 36582 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)` 36583 fn compareAll( 36584 sema: *Sema, 36585 lhs: Value, 36586 op: std.math.CompareOperator, 36587 rhs: Value, 36588 ty: Type, 36589 ) CompileError!bool { 36590 const mod = sema.mod; 36591 if (ty.zigTypeTag(mod) == .Vector) { 36592 var i: usize = 0; 36593 while (i < ty.vectorLen(mod)) : (i += 1) { 36594 const lhs_elem = try lhs.elemValue(sema.mod, i); 36595 const rhs_elem = try rhs.elemValue(sema.mod, i); 36596 if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)))) { 36597 return false; 36598 } 36599 } 36600 return true; 36601 } 36602 return sema.compareScalar(lhs, op, rhs, ty); 36603 } 36604 36605 /// Asserts the values are comparable. Both operands have type `ty`. 36606 fn compareScalar( 36607 sema: *Sema, 36608 lhs: Value, 36609 op: std.math.CompareOperator, 36610 rhs: Value, 36611 ty: Type, 36612 ) CompileError!bool { 36613 const mod = sema.mod; 36614 const coerced_lhs = try mod.getCoerced(lhs, ty); 36615 const coerced_rhs = try mod.getCoerced(rhs, ty); 36616 switch (op) { 36617 .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty), 36618 .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)), 36619 else => return Value.compareHeteroAdvanced(coerced_lhs, op, coerced_rhs, mod, sema), 36620 } 36621 } 36622 36623 fn valuesEqual( 36624 sema: *Sema, 36625 lhs: Value, 36626 rhs: Value, 36627 ty: Type, 36628 ) CompileError!bool { 36629 return lhs.eql(rhs, ty, sema.mod); 36630 } 36631 36632 /// Asserts the values are comparable vectors of type `ty`. 36633 fn compareVector( 36634 sema: *Sema, 36635 lhs: Value, 36636 op: std.math.CompareOperator, 36637 rhs: Value, 36638 ty: Type, 36639 ) !Value { 36640 const mod = sema.mod; 36641 assert(ty.zigTypeTag(mod) == .Vector); 36642 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 36643 for (result_data, 0..) |*scalar, i| { 36644 const lhs_elem = try lhs.elemValue(sema.mod, i); 36645 const rhs_elem = try rhs.elemValue(sema.mod, i); 36646 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)); 36647 scalar.* = try Value.makeBool(res_bool).intern(Type.bool, mod); 36648 } 36649 return (try mod.intern(.{ .aggregate = .{ 36650 .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .bool_type })).toIntern(), 36651 .storage = .{ .elems = result_data }, 36652 } })).toValue(); 36653 } 36654 36655 /// Returns the type of a pointer to an element. 36656 /// Asserts that the type is a pointer, and that the element type is indexable. 36657 /// For *[N]T, return *T 36658 /// For [*]T, returns *T 36659 /// For []T, returns *T 36660 /// Handles const-ness and address spaces in particular. 36661 /// This code is duplicated in `analyzePtrArithmetic`. 36662 fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { 36663 const mod = sema.mod; 36664 const ptr_info = ptr_ty.ptrInfo(mod); 36665 const elem_ty = ptr_ty.elemType2(mod); 36666 const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0; 36667 const parent_ty = ptr_ty.childType(mod); 36668 36669 const VI = InternPool.Key.PtrType.VectorIndex; 36670 36671 const vector_info: struct { 36672 host_size: u16 = 0, 36673 alignment: u32 = 0, 36674 vector_index: VI = .none, 36675 } = if (parent_ty.isVector(mod) and ptr_info.flags.size == .One) blk: { 36676 const elem_bits = elem_ty.bitSize(mod); 36677 if (elem_bits == 0) break :blk .{}; 36678 const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits); 36679 if (!is_packed) break :blk .{}; 36680 36681 break :blk .{ 36682 .host_size = @as(u16, @intCast(parent_ty.arrayLen(mod))), 36683 .alignment = @as(u32, @intCast(parent_ty.abiAlignment(mod))), 36684 .vector_index = if (offset) |some| @as(VI, @enumFromInt(some)) else .runtime, 36685 }; 36686 } else .{}; 36687 36688 const alignment: Alignment = a: { 36689 // Calculate the new pointer alignment. 36690 if (ptr_info.flags.alignment == .none) { 36691 if (vector_info.alignment != 0) break :a Alignment.fromNonzeroByteUnits(vector_info.alignment); 36692 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. 36693 break :a .none; 36694 } 36695 // If the addend is not a comptime-known value we can still count on 36696 // it being a multiple of the type size. 36697 const elem_size = try sema.typeAbiSize(elem_ty); 36698 const addend = if (offset) |off| elem_size * off else elem_size; 36699 36700 // The resulting pointer is aligned to the lcd between the offset (an 36701 // arbitrary number) and the alignment factor (always a power of two, 36702 // non zero). 36703 const new_align = @as(Alignment, @enumFromInt(@min( 36704 @ctz(addend), 36705 @intFromEnum(ptr_info.flags.alignment), 36706 ))); 36707 assert(new_align != .none); 36708 break :a new_align; 36709 }; 36710 return mod.ptrType(.{ 36711 .child = elem_ty.toIntern(), 36712 .flags = .{ 36713 .alignment = alignment, 36714 .is_const = ptr_info.flags.is_const, 36715 .is_volatile = ptr_info.flags.is_volatile, 36716 .is_allowzero = is_allowzero, 36717 .address_space = ptr_info.flags.address_space, 36718 .vector_index = vector_info.vector_index, 36719 }, 36720 .packed_offset = .{ 36721 .host_size = vector_info.host_size, 36722 .bit_offset = 0, 36723 }, 36724 }); 36725 } 36726 36727 /// Merge lhs with rhs. 36728 /// Asserts that lhs and rhs are both error sets and are resolved. 36729 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { 36730 const mod = sema.mod; 36731 const arena = sema.arena; 36732 const lhs_names = lhs.errorSetNames(mod); 36733 const rhs_names = rhs.errorSetNames(mod); 36734 var names: Module.Fn.InferredErrorSet.NameMap = .{}; 36735 try names.ensureUnusedCapacity(arena, lhs_names.len); 36736 36737 for (lhs_names) |name| { 36738 names.putAssumeCapacityNoClobber(name, {}); 36739 } 36740 for (rhs_names) |name| { 36741 try names.put(arena, name, {}); 36742 } 36743 36744 return mod.errorSetFromUnsortedNames(names.keys()); 36745 } 36746 36747 /// Avoids crashing the compiler when asking if inferred allocations are noreturn. 36748 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool { 36749 if (ref == .unreachable_value) return true; 36750 if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) { 36751 .inferred_alloc, .inferred_alloc_comptime => return false, 36752 else => {}, 36753 }; 36754 return sema.typeOf(ref).isNoReturn(sema.mod); 36755 } 36756 36757 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type. 36758 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool { 36759 if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) { 36760 .inferred_alloc, .inferred_alloc_comptime => return false, 36761 else => {}, 36762 }; 36763 return sema.typeOf(ref).zigTypeTag(sema.mod) == tag; 36764 }