blob 0681de4a (1566898B) - 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 const is_comptime = field_val != .none; 17297 const opt_default_val = if (is_comptime) field_val.toValue() else null; 17298 const default_val_ptr = try sema.optRefValue(block, field_ty.toType(), opt_default_val); 17299 const struct_field_fields = .{ 17300 // name: []const u8, 17301 name_val, 17302 // type: type, 17303 field_ty, 17304 // default_value: ?*const anyopaque, 17305 default_val_ptr.toIntern(), 17306 // is_comptime: bool, 17307 Value.makeBool(is_comptime).toIntern(), 17308 // alignment: comptime_int, 17309 (try mod.intValue(Type.comptime_int, field_ty.toType().abiAlignment(mod))).toIntern(), 17310 }; 17311 struct_field_val.* = try mod.intern(.{ .aggregate = .{ 17312 .ty = struct_field_ty.toIntern(), 17313 .storage = .{ .elems = &struct_field_fields }, 17314 } }); 17315 } 17316 break :fv; 17317 }, 17318 .struct_type => |s| s, 17319 else => unreachable, 17320 }; 17321 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse break :fv; 17322 struct_field_vals = try gpa.alloc(InternPool.Index, struct_obj.fields.count()); 17323 17324 for ( 17325 struct_field_vals, 17326 struct_obj.fields.keys(), 17327 struct_obj.fields.values(), 17328 ) |*field_val, name_nts, field| { 17329 // TODO: write something like getCoercedInts to avoid needing to dupe 17330 const name = try sema.arena.dupe(u8, ip.stringToSlice(name_nts)); 17331 const name_val = v: { 17332 var anon_decl = try block.startAnonDecl(); 17333 defer anon_decl.deinit(); 17334 const new_decl_ty = try mod.arrayType(.{ 17335 .len = name.len, 17336 .child = .u8_type, 17337 }); 17338 const new_decl = try anon_decl.finish( 17339 new_decl_ty, 17340 (try mod.intern(.{ .aggregate = .{ 17341 .ty = new_decl_ty.toIntern(), 17342 .storage = .{ .bytes = name }, 17343 } })).toValue(), 17344 .none, // default alignment 17345 ); 17346 break :v try mod.intern(.{ .ptr = .{ 17347 .ty = .slice_const_u8_type, 17348 .addr = .{ .decl = new_decl }, 17349 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17350 } }); 17351 }; 17352 17353 const opt_default_val = if (field.default_val == .none) 17354 null 17355 else 17356 field.default_val.toValue(); 17357 const default_val_ptr = try sema.optRefValue(block, field.ty, opt_default_val); 17358 const alignment = field.alignment(mod, layout); 17359 17360 const struct_field_fields = .{ 17361 // name: []const u8, 17362 name_val, 17363 // type: type, 17364 field.ty.toIntern(), 17365 // default_value: ?*const anyopaque, 17366 default_val_ptr.toIntern(), 17367 // is_comptime: bool, 17368 Value.makeBool(field.is_comptime).toIntern(), 17369 // alignment: comptime_int, 17370 (try mod.intValue(Type.comptime_int, alignment)).toIntern(), 17371 }; 17372 field_val.* = try mod.intern(.{ .aggregate = .{ 17373 .ty = struct_field_ty.toIntern(), 17374 .storage = .{ .elems = &struct_field_fields }, 17375 } }); 17376 } 17377 } 17378 17379 const fields_val = v: { 17380 const array_fields_ty = try mod.arrayType(.{ 17381 .len = struct_field_vals.len, 17382 .child = struct_field_ty.toIntern(), 17383 }); 17384 const new_decl = try fields_anon_decl.finish( 17385 array_fields_ty, 17386 (try mod.intern(.{ .aggregate = .{ 17387 .ty = array_fields_ty.toIntern(), 17388 .storage = .{ .elems = struct_field_vals }, 17389 } })).toValue(), 17390 .none, // default alignment 17391 ); 17392 break :v try mod.intern(.{ .ptr = .{ 17393 .ty = (try mod.ptrType(.{ 17394 .child = struct_field_ty.toIntern(), 17395 .flags = .{ 17396 .size = .Slice, 17397 .is_const = true, 17398 }, 17399 })).toIntern(), 17400 .addr = .{ .decl = new_decl }, 17401 .len = (try mod.intValue(Type.usize, struct_field_vals.len)).toIntern(), 17402 } }); 17403 }; 17404 17405 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespaceIndex(mod)); 17406 17407 const backing_integer_val = try mod.intern(.{ .opt = .{ 17408 .ty = (try mod.optionalType(.type_type)).toIntern(), 17409 .val = if (layout == .Packed) val: { 17410 const struct_obj = mod.typeToStruct(struct_ty).?; 17411 assert(struct_obj.haveLayout()); 17412 assert(struct_obj.backing_int_ty.isInt(mod)); 17413 break :val struct_obj.backing_int_ty.toIntern(); 17414 } else .none, 17415 } }); 17416 17417 const container_layout_ty = t: { 17418 const decl_index = (try sema.namespaceLookup( 17419 block, 17420 src, 17421 (try sema.getBuiltinType("Type")).getNamespaceIndex(mod).unwrap().?, 17422 try ip.getOrPutString(gpa, "ContainerLayout"), 17423 )).?; 17424 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 17425 try sema.ensureDeclAnalyzed(decl_index); 17426 const decl = mod.declPtr(decl_index); 17427 break :t decl.val.toType(); 17428 }; 17429 17430 const field_values = [_]InternPool.Index{ 17431 // layout: ContainerLayout, 17432 (try mod.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(), 17433 // backing_integer: ?type, 17434 backing_integer_val, 17435 // fields: []const StructField, 17436 fields_val, 17437 // decls: []const Declaration, 17438 decls_val, 17439 // is_tuple: bool, 17440 Value.makeBool(struct_ty.isTuple(mod)).toIntern(), 17441 }; 17442 return sema.addConstant((try mod.intern(.{ .un = .{ 17443 .ty = type_info_ty.toIntern(), 17444 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Struct))).toIntern(), 17445 .val = try mod.intern(.{ .aggregate = .{ 17446 .ty = type_struct_ty.toIntern(), 17447 .storage = .{ .elems = &field_values }, 17448 } }), 17449 } })).toValue()); 17450 }, 17451 .Opaque => { 17452 // TODO: look into memoizing this result. 17453 17454 const type_opaque_ty = t: { 17455 const type_opaque_ty_decl_index = (try sema.namespaceLookup( 17456 block, 17457 src, 17458 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17459 try ip.getOrPutString(gpa, "Opaque"), 17460 )).?; 17461 try mod.declareDeclDependency(sema.owner_decl_index, type_opaque_ty_decl_index); 17462 try sema.ensureDeclAnalyzed(type_opaque_ty_decl_index); 17463 const type_opaque_ty_decl = mod.declPtr(type_opaque_ty_decl_index); 17464 break :t type_opaque_ty_decl.val.toType(); 17465 }; 17466 17467 const opaque_ty = try sema.resolveTypeFields(ty); 17468 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespaceIndex(mod)); 17469 17470 const field_values = .{ 17471 // decls: []const Declaration, 17472 decls_val, 17473 }; 17474 return sema.addConstant((try mod.intern(.{ .un = .{ 17475 .ty = type_info_ty.toIntern(), 17476 .tag = (try mod.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.Opaque))).toIntern(), 17477 .val = try mod.intern(.{ .aggregate = .{ 17478 .ty = type_opaque_ty.toIntern(), 17479 .storage = .{ .elems = &field_values }, 17480 } }), 17481 } })).toValue()); 17482 }, 17483 .Frame => return sema.failWithUseOfAsync(block, src), 17484 .AnyFrame => return sema.failWithUseOfAsync(block, src), 17485 } 17486 } 17487 17488 fn typeInfoDecls( 17489 sema: *Sema, 17490 block: *Block, 17491 src: LazySrcLoc, 17492 type_info_ty: Type, 17493 opt_namespace: Module.Namespace.OptionalIndex, 17494 ) CompileError!InternPool.Index { 17495 const mod = sema.mod; 17496 const gpa = sema.gpa; 17497 17498 var decls_anon_decl = try block.startAnonDecl(); 17499 defer decls_anon_decl.deinit(); 17500 17501 const declaration_ty = t: { 17502 const declaration_ty_decl_index = (try sema.namespaceLookup( 17503 block, 17504 src, 17505 type_info_ty.getNamespaceIndex(mod).unwrap().?, 17506 try mod.intern_pool.getOrPutString(gpa, "Declaration"), 17507 )).?; 17508 try mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index); 17509 try sema.ensureDeclAnalyzed(declaration_ty_decl_index); 17510 const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index); 17511 break :t declaration_ty_decl.val.toType(); 17512 }; 17513 try sema.queueFullTypeResolution(declaration_ty); 17514 17515 var decl_vals = std.ArrayList(InternPool.Index).init(gpa); 17516 defer decl_vals.deinit(); 17517 17518 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); 17519 defer seen_namespaces.deinit(); 17520 17521 if (opt_namespace.unwrap()) |namespace_index| { 17522 const namespace = mod.namespacePtr(namespace_index); 17523 try sema.typeInfoNamespaceDecls(block, namespace, declaration_ty, &decl_vals, &seen_namespaces); 17524 } 17525 17526 const array_decl_ty = try mod.arrayType(.{ 17527 .len = decl_vals.items.len, 17528 .child = declaration_ty.toIntern(), 17529 }); 17530 const new_decl = try decls_anon_decl.finish( 17531 array_decl_ty, 17532 (try mod.intern(.{ .aggregate = .{ 17533 .ty = array_decl_ty.toIntern(), 17534 .storage = .{ .elems = decl_vals.items }, 17535 } })).toValue(), 17536 .none, // default alignment 17537 ); 17538 return try mod.intern(.{ .ptr = .{ 17539 .ty = (try mod.ptrType(.{ 17540 .child = declaration_ty.toIntern(), 17541 .flags = .{ 17542 .size = .Slice, 17543 .is_const = true, 17544 }, 17545 })).toIntern(), 17546 .addr = .{ .decl = new_decl }, 17547 .len = (try mod.intValue(Type.usize, decl_vals.items.len)).toIntern(), 17548 } }); 17549 } 17550 17551 fn typeInfoNamespaceDecls( 17552 sema: *Sema, 17553 block: *Block, 17554 namespace: *Namespace, 17555 declaration_ty: Type, 17556 decl_vals: *std.ArrayList(InternPool.Index), 17557 seen_namespaces: *std.AutoHashMap(*Namespace, void), 17558 ) !void { 17559 const mod = sema.mod; 17560 const ip = &mod.intern_pool; 17561 const gop = try seen_namespaces.getOrPut(namespace); 17562 if (gop.found_existing) return; 17563 const decls = namespace.decls.keys(); 17564 for (decls) |decl_index| { 17565 const decl = mod.declPtr(decl_index); 17566 if (decl.kind == .@"usingnamespace") { 17567 if (decl.analysis == .in_progress) continue; 17568 try mod.ensureDeclAnalyzed(decl_index); 17569 const new_ns = decl.val.toType().getNamespace(mod).?; 17570 try sema.typeInfoNamespaceDecls(block, new_ns, declaration_ty, decl_vals, seen_namespaces); 17571 continue; 17572 } 17573 if (decl.kind != .named) continue; 17574 const name_val = v: { 17575 var anon_decl = try block.startAnonDecl(); 17576 defer anon_decl.deinit(); 17577 // TODO: write something like getCoercedInts to avoid needing to dupe 17578 const name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name)); 17579 const new_decl_ty = try mod.arrayType(.{ 17580 .len = name.len, 17581 .child = .u8_type, 17582 }); 17583 const new_decl = try anon_decl.finish( 17584 new_decl_ty, 17585 (try mod.intern(.{ .aggregate = .{ 17586 .ty = new_decl_ty.toIntern(), 17587 .storage = .{ .bytes = name }, 17588 } })).toValue(), 17589 .none, // default alignment 17590 ); 17591 break :v try mod.intern(.{ .ptr = .{ 17592 .ty = .slice_const_u8_type, 17593 .addr = .{ .decl = new_decl }, 17594 .len = (try mod.intValue(Type.usize, name.len)).toIntern(), 17595 } }); 17596 }; 17597 17598 const fields = .{ 17599 //name: []const u8, 17600 name_val, 17601 //is_pub: bool, 17602 Value.makeBool(decl.is_pub).toIntern(), 17603 }; 17604 try decl_vals.append(try mod.intern(.{ .aggregate = .{ 17605 .ty = declaration_ty.toIntern(), 17606 .storage = .{ .elems = &fields }, 17607 } })); 17608 } 17609 } 17610 17611 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17612 _ = block; 17613 const zir_datas = sema.code.instructions.items(.data); 17614 const inst_data = zir_datas[inst].un_node; 17615 const operand = try sema.resolveInst(inst_data.operand); 17616 const operand_ty = sema.typeOf(operand); 17617 return sema.addType(operand_ty); 17618 } 17619 17620 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17621 const pl_node = sema.code.instructions.items(.data)[inst].pl_node; 17622 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); 17623 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 17624 17625 var child_block: Block = .{ 17626 .parent = block, 17627 .sema = sema, 17628 .src_decl = block.src_decl, 17629 .namespace = block.namespace, 17630 .wip_capture_scope = block.wip_capture_scope, 17631 .instructions = .{}, 17632 .inlining = block.inlining, 17633 .is_comptime = false, 17634 .is_typeof = true, 17635 .want_safety = false, 17636 .error_return_trace_index = block.error_return_trace_index, 17637 }; 17638 defer child_block.instructions.deinit(sema.gpa); 17639 17640 const operand = try sema.resolveBody(&child_block, body, inst); 17641 const operand_ty = sema.typeOf(operand); 17642 if (operand_ty.isGenericPoison()) return error.GenericPoison; 17643 return sema.addType(operand_ty); 17644 } 17645 17646 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17647 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17648 const src = inst_data.src(); 17649 const operand = try sema.resolveInst(inst_data.operand); 17650 const operand_ty = sema.typeOf(operand); 17651 const res_ty = try sema.log2IntType(block, operand_ty, src); 17652 return sema.addType(res_ty); 17653 } 17654 17655 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type { 17656 const mod = sema.mod; 17657 switch (operand.zigTypeTag(mod)) { 17658 .ComptimeInt => return Type.comptime_int, 17659 .Int => { 17660 const bits = operand.bitSize(mod); 17661 const count = if (bits == 0) 17662 0 17663 else blk: { 17664 var count: u16 = 0; 17665 var s = bits - 1; 17666 while (s != 0) : (s >>= 1) { 17667 count += 1; 17668 } 17669 break :blk count; 17670 }; 17671 return mod.intType(.unsigned, count); 17672 }, 17673 .Vector => { 17674 const elem_ty = operand.elemType2(mod); 17675 const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); 17676 return mod.vectorType(.{ 17677 .len = operand.vectorLen(mod), 17678 .child = log2_elem_ty.toIntern(), 17679 }); 17680 }, 17681 else => {}, 17682 } 17683 return sema.fail( 17684 block, 17685 src, 17686 "bit shifting operation expected integer type, found '{}'", 17687 .{operand.fmt(mod)}, 17688 ); 17689 } 17690 17691 fn zirTypeofPeer( 17692 sema: *Sema, 17693 block: *Block, 17694 extended: Zir.Inst.Extended.InstData, 17695 ) CompileError!Air.Inst.Ref { 17696 const tracy = trace(@src()); 17697 defer tracy.end(); 17698 17699 const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); 17700 const src = LazySrcLoc.nodeOffset(extra.data.src_node); 17701 const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len]; 17702 17703 var child_block: Block = .{ 17704 .parent = block, 17705 .sema = sema, 17706 .src_decl = block.src_decl, 17707 .namespace = block.namespace, 17708 .wip_capture_scope = block.wip_capture_scope, 17709 .instructions = .{}, 17710 .inlining = block.inlining, 17711 .is_comptime = false, 17712 .is_typeof = true, 17713 .runtime_cond = block.runtime_cond, 17714 .runtime_loop = block.runtime_loop, 17715 .runtime_index = block.runtime_index, 17716 }; 17717 defer child_block.instructions.deinit(sema.gpa); 17718 // Ignore the result, we only care about the instructions in `args`. 17719 _ = try sema.analyzeBodyBreak(&child_block, body); 17720 17721 const args = sema.code.refSlice(extra.end, extended.small); 17722 17723 const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len); 17724 defer sema.gpa.free(inst_list); 17725 17726 for (args, 0..) |arg_ref, i| { 17727 inst_list[i] = try sema.resolveInst(arg_ref); 17728 } 17729 17730 const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); 17731 return sema.addType(result_type); 17732 } 17733 17734 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17735 const tracy = trace(@src()); 17736 defer tracy.end(); 17737 17738 const mod = sema.mod; 17739 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17740 const src = inst_data.src(); 17741 const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; 17742 const uncasted_operand = try sema.resolveInst(inst_data.operand); 17743 17744 const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src); 17745 if (try sema.resolveMaybeUndefVal(operand)) |val| { 17746 return if (val.isUndef(mod)) 17747 sema.addConstUndef(Type.bool) 17748 else if (val.toBool()) 17749 Air.Inst.Ref.bool_false 17750 else 17751 Air.Inst.Ref.bool_true; 17752 } 17753 try sema.requireRuntimeBlock(block, src, null); 17754 return block.addTyOp(.not, Type.bool, operand); 17755 } 17756 17757 fn zirBoolBr( 17758 sema: *Sema, 17759 parent_block: *Block, 17760 inst: Zir.Inst.Index, 17761 is_bool_or: bool, 17762 ) CompileError!Air.Inst.Ref { 17763 const tracy = trace(@src()); 17764 defer tracy.end(); 17765 17766 const mod = sema.mod; 17767 const datas = sema.code.instructions.items(.data); 17768 const inst_data = datas[inst].bool_br; 17769 const lhs = try sema.resolveInst(inst_data.lhs); 17770 const lhs_src = sema.src; 17771 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); 17772 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 17773 const gpa = sema.gpa; 17774 17775 if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| { 17776 if (is_bool_or and lhs_val.toBool()) { 17777 return Air.Inst.Ref.bool_true; 17778 } else if (!is_bool_or and !lhs_val.toBool()) { 17779 return Air.Inst.Ref.bool_false; 17780 } 17781 // comptime-known left-hand side. No need for a block here; the result 17782 // is simply the rhs expression. Here we rely on there only being 1 17783 // break instruction (`break_inline`). 17784 return sema.resolveBody(parent_block, body, inst); 17785 } 17786 17787 const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 17788 try sema.air_instructions.append(gpa, .{ 17789 .tag = .block, 17790 .data = .{ .ty_pl = .{ 17791 .ty = .bool_type, 17792 .payload = undefined, 17793 } }, 17794 }); 17795 17796 var child_block = parent_block.makeSubBlock(); 17797 child_block.runtime_loop = null; 17798 child_block.runtime_cond = lhs_src; 17799 child_block.runtime_index.increment(); 17800 defer child_block.instructions.deinit(gpa); 17801 17802 var then_block = child_block.makeSubBlock(); 17803 defer then_block.instructions.deinit(gpa); 17804 17805 var else_block = child_block.makeSubBlock(); 17806 defer else_block.instructions.deinit(gpa); 17807 17808 const lhs_block = if (is_bool_or) &then_block else &else_block; 17809 const rhs_block = if (is_bool_or) &else_block else &then_block; 17810 17811 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false; 17812 _ = try lhs_block.addBr(block_inst, lhs_result); 17813 17814 const rhs_result = try sema.resolveBody(rhs_block, body, inst); 17815 if (!sema.typeOf(rhs_result).isNoReturn(mod)) { 17816 _ = try rhs_block.addBr(block_inst, rhs_result); 17817 } 17818 17819 const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst); 17820 if (!sema.typeOf(rhs_result).isNoReturn(mod)) { 17821 if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| { 17822 if (is_bool_or and rhs_val.toBool()) { 17823 return Air.Inst.Ref.bool_true; 17824 } else if (!is_bool_or and !rhs_val.toBool()) { 17825 return Air.Inst.Ref.bool_false; 17826 } 17827 } 17828 } 17829 17830 return result; 17831 } 17832 17833 fn finishCondBr( 17834 sema: *Sema, 17835 parent_block: *Block, 17836 child_block: *Block, 17837 then_block: *Block, 17838 else_block: *Block, 17839 cond: Air.Inst.Ref, 17840 block_inst: Air.Inst.Index, 17841 ) !Air.Inst.Ref { 17842 const gpa = sema.gpa; 17843 17844 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 17845 then_block.instructions.items.len + else_block.instructions.items.len + 17846 @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1); 17847 17848 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 17849 .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)), 17850 .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)), 17851 }); 17852 sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items); 17853 sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); 17854 17855 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 17856 .operand = cond, 17857 .payload = cond_br_payload, 17858 } } }); 17859 17860 sema.air_instructions.items(.data)[block_inst].ty_pl.payload = sema.addExtraAssumeCapacity( 17861 Air.Block{ .body_len = @as(u32, @intCast(child_block.instructions.items.len)) }, 17862 ); 17863 sema.air_extra.appendSliceAssumeCapacity(child_block.instructions.items); 17864 17865 try parent_block.instructions.append(gpa, block_inst); 17866 return Air.indexToRef(block_inst); 17867 } 17868 17869 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 17870 const mod = sema.mod; 17871 switch (ty.zigTypeTag(mod)) { 17872 .Optional, .Null, .Undefined => return, 17873 .Pointer => if (ty.isPtrLikeOptional(mod)) return, 17874 else => {}, 17875 } 17876 return sema.failWithExpectedOptionalType(block, src, ty); 17877 } 17878 17879 fn zirIsNonNull( 17880 sema: *Sema, 17881 block: *Block, 17882 inst: Zir.Inst.Index, 17883 ) CompileError!Air.Inst.Ref { 17884 const tracy = trace(@src()); 17885 defer tracy.end(); 17886 17887 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17888 const src = inst_data.src(); 17889 const operand = try sema.resolveInst(inst_data.operand); 17890 try sema.checkNullableType(block, src, sema.typeOf(operand)); 17891 return sema.analyzeIsNull(block, src, operand, true); 17892 } 17893 17894 fn zirIsNonNullPtr( 17895 sema: *Sema, 17896 block: *Block, 17897 inst: Zir.Inst.Index, 17898 ) CompileError!Air.Inst.Ref { 17899 const tracy = trace(@src()); 17900 defer tracy.end(); 17901 17902 const mod = sema.mod; 17903 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17904 const src = inst_data.src(); 17905 const ptr = try sema.resolveInst(inst_data.operand); 17906 try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(mod)); 17907 if ((try sema.resolveMaybeUndefVal(ptr)) == null) { 17908 return block.addUnOp(.is_non_null_ptr, ptr); 17909 } 17910 const loaded = try sema.analyzeLoad(block, src, ptr, src); 17911 return sema.analyzeIsNull(block, src, loaded, true); 17912 } 17913 17914 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 17915 const mod = sema.mod; 17916 switch (ty.zigTypeTag(mod)) { 17917 .ErrorSet, .ErrorUnion, .Undefined => return, 17918 else => return sema.fail(block, src, "expected error union type, found '{}'", .{ 17919 ty.fmt(mod), 17920 }), 17921 } 17922 } 17923 17924 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17925 const tracy = trace(@src()); 17926 defer tracy.end(); 17927 17928 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17929 const src = inst_data.src(); 17930 const operand = try sema.resolveInst(inst_data.operand); 17931 try sema.checkErrorType(block, src, sema.typeOf(operand)); 17932 return sema.analyzeIsNonErr(block, src, operand); 17933 } 17934 17935 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17936 const tracy = trace(@src()); 17937 defer tracy.end(); 17938 17939 const mod = sema.mod; 17940 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17941 const src = inst_data.src(); 17942 const ptr = try sema.resolveInst(inst_data.operand); 17943 try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(mod)); 17944 const loaded = try sema.analyzeLoad(block, src, ptr, src); 17945 return sema.analyzeIsNonErr(block, src, loaded); 17946 } 17947 17948 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 17949 const tracy = trace(@src()); 17950 defer tracy.end(); 17951 17952 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 17953 const src = inst_data.src(); 17954 const operand = try sema.resolveInst(inst_data.operand); 17955 return sema.analyzeIsNonErr(block, src, operand); 17956 } 17957 17958 fn zirCondbr( 17959 sema: *Sema, 17960 parent_block: *Block, 17961 inst: Zir.Inst.Index, 17962 ) CompileError!Zir.Inst.Index { 17963 const tracy = trace(@src()); 17964 defer tracy.end(); 17965 17966 const mod = sema.mod; 17967 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 17968 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; 17969 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); 17970 17971 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; 17972 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; 17973 17974 const uncasted_cond = try sema.resolveInst(extra.data.condition); 17975 const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src); 17976 17977 if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| { 17978 const body = if (cond_val.toBool()) then_body else else_body; 17979 17980 try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src); 17981 // We use `analyzeBodyInner` since we want to propagate any possible 17982 // `error.ComptimeBreak` to the caller. 17983 return sema.analyzeBodyInner(parent_block, body); 17984 } 17985 17986 const gpa = sema.gpa; 17987 17988 // We'll re-use the sub block to save on memory bandwidth, and yank out the 17989 // instructions array in between using it for the then block and else block. 17990 var sub_block = parent_block.makeSubBlock(); 17991 sub_block.runtime_loop = null; 17992 sub_block.runtime_cond = cond_src; 17993 sub_block.runtime_index.increment(); 17994 defer sub_block.instructions.deinit(gpa); 17995 17996 try sema.analyzeBodyRuntimeBreak(&sub_block, then_body); 17997 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); 17998 defer gpa.free(true_instructions); 17999 18000 const err_cond = blk: { 18001 const index = Zir.refToIndex(extra.data.condition) orelse break :blk null; 18002 if (sema.code.instructions.items(.tag)[index] != .is_non_err) break :blk null; 18003 18004 const err_inst_data = sema.code.instructions.items(.data)[index].un_node; 18005 const err_operand = try sema.resolveInst(err_inst_data.operand); 18006 const operand_ty = sema.typeOf(err_operand); 18007 assert(operand_ty.zigTypeTag(mod) == .ErrorUnion); 18008 const result_ty = operand_ty.errorUnionSet(mod); 18009 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); 18010 }; 18011 18012 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) { 18013 // nothing to do 18014 } else { 18015 try sema.analyzeBodyRuntimeBreak(&sub_block, else_body); 18016 } 18017 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 18018 true_instructions.len + sub_block.instructions.items.len); 18019 _ = try parent_block.addInst(.{ 18020 .tag = .cond_br, 18021 .data = .{ .pl_op = .{ 18022 .operand = cond, 18023 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18024 .then_body_len = @as(u32, @intCast(true_instructions.len)), 18025 .else_body_len = @as(u32, @intCast(sub_block.instructions.items.len)), 18026 }), 18027 } }, 18028 }); 18029 sema.air_extra.appendSliceAssumeCapacity(true_instructions); 18030 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); 18031 return always_noreturn; 18032 } 18033 18034 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18035 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 18036 const src = inst_data.src(); 18037 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 18038 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18039 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 18040 const err_union = try sema.resolveInst(extra.data.operand); 18041 const err_union_ty = sema.typeOf(err_union); 18042 const mod = sema.mod; 18043 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 18044 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ 18045 err_union_ty.fmt(mod), 18046 }); 18047 } 18048 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18049 if (is_non_err != .none) { 18050 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18051 if (is_non_err_val.toBool()) { 18052 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); 18053 } 18054 // We can analyze the body directly in the parent block because we know there are 18055 // no breaks from the body possible, and that the body is noreturn. 18056 return sema.resolveBody(parent_block, body, inst); 18057 } 18058 18059 var sub_block = parent_block.makeSubBlock(); 18060 defer sub_block.instructions.deinit(sema.gpa); 18061 18062 // This body is guaranteed to end with noreturn and has no breaks. 18063 _ = try sema.analyzeBodyInner(&sub_block, body); 18064 18065 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + 18066 sub_block.instructions.items.len); 18067 const try_inst = try parent_block.addInst(.{ 18068 .tag = .@"try", 18069 .data = .{ .pl_op = .{ 18070 .operand = err_union, 18071 .payload = sema.addExtraAssumeCapacity(Air.Try{ 18072 .body_len = @as(u32, @intCast(sub_block.instructions.items.len)), 18073 }), 18074 } }, 18075 }); 18076 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); 18077 return try_inst; 18078 } 18079 18080 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18081 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 18082 const src = inst_data.src(); 18083 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 18084 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); 18085 const body = sema.code.extra[extra.end..][0..extra.data.body_len]; 18086 const operand = try sema.resolveInst(extra.data.operand); 18087 const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); 18088 const err_union_ty = sema.typeOf(err_union); 18089 const mod = sema.mod; 18090 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) { 18091 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ 18092 err_union_ty.fmt(mod), 18093 }); 18094 } 18095 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); 18096 if (is_non_err != .none) { 18097 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; 18098 if (is_non_err_val.toBool()) { 18099 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); 18100 } 18101 // We can analyze the body directly in the parent block because we know there are 18102 // no breaks from the body possible, and that the body is noreturn. 18103 return sema.resolveBody(parent_block, body, inst); 18104 } 18105 18106 var sub_block = parent_block.makeSubBlock(); 18107 defer sub_block.instructions.deinit(sema.gpa); 18108 18109 // This body is guaranteed to end with noreturn and has no breaks. 18110 _ = try sema.analyzeBodyInner(&sub_block, body); 18111 18112 const operand_ty = sema.typeOf(operand); 18113 const ptr_info = operand_ty.ptrInfo(mod); 18114 const res_ty = try mod.ptrType(.{ 18115 .child = err_union_ty.errorUnionPayload(mod).toIntern(), 18116 .flags = .{ 18117 .is_const = ptr_info.flags.is_const, 18118 .is_volatile = ptr_info.flags.is_volatile, 18119 .is_allowzero = ptr_info.flags.is_allowzero, 18120 .address_space = ptr_info.flags.address_space, 18121 }, 18122 }); 18123 const res_ty_ref = try sema.addType(res_ty); 18124 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len + 18125 sub_block.instructions.items.len); 18126 const try_inst = try parent_block.addInst(.{ 18127 .tag = .try_ptr, 18128 .data = .{ .ty_pl = .{ 18129 .ty = res_ty_ref, 18130 .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ 18131 .ptr = operand, 18132 .body_len = @as(u32, @intCast(sub_block.instructions.items.len)), 18133 }), 18134 } }, 18135 }); 18136 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); 18137 return try_inst; 18138 } 18139 18140 // A `break` statement is inside a runtime condition, but trying to 18141 // break from an inline loop. In such case we must convert it to 18142 // a runtime break. 18143 fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void { 18144 const gop = sema.inst_map.getOrPutAssumeCapacity(break_data.block_inst); 18145 const labeled_block = if (!gop.found_existing) blk: { 18146 try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); 18147 18148 const new_block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 18149 gop.value_ptr.* = Air.indexToRef(new_block_inst); 18150 try sema.air_instructions.append(sema.gpa, .{ 18151 .tag = .block, 18152 .data = undefined, 18153 }); 18154 const labeled_block = try sema.gpa.create(LabeledBlock); 18155 labeled_block.* = .{ 18156 .label = .{ 18157 .zir_block = break_data.block_inst, 18158 .merges = .{ 18159 .src_locs = .{}, 18160 .results = .{}, 18161 .br_list = .{}, 18162 .block_inst = new_block_inst, 18163 }, 18164 }, 18165 .block = .{ 18166 .parent = child_block, 18167 .sema = sema, 18168 .src_decl = child_block.src_decl, 18169 .namespace = child_block.namespace, 18170 .wip_capture_scope = child_block.wip_capture_scope, 18171 .instructions = .{}, 18172 .label = &labeled_block.label, 18173 .inlining = child_block.inlining, 18174 .is_comptime = child_block.is_comptime, 18175 }, 18176 }; 18177 sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); 18178 break :blk labeled_block; 18179 } else blk: { 18180 const new_block_inst = Air.refToIndex(gop.value_ptr.*).?; 18181 const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?; 18182 break :blk labeled_block; 18183 }; 18184 18185 const operand = try sema.resolveInst(break_data.operand); 18186 const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); 18187 try labeled_block.label.merges.results.append(sema.gpa, operand); 18188 try labeled_block.label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?); 18189 labeled_block.block.runtime_index.increment(); 18190 if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { 18191 labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; 18192 labeled_block.block.runtime_loop = child_block.runtime_loop; 18193 } 18194 } 18195 18196 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18197 const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable"; 18198 const src = inst_data.src(); 18199 18200 if (block.is_comptime) { 18201 return sema.fail(block, src, "reached unreachable code", .{}); 18202 } 18203 // TODO Add compile error for @optimizeFor occurring too late in a scope. 18204 try block.addUnreachable(true); 18205 return always_noreturn; 18206 } 18207 18208 fn zirRetErrValue( 18209 sema: *Sema, 18210 block: *Block, 18211 inst: Zir.Inst.Index, 18212 ) CompileError!Zir.Inst.Index { 18213 const mod = sema.mod; 18214 const inst_data = sema.code.instructions.items(.data)[inst].str_tok; 18215 const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); 18216 _ = try mod.getErrorValue(err_name); 18217 const src = inst_data.src(); 18218 // Return the error code from the function. 18219 const error_set_type = try mod.singleErrorSetType(err_name); 18220 const result_inst = try sema.addConstant((try mod.intern(.{ .err = .{ 18221 .ty = error_set_type.toIntern(), 18222 .name = err_name, 18223 } })).toValue()); 18224 return sema.analyzeRet(block, result_inst, src); 18225 } 18226 18227 fn zirRetImplicit( 18228 sema: *Sema, 18229 block: *Block, 18230 inst: Zir.Inst.Index, 18231 ) CompileError!Zir.Inst.Index { 18232 const tracy = trace(@src()); 18233 defer tracy.end(); 18234 18235 const mod = sema.mod; 18236 const inst_data = sema.code.instructions.items(.data)[inst].un_tok; 18237 const operand = try sema.resolveInst(inst_data.operand); 18238 18239 const r_brace_src = inst_data.src(); 18240 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 18241 const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod); 18242 if (base_tag == .NoReturn) { 18243 const msg = msg: { 18244 const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{ 18245 sema.fn_ret_ty.fmt(mod), 18246 }); 18247 errdefer msg.destroy(sema.gpa); 18248 try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); 18249 break :msg msg; 18250 }; 18251 return sema.failWithOwnedErrorMsg(msg); 18252 } else if (base_tag != .Void) { 18253 const msg = msg: { 18254 const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{ 18255 sema.fn_ret_ty.fmt(mod), 18256 }); 18257 errdefer msg.destroy(sema.gpa); 18258 try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); 18259 break :msg msg; 18260 }; 18261 return sema.failWithOwnedErrorMsg(msg); 18262 } 18263 18264 return sema.analyzeRet(block, operand, .unneeded); 18265 } 18266 18267 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18268 const tracy = trace(@src()); 18269 defer tracy.end(); 18270 18271 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 18272 const operand = try sema.resolveInst(inst_data.operand); 18273 const src = inst_data.src(); 18274 18275 return sema.analyzeRet(block, operand, src); 18276 } 18277 18278 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { 18279 const tracy = trace(@src()); 18280 defer tracy.end(); 18281 18282 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 18283 const src = inst_data.src(); 18284 const ret_ptr = try sema.resolveInst(inst_data.operand); 18285 18286 if (block.is_comptime or block.inlining != null) { 18287 const operand = try sema.analyzeLoad(block, src, ret_ptr, src); 18288 return sema.analyzeRet(block, operand, src); 18289 } 18290 18291 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18292 const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); 18293 return sema.retWithErrTracing(block, is_non_err, .ret_load, ret_ptr); 18294 } 18295 18296 _ = try block.addUnOp(.ret_load, ret_ptr); 18297 return always_noreturn; 18298 } 18299 18300 fn retWithErrTracing( 18301 sema: *Sema, 18302 block: *Block, 18303 is_non_err: Air.Inst.Ref, 18304 ret_tag: Air.Inst.Tag, 18305 operand: Air.Inst.Ref, 18306 ) CompileError!Zir.Inst.Index { 18307 const mod = sema.mod; 18308 const need_check = switch (is_non_err) { 18309 .bool_true => { 18310 _ = try block.addUnOp(ret_tag, operand); 18311 return always_noreturn; 18312 }, 18313 .bool_false => false, 18314 else => true, 18315 }; 18316 const gpa = sema.gpa; 18317 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 18318 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 18319 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 18320 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); 18321 const return_err_fn = try sema.getBuiltin("returnError"); 18322 const args: [1]Air.Inst.Ref = .{err_return_trace}; 18323 18324 if (!need_check) { 18325 try sema.callBuiltin(block, return_err_fn, .never_inline, &args); 18326 _ = try block.addUnOp(ret_tag, operand); 18327 return always_noreturn; 18328 } 18329 18330 var then_block = block.makeSubBlock(); 18331 defer then_block.instructions.deinit(gpa); 18332 _ = try then_block.addUnOp(ret_tag, operand); 18333 18334 var else_block = block.makeSubBlock(); 18335 defer else_block.instructions.deinit(gpa); 18336 try sema.callBuiltin(&else_block, return_err_fn, .never_inline, &args); 18337 _ = try else_block.addUnOp(ret_tag, operand); 18338 18339 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + 18340 then_block.instructions.items.len + else_block.instructions.items.len + 18341 @typeInfo(Air.Block).Struct.fields.len + 1); 18342 18343 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ 18344 .then_body_len = @as(u32, @intCast(then_block.instructions.items.len)), 18345 .else_body_len = @as(u32, @intCast(else_block.instructions.items.len)), 18346 }); 18347 sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items); 18348 sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); 18349 18350 _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ 18351 .operand = is_non_err, 18352 .payload = cond_br_payload, 18353 } } }); 18354 18355 return always_noreturn; 18356 } 18357 18358 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { 18359 const mod = sema.mod; 18360 if (!mod.backendSupportsFeature(.error_return_trace)) return false; 18361 18362 return fn_ret_ty.isError(mod) and 18363 mod.comp.bin_file.options.error_return_tracing; 18364 } 18365 18366 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 18367 const mod = sema.mod; 18368 const inst_data = sema.code.instructions.items(.data)[inst].save_err_ret_index; 18369 18370 if (!mod.backendSupportsFeature(.error_return_trace)) return; 18371 if (!mod.comp.bin_file.options.error_return_tracing) return; 18372 18373 // This is only relevant at runtime. 18374 if (block.is_comptime or block.is_typeof) return; 18375 18376 const save_index = inst_data.operand == .none or b: { 18377 const operand = try sema.resolveInst(inst_data.operand); 18378 const operand_ty = sema.typeOf(operand); 18379 break :b operand_ty.isError(mod); 18380 }; 18381 18382 if (save_index) 18383 block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block); 18384 } 18385 18386 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { 18387 const inst_data = sema.code.instructions.items(.data)[inst].restore_err_ret_index; 18388 const src = sema.src; // TODO 18389 18390 // This is only relevant at runtime. 18391 if (start_block.is_comptime or start_block.is_typeof) return; 18392 18393 if (!sema.mod.backendSupportsFeature(.error_return_trace)) return; 18394 if (!sema.owner_func.?.calls_or_awaits_errorable_fn) return; 18395 if (!sema.mod.comp.bin_file.options.error_return_tracing) return; 18396 18397 const tracy = trace(@src()); 18398 defer tracy.end(); 18399 18400 const saved_index = if (Zir.refToIndexAllowNone(inst_data.block)) |zir_block| b: { 18401 var block = start_block; 18402 while (true) { 18403 if (block.label) |label| { 18404 if (label.zir_block == zir_block) { 18405 const target_trace_index = if (block.parent) |parent_block| tgt: { 18406 break :tgt parent_block.error_return_trace_index; 18407 } else sema.error_return_trace_index_on_fn_entry; 18408 18409 if (start_block.error_return_trace_index != target_trace_index) 18410 break :b target_trace_index; 18411 18412 return; // No need to restore 18413 } 18414 } 18415 block = block.parent.?; 18416 } 18417 } else b: { 18418 if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry) 18419 break :b sema.error_return_trace_index_on_fn_entry; 18420 18421 return; // No need to restore 18422 }; 18423 18424 assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere 18425 18426 const operand = try sema.resolveInstAllowNone(inst_data.operand); 18427 return sema.popErrorReturnTrace(start_block, src, operand, saved_index); 18428 } 18429 18430 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { 18431 const mod = sema.mod; 18432 const gpa = sema.gpa; 18433 const ip = &mod.intern_pool; 18434 assert(sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion); 18435 18436 if (mod.typeToInferredErrorSet(sema.fn_ret_ty.errorUnionSet(mod))) |ies| { 18437 const op_ty = sema.typeOf(uncasted_operand); 18438 switch (op_ty.zigTypeTag(mod)) { 18439 .ErrorSet => try ies.addErrorSet(op_ty, ip, gpa), 18440 .ErrorUnion => try ies.addErrorSet(op_ty.errorUnionSet(mod), ip, gpa), 18441 else => {}, 18442 } 18443 } 18444 } 18445 18446 fn analyzeRet( 18447 sema: *Sema, 18448 block: *Block, 18449 uncasted_operand: Air.Inst.Ref, 18450 src: LazySrcLoc, 18451 ) CompileError!Zir.Inst.Index { 18452 // Special case for returning an error to an inferred error set; we need to 18453 // add the error tag to the inferred error set of the in-scope function, so 18454 // that the coercion below works correctly. 18455 const mod = sema.mod; 18456 if (sema.fn_ret_ty.zigTypeTag(mod) == .ErrorUnion) { 18457 try sema.addToInferredErrorSet(uncasted_operand); 18458 } 18459 const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) { 18460 error.NotCoercible => unreachable, 18461 else => |e| return e, 18462 }; 18463 18464 if (block.inlining) |inlining| { 18465 if (block.is_comptime) { 18466 _ = try sema.resolveConstMaybeUndefVal(block, src, operand, "value being returned at comptime must be comptime-known"); 18467 inlining.comptime_result = operand; 18468 return error.ComptimeReturn; 18469 } 18470 // We are inlining a function call; rewrite the `ret` as a `break`. 18471 try inlining.merges.results.append(sema.gpa, operand); 18472 _ = try block.addBr(inlining.merges.block_inst, operand); 18473 return always_noreturn; 18474 } else if (block.is_comptime) { 18475 return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{}); 18476 } 18477 18478 try sema.resolveTypeLayout(sema.fn_ret_ty); 18479 18480 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) { 18481 // Avoid adding a frame to the error return trace in case the value is comptime-known 18482 // to be not an error. 18483 const is_non_err = try sema.analyzeIsNonErr(block, src, operand); 18484 return sema.retWithErrTracing(block, is_non_err, .ret, operand); 18485 } 18486 18487 _ = try block.addUnOp(.ret, operand); 18488 18489 return always_noreturn; 18490 } 18491 18492 fn floatOpAllowed(tag: Zir.Inst.Tag) bool { 18493 // extend this swich as additional operators are implemented 18494 return switch (tag) { 18495 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true, 18496 else => false, 18497 }; 18498 } 18499 18500 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18501 const tracy = trace(@src()); 18502 defer tracy.end(); 18503 18504 const mod = sema.mod; 18505 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; 18506 const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); 18507 const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node }; 18508 const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node }; 18509 const align_src: LazySrcLoc = .{ .node_offset_ptr_align = extra.data.src_node }; 18510 const addrspace_src: LazySrcLoc = .{ .node_offset_ptr_addrspace = extra.data.src_node }; 18511 const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node }; 18512 const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node }; 18513 18514 const elem_ty = blk: { 18515 const air_inst = try sema.resolveInst(extra.data.elem_type); 18516 const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| { 18517 if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(mod)) { 18518 try sema.errNote(block, elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{}); 18519 } 18520 return err; 18521 }; 18522 if (ty.isGenericPoison()) return error.GenericPoison; 18523 break :blk ty; 18524 }; 18525 18526 if (elem_ty.zigTypeTag(mod) == .NoReturn) 18527 return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); 18528 18529 const target = mod.getTarget(); 18530 18531 var extra_i = extra.end; 18532 18533 const sentinel = if (inst_data.flags.has_sentinel) blk: { 18534 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18535 extra_i += 1; 18536 const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src); 18537 const val = try sema.resolveConstValue(block, sentinel_src, coerced, "pointer sentinel value must be comptime-known"); 18538 break :blk val.toIntern(); 18539 } else .none; 18540 18541 const abi_align: Alignment = if (inst_data.flags.has_align) blk: { 18542 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18543 extra_i += 1; 18544 const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src); 18545 const val = try sema.resolveConstValue(block, align_src, coerced, "pointer alignment must be comptime-known"); 18546 // Check if this happens to be the lazy alignment of our element type, in 18547 // which case we can make this 0 without resolving it. 18548 switch (mod.intern_pool.indexToKey(val.toIntern())) { 18549 .int => |int| switch (int.storage) { 18550 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none, 18551 else => {}, 18552 }, 18553 else => {}, 18554 } 18555 const abi_align = @as(u32, @intCast((try val.getUnsignedIntAdvanced(mod, sema)).?)); 18556 try sema.validateAlign(block, align_src, abi_align); 18557 break :blk Alignment.fromByteUnits(abi_align); 18558 } else .none; 18559 18560 const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: { 18561 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18562 extra_i += 1; 18563 break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer); 18564 } else if (elem_ty.zigTypeTag(mod) == .Fn and target.cpu.arch == .avr) .flash else .generic; 18565 18566 const bit_offset = if (inst_data.flags.has_bit_range) blk: { 18567 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18568 extra_i += 1; 18569 const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16, "pointer bit-offset must be comptime-known"); 18570 break :blk @as(u16, @intCast(bit_offset)); 18571 } else 0; 18572 18573 const host_size: u16 = if (inst_data.flags.has_bit_range) blk: { 18574 const ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_i])); 18575 extra_i += 1; 18576 const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16, "pointer host size must be comptime-known"); 18577 break :blk @as(u16, @intCast(host_size)); 18578 } else 0; 18579 18580 if (host_size != 0 and bit_offset >= host_size * 8) { 18581 return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{}); 18582 } 18583 18584 if (elem_ty.zigTypeTag(mod) == .Fn) { 18585 if (inst_data.size != .One) { 18586 return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); 18587 } 18588 const fn_align = mod.typeToFunc(elem_ty).?.alignment; 18589 if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and 18590 abi_align != fn_align) 18591 { 18592 return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); 18593 } 18594 } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { 18595 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); 18596 } else if (inst_data.size == .C) { 18597 if (!try sema.validateExternType(elem_ty, .other)) { 18598 const msg = msg: { 18599 const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)}); 18600 errdefer msg.destroy(sema.gpa); 18601 18602 const src_decl = mod.declPtr(block.src_decl); 18603 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other); 18604 18605 try sema.addDeclaredHereNote(msg, elem_ty); 18606 break :msg msg; 18607 }; 18608 return sema.failWithOwnedErrorMsg(msg); 18609 } 18610 if (elem_ty.zigTypeTag(mod) == .Opaque) { 18611 return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{}); 18612 } 18613 } 18614 18615 const ty = try mod.ptrType(.{ 18616 .child = elem_ty.toIntern(), 18617 .sentinel = sentinel, 18618 .flags = .{ 18619 .alignment = abi_align, 18620 .address_space = address_space, 18621 .is_const = !inst_data.flags.is_mutable, 18622 .is_allowzero = inst_data.flags.is_allowzero, 18623 .is_volatile = inst_data.flags.is_volatile, 18624 .size = inst_data.size, 18625 }, 18626 .packed_offset = .{ 18627 .bit_offset = bit_offset, 18628 .host_size = host_size, 18629 }, 18630 }); 18631 return sema.addType(ty); 18632 } 18633 18634 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18635 const tracy = trace(@src()); 18636 defer tracy.end(); 18637 18638 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 18639 const src = inst_data.src(); 18640 const obj_ty = try sema.resolveType(block, src, inst_data.operand); 18641 const mod = sema.mod; 18642 18643 switch (obj_ty.zigTypeTag(mod)) { 18644 .Struct => return sema.structInitEmpty(block, obj_ty, src, src), 18645 .Array, .Vector => return sema.arrayInitEmpty(block, src, obj_ty), 18646 .Void => return sema.addConstant(Value.void), 18647 .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}), 18648 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), 18649 } 18650 } 18651 18652 fn structInitEmpty( 18653 sema: *Sema, 18654 block: *Block, 18655 obj_ty: Type, 18656 dest_src: LazySrcLoc, 18657 init_src: LazySrcLoc, 18658 ) CompileError!Air.Inst.Ref { 18659 const mod = sema.mod; 18660 const gpa = sema.gpa; 18661 // This logic must be synchronized with that in `zirStructInit`. 18662 const struct_ty = try sema.resolveTypeFields(obj_ty); 18663 18664 // The init values to use for the struct instance. 18665 const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(mod)); 18666 defer gpa.free(field_inits); 18667 @memset(field_inits, .none); 18668 18669 return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false); 18670 } 18671 18672 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { 18673 const mod = sema.mod; 18674 const arr_len = obj_ty.arrayLen(mod); 18675 if (arr_len != 0) { 18676 if (obj_ty.zigTypeTag(mod) == .Array) { 18677 return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len}); 18678 } else { 18679 return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len}); 18680 } 18681 } 18682 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 18683 .ty = obj_ty.toIntern(), 18684 .storage = .{ .elems = &.{} }, 18685 } })).toValue()); 18686 } 18687 18688 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 18689 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 18690 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 18691 const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 18692 const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 18693 const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; 18694 const union_ty = try sema.resolveType(block, ty_src, extra.union_type); 18695 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "name of field being initialized must be comptime-known"); 18696 const init = try sema.resolveInst(extra.init); 18697 return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); 18698 } 18699 18700 fn unionInit( 18701 sema: *Sema, 18702 block: *Block, 18703 uncasted_init: Air.Inst.Ref, 18704 init_src: LazySrcLoc, 18705 union_ty: Type, 18706 union_ty_src: LazySrcLoc, 18707 field_name: InternPool.NullTerminatedString, 18708 field_src: LazySrcLoc, 18709 ) CompileError!Air.Inst.Ref { 18710 const mod = sema.mod; 18711 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); 18712 const field = union_ty.unionFields(mod).values()[field_index]; 18713 const init = try sema.coerce(block, field.ty, uncasted_init, init_src); 18714 18715 if (try sema.resolveMaybeUndefVal(init)) |init_val| { 18716 const tag_ty = union_ty.unionTagTypeHypothetical(mod); 18717 const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?)); 18718 const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); 18719 return sema.addConstant((try mod.intern(.{ .un = .{ 18720 .ty = union_ty.toIntern(), 18721 .tag = try tag_val.intern(tag_ty, mod), 18722 .val = try init_val.intern(field.ty, mod), 18723 } })).toValue()); 18724 } 18725 18726 try sema.requireRuntimeBlock(block, init_src, null); 18727 _ = union_ty_src; 18728 try sema.queueFullTypeResolution(union_ty); 18729 return block.addUnionInit(union_ty, field_index, init); 18730 } 18731 18732 fn zirStructInit( 18733 sema: *Sema, 18734 block: *Block, 18735 inst: Zir.Inst.Index, 18736 is_ref: bool, 18737 ) CompileError!Air.Inst.Ref { 18738 const gpa = sema.gpa; 18739 const zir_datas = sema.code.instructions.items(.data); 18740 const inst_data = zir_datas[inst].pl_node; 18741 const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); 18742 const src = inst_data.src(); 18743 18744 const mod = sema.mod; 18745 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; 18746 const first_field_type_data = zir_datas[first_item.field_type].pl_node; 18747 const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; 18748 const resolved_ty = try sema.resolveType(block, src, first_field_type_extra.container_type); 18749 try sema.resolveTypeLayout(resolved_ty); 18750 18751 if (resolved_ty.zigTypeTag(mod) == .Struct) { 18752 // This logic must be synchronized with that in `zirStructInitEmpty`. 18753 18754 // Maps field index to field_type index of where it was already initialized. 18755 // For making sure all fields are accounted for and no fields are duplicated. 18756 const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(mod)); 18757 defer gpa.free(found_fields); 18758 18759 // The init values to use for the struct instance. 18760 const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(mod)); 18761 defer gpa.free(field_inits); 18762 @memset(field_inits, .none); 18763 18764 var field_i: u32 = 0; 18765 var extra_index = extra.end; 18766 18767 const is_packed = resolved_ty.containerLayout(mod) == .Packed; 18768 while (field_i < extra.data.fields_len) : (field_i += 1) { 18769 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); 18770 extra_index = item.end; 18771 18772 const field_type_data = zir_datas[item.data.field_type].pl_node; 18773 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; 18774 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 18775 const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); 18776 const field_index = if (resolved_ty.isTuple(mod)) 18777 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) 18778 else 18779 try sema.structFieldIndex(block, resolved_ty, field_name, field_src); 18780 if (field_inits[field_index] != .none) { 18781 const other_field_type = found_fields[field_index]; 18782 const other_field_type_data = zir_datas[other_field_type].pl_node; 18783 const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_type_data.src_node }; 18784 const msg = msg: { 18785 const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); 18786 errdefer msg.destroy(gpa); 18787 try sema.errNote(block, other_field_src, msg, "other field here", .{}); 18788 break :msg msg; 18789 }; 18790 return sema.failWithOwnedErrorMsg(msg); 18791 } 18792 found_fields[field_index] = item.data.field_type; 18793 field_inits[field_index] = try sema.resolveInst(item.data.init); 18794 if (!is_packed) if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| { 18795 const init_val = (try sema.resolveMaybeUndefVal(field_inits[field_index])) orelse { 18796 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); 18797 }; 18798 18799 if (!init_val.eql(default_value, resolved_ty.structFieldType(field_index, mod), mod)) { 18800 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); 18801 } 18802 }; 18803 } 18804 18805 return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref); 18806 } else if (resolved_ty.zigTypeTag(mod) == .Union) { 18807 if (extra.data.fields_len != 1) { 18808 return sema.fail(block, src, "union initialization expects exactly one field", .{}); 18809 } 18810 18811 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end); 18812 18813 const field_type_data = zir_datas[item.data.field_type].pl_node; 18814 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; 18815 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; 18816 const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); 18817 const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); 18818 const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); 18819 const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?)); 18820 const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); 18821 18822 const init_inst = try sema.resolveInst(item.data.init); 18823 if (try sema.resolveMaybeUndefVal(init_inst)) |val| { 18824 const field = resolved_ty.unionFields(mod).values()[field_index]; 18825 return sema.addConstantMaybeRef(block, resolved_ty, (try mod.intern(.{ .un = .{ 18826 .ty = resolved_ty.toIntern(), 18827 .tag = try tag_val.intern(tag_ty, mod), 18828 .val = try val.intern(field.ty, mod), 18829 } })).toValue(), is_ref); 18830 } 18831 18832 if (is_ref) { 18833 const target = mod.getTarget(); 18834 const alloc_ty = try mod.ptrType(.{ 18835 .child = resolved_ty.toIntern(), 18836 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 18837 }); 18838 const alloc = try block.addTy(.alloc, alloc_ty); 18839 const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true); 18840 try sema.storePtr(block, src, field_ptr, init_inst); 18841 const new_tag = try sema.addConstant(tag_val); 18842 _ = try block.addBinOp(.set_union_tag, alloc, new_tag); 18843 return sema.makePtrConst(block, alloc); 18844 } 18845 18846 try sema.requireRuntimeBlock(block, src, null); 18847 try sema.queueFullTypeResolution(resolved_ty); 18848 return block.addUnionInit(resolved_ty, field_index, init_inst); 18849 } else if (resolved_ty.isAnonStruct(mod)) { 18850 return sema.fail(block, src, "TODO anon struct init validation", .{}); 18851 } 18852 unreachable; 18853 } 18854 18855 fn finishStructInit( 18856 sema: *Sema, 18857 block: *Block, 18858 init_src: LazySrcLoc, 18859 dest_src: LazySrcLoc, 18860 field_inits: []Air.Inst.Ref, 18861 struct_ty: Type, 18862 is_ref: bool, 18863 ) CompileError!Air.Inst.Ref { 18864 const mod = sema.mod; 18865 const ip = &mod.intern_pool; 18866 18867 var root_msg: ?*Module.ErrorMsg = null; 18868 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 18869 18870 switch (ip.indexToKey(struct_ty.toIntern())) { 18871 .anon_struct_type => |anon_struct| { 18872 for (anon_struct.values, 0..) |default_val, i| { 18873 if (field_inits[i] != .none) continue; 18874 18875 if (default_val == .none) { 18876 if (anon_struct.names.len == 0) { 18877 const template = "missing tuple field with index {d}"; 18878 if (root_msg) |msg| { 18879 try sema.errNote(block, init_src, msg, template, .{i}); 18880 } else { 18881 root_msg = try sema.errMsg(block, init_src, template, .{i}); 18882 } 18883 } else { 18884 const field_name = anon_struct.names[i]; 18885 const template = "missing struct field: {}"; 18886 const args = .{field_name.fmt(ip)}; 18887 if (root_msg) |msg| { 18888 try sema.errNote(block, init_src, msg, template, args); 18889 } else { 18890 root_msg = try sema.errMsg(block, init_src, template, args); 18891 } 18892 } 18893 } else { 18894 field_inits[i] = try sema.addConstant(default_val.toValue()); 18895 } 18896 } 18897 }, 18898 .struct_type => |struct_type| { 18899 const struct_obj = mod.structPtrUnwrap(struct_type.index).?; 18900 for (struct_obj.fields.values(), 0..) |field, i| { 18901 if (field_inits[i] != .none) continue; 18902 18903 if (field.default_val == .none) { 18904 const field_name = struct_obj.fields.keys()[i]; 18905 const template = "missing struct field: {}"; 18906 const args = .{field_name.fmt(ip)}; 18907 if (root_msg) |msg| { 18908 try sema.errNote(block, init_src, msg, template, args); 18909 } else { 18910 root_msg = try sema.errMsg(block, init_src, template, args); 18911 } 18912 } else { 18913 field_inits[i] = try sema.addConstant(field.default_val.toValue()); 18914 } 18915 } 18916 }, 18917 else => unreachable, 18918 } 18919 18920 if (root_msg) |msg| { 18921 if (mod.typeToStruct(struct_ty)) |struct_obj| { 18922 const fqn = try struct_obj.getFullyQualifiedName(mod); 18923 try mod.errNoteNonLazy( 18924 struct_obj.srcLoc(mod), 18925 msg, 18926 "struct '{}' declared here", 18927 .{fqn.fmt(ip)}, 18928 ); 18929 } 18930 root_msg = null; 18931 return sema.failWithOwnedErrorMsg(msg); 18932 } 18933 18934 // Find which field forces the expression to be runtime, if any. 18935 const opt_runtime_index = for (field_inits, 0..) |field_init, i| { 18936 if (!(try sema.isComptimeKnown(field_init))) { 18937 break i; 18938 } 18939 } else null; 18940 18941 const runtime_index = opt_runtime_index orelse { 18942 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); 18943 for (elems, field_inits, 0..) |*elem, field_init, field_i| { 18944 elem.* = try (sema.resolveMaybeUndefVal(field_init) catch unreachable).? 18945 .intern(struct_ty.structFieldType(field_i, mod), mod); 18946 } 18947 const struct_val = try mod.intern(.{ .aggregate = .{ 18948 .ty = struct_ty.toIntern(), 18949 .storage = .{ .elems = elems }, 18950 } }); 18951 return sema.addConstantMaybeRef(block, struct_ty, struct_val.toValue(), is_ref); 18952 }; 18953 18954 if (is_ref) { 18955 try sema.resolveStructLayout(struct_ty); 18956 const target = sema.mod.getTarget(); 18957 const alloc_ty = try mod.ptrType(.{ 18958 .child = struct_ty.toIntern(), 18959 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 18960 }); 18961 const alloc = try block.addTy(.alloc, alloc_ty); 18962 for (field_inits, 0..) |field_init, i_usize| { 18963 const i = @as(u32, @intCast(i_usize)); 18964 const field_src = dest_src; 18965 const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, alloc, i, field_src, struct_ty, true); 18966 try sema.storePtr(block, dest_src, field_ptr, field_init); 18967 } 18968 18969 return sema.makePtrConst(block, alloc); 18970 } 18971 18972 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 18973 error.NeededSourceLocation => { 18974 const decl = mod.declPtr(block.src_decl); 18975 const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index); 18976 try sema.requireRuntimeBlock(block, dest_src, field_src); 18977 unreachable; 18978 }, 18979 else => |e| return e, 18980 }; 18981 try sema.queueFullTypeResolution(struct_ty); 18982 return block.addAggregateInit(struct_ty, field_inits); 18983 } 18984 18985 fn zirStructInitAnon( 18986 sema: *Sema, 18987 block: *Block, 18988 inst: Zir.Inst.Index, 18989 is_ref: bool, 18990 ) CompileError!Air.Inst.Ref { 18991 const mod = sema.mod; 18992 const gpa = sema.gpa; 18993 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 18994 const src = inst_data.src(); 18995 const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); 18996 const types = try sema.arena.alloc(InternPool.Index, extra.data.fields_len); 18997 const values = try sema.arena.alloc(InternPool.Index, types.len); 18998 var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena); 18999 try fields.ensureUnusedCapacity(types.len); 19000 19001 // Find which field forces the expression to be runtime, if any. 19002 const opt_runtime_index = rs: { 19003 var runtime_index: ?usize = null; 19004 var extra_index = extra.end; 19005 for (types, 0..) |*field_ty, i_usize| { 19006 const i = @as(u32, @intCast(i_usize)); 19007 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); 19008 extra_index = item.end; 19009 19010 const name = sema.code.nullTerminatedString(item.data.field_name); 19011 const name_ip = try mod.intern_pool.getOrPutString(gpa, name); 19012 const gop = fields.getOrPutAssumeCapacity(name_ip); 19013 if (gop.found_existing) { 19014 const msg = msg: { 19015 const decl = mod.declPtr(block.src_decl); 19016 const field_src = mod.initSrc(src.node_offset.x, decl, i); 19017 const msg = try sema.errMsg(block, field_src, "duplicate field", .{}); 19018 errdefer msg.destroy(gpa); 19019 19020 const prev_source = mod.initSrc(src.node_offset.x, decl, gop.value_ptr.*); 19021 try sema.errNote(block, prev_source, msg, "other field here", .{}); 19022 break :msg msg; 19023 }; 19024 return sema.failWithOwnedErrorMsg(msg); 19025 } 19026 gop.value_ptr.* = i; 19027 19028 const init = try sema.resolveInst(item.data.init); 19029 field_ty.* = sema.typeOf(init).toIntern(); 19030 if (field_ty.toType().zigTypeTag(mod) == .Opaque) { 19031 const msg = msg: { 19032 const decl = mod.declPtr(block.src_decl); 19033 const field_src = mod.initSrc(src.node_offset.x, decl, i); 19034 const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19035 errdefer msg.destroy(sema.gpa); 19036 19037 try sema.addDeclaredHereNote(msg, field_ty.toType()); 19038 break :msg msg; 19039 }; 19040 return sema.failWithOwnedErrorMsg(msg); 19041 } 19042 if (try sema.resolveMaybeUndefVal(init)) |init_val| { 19043 values[i] = try init_val.intern(field_ty.toType(), mod); 19044 } else { 19045 values[i] = .none; 19046 runtime_index = i; 19047 } 19048 } 19049 break :rs runtime_index; 19050 }; 19051 19052 const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ 19053 .names = fields.keys(), 19054 .types = types, 19055 .values = values, 19056 } }); 19057 19058 const runtime_index = opt_runtime_index orelse { 19059 const tuple_val = try mod.intern(.{ .aggregate = .{ 19060 .ty = tuple_ty, 19061 .storage = .{ .elems = values }, 19062 } }); 19063 return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); 19064 }; 19065 19066 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 19067 error.NeededSourceLocation => { 19068 const decl = mod.declPtr(block.src_decl); 19069 const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index); 19070 try sema.requireRuntimeBlock(block, src, field_src); 19071 unreachable; 19072 }, 19073 else => |e| return e, 19074 }; 19075 19076 if (is_ref) { 19077 const target = mod.getTarget(); 19078 const alloc_ty = try mod.ptrType(.{ 19079 .child = tuple_ty, 19080 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19081 }); 19082 const alloc = try block.addTy(.alloc, alloc_ty); 19083 var extra_index = extra.end; 19084 for (types, 0..) |field_ty, i_usize| { 19085 const i = @as(u32, @intCast(i_usize)); 19086 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); 19087 extra_index = item.end; 19088 19089 const field_ptr_ty = try mod.ptrType(.{ 19090 .child = field_ty, 19091 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19092 }); 19093 if (values[i] == .none) { 19094 const init = try sema.resolveInst(item.data.init); 19095 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19096 _ = try block.addBinOp(.store, field_ptr, init); 19097 } 19098 } 19099 19100 return sema.makePtrConst(block, alloc); 19101 } 19102 19103 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); 19104 var extra_index = extra.end; 19105 for (types, 0..) |_, i| { 19106 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); 19107 extra_index = item.end; 19108 element_refs[i] = try sema.resolveInst(item.data.init); 19109 } 19110 19111 return block.addAggregateInit(tuple_ty.toType(), element_refs); 19112 } 19113 19114 fn zirArrayInit( 19115 sema: *Sema, 19116 block: *Block, 19117 inst: Zir.Inst.Index, 19118 is_ref: bool, 19119 ) CompileError!Air.Inst.Ref { 19120 const mod = sema.mod; 19121 const gpa = sema.gpa; 19122 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 19123 const src = inst_data.src(); 19124 19125 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19126 const args = sema.code.refSlice(extra.end, extra.data.operands_len); 19127 assert(args.len >= 2); // array_ty + at least one element 19128 19129 const array_ty = try sema.resolveType(block, src, args[0]); 19130 const sentinel_val = array_ty.sentinel(mod); 19131 19132 const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @intFromBool(sentinel_val != null)); 19133 defer gpa.free(resolved_args); 19134 for (args[1..], 0..) |arg, i| { 19135 const resolved_arg = try sema.resolveInst(arg); 19136 const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct) 19137 array_ty.structFieldType(i, mod) 19138 else 19139 array_ty.elemType2(mod); 19140 resolved_args[i] = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) { 19141 error.NeededSourceLocation => { 19142 const decl = mod.declPtr(block.src_decl); 19143 const elem_src = mod.initSrc(src.node_offset.x, decl, i); 19144 _ = try sema.coerce(block, elem_ty, resolved_arg, elem_src); 19145 unreachable; 19146 }, 19147 else => return err, 19148 }; 19149 } 19150 19151 if (sentinel_val) |some| { 19152 resolved_args[resolved_args.len - 1] = try sema.addConstant(some); 19153 } 19154 19155 const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| { 19156 const comptime_known = try sema.isComptimeKnown(arg); 19157 if (!comptime_known) break @as(u32, @intCast(i)); 19158 } else null; 19159 19160 const runtime_index = opt_runtime_index orelse { 19161 const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len); 19162 for (elem_vals, resolved_args, 0..) |*val, arg, i| { 19163 const elem_ty = if (array_ty.zigTypeTag(mod) == .Struct) 19164 array_ty.structFieldType(i, mod) 19165 else 19166 array_ty.elemType2(mod); 19167 // We checked that all args are comptime above. 19168 val.* = try ((sema.resolveMaybeUndefVal(arg) catch unreachable).?).intern(elem_ty, mod); 19169 } 19170 return sema.addConstantMaybeRef(block, array_ty, (try mod.intern(.{ .aggregate = .{ 19171 .ty = array_ty.toIntern(), 19172 .storage = .{ .elems = elem_vals }, 19173 } })).toValue(), is_ref); 19174 }; 19175 19176 sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { 19177 error.NeededSourceLocation => { 19178 const decl = mod.declPtr(block.src_decl); 19179 const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index); 19180 try sema.requireRuntimeBlock(block, src, elem_src); 19181 unreachable; 19182 }, 19183 else => return err, 19184 }; 19185 try sema.queueFullTypeResolution(array_ty); 19186 19187 if (is_ref) { 19188 const target = mod.getTarget(); 19189 const alloc_ty = try mod.ptrType(.{ 19190 .child = array_ty.toIntern(), 19191 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19192 }); 19193 const alloc = try block.addTy(.alloc, alloc_ty); 19194 19195 if (array_ty.isTuple(mod)) { 19196 for (resolved_args, 0..) |arg, i| { 19197 const elem_ptr_ty = try mod.ptrType(.{ 19198 .child = array_ty.structFieldType(i, mod).toIntern(), 19199 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19200 }); 19201 const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); 19202 19203 const index = try sema.addIntUnsigned(Type.usize, i); 19204 const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); 19205 _ = try block.addBinOp(.store, elem_ptr, arg); 19206 } 19207 return sema.makePtrConst(block, alloc); 19208 } 19209 19210 const elem_ptr_ty = try mod.ptrType(.{ 19211 .child = array_ty.elemType2(mod).toIntern(), 19212 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19213 }); 19214 const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); 19215 19216 for (resolved_args, 0..) |arg, i| { 19217 const index = try sema.addIntUnsigned(Type.usize, i); 19218 const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); 19219 _ = try block.addBinOp(.store, elem_ptr, arg); 19220 } 19221 return sema.makePtrConst(block, alloc); 19222 } 19223 19224 return block.addAggregateInit(array_ty, resolved_args); 19225 } 19226 19227 fn zirArrayInitAnon( 19228 sema: *Sema, 19229 block: *Block, 19230 inst: Zir.Inst.Index, 19231 is_ref: bool, 19232 ) CompileError!Air.Inst.Ref { 19233 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 19234 const src = inst_data.src(); 19235 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); 19236 const operands = sema.code.refSlice(extra.end, extra.data.operands_len); 19237 const mod = sema.mod; 19238 19239 const types = try sema.arena.alloc(InternPool.Index, operands.len); 19240 const values = try sema.arena.alloc(InternPool.Index, operands.len); 19241 19242 const opt_runtime_src = rs: { 19243 var runtime_src: ?LazySrcLoc = null; 19244 for (operands, 0..) |operand, i| { 19245 const operand_src = src; // TODO better source location 19246 const elem = try sema.resolveInst(operand); 19247 types[i] = sema.typeOf(elem).toIntern(); 19248 if (types[i].toType().zigTypeTag(mod) == .Opaque) { 19249 const msg = msg: { 19250 const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 19251 errdefer msg.destroy(sema.gpa); 19252 19253 try sema.addDeclaredHereNote(msg, types[i].toType()); 19254 break :msg msg; 19255 }; 19256 return sema.failWithOwnedErrorMsg(msg); 19257 } 19258 if (try sema.resolveMaybeUndefVal(elem)) |val| { 19259 values[i] = val.toIntern(); 19260 } else { 19261 values[i] = .none; 19262 runtime_src = operand_src; 19263 } 19264 } 19265 break :rs runtime_src; 19266 }; 19267 19268 const tuple_ty = try mod.intern(.{ .anon_struct_type = .{ 19269 .types = types, 19270 .values = values, 19271 .names = &.{}, 19272 } }); 19273 19274 const runtime_src = opt_runtime_src orelse { 19275 const tuple_val = try mod.intern(.{ .aggregate = .{ 19276 .ty = tuple_ty, 19277 .storage = .{ .elems = values }, 19278 } }); 19279 return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); 19280 }; 19281 19282 try sema.requireRuntimeBlock(block, src, runtime_src); 19283 19284 if (is_ref) { 19285 const target = sema.mod.getTarget(); 19286 const alloc_ty = try mod.ptrType(.{ 19287 .child = tuple_ty, 19288 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19289 }); 19290 const alloc = try block.addTy(.alloc, alloc_ty); 19291 for (operands, 0..) |operand, i_usize| { 19292 const i = @as(u32, @intCast(i_usize)); 19293 const field_ptr_ty = try mod.ptrType(.{ 19294 .child = types[i], 19295 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, 19296 }); 19297 if (values[i] == .none) { 19298 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); 19299 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); 19300 } 19301 } 19302 19303 return sema.makePtrConst(block, alloc); 19304 } 19305 19306 const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 19307 for (operands, 0..) |operand, i| { 19308 element_refs[i] = try sema.resolveInst(operand); 19309 } 19310 19311 return block.addAggregateInit(tuple_ty.toType(), element_refs); 19312 } 19313 19314 fn addConstantMaybeRef( 19315 sema: *Sema, 19316 block: *Block, 19317 ty: Type, 19318 val: Value, 19319 is_ref: bool, 19320 ) !Air.Inst.Ref { 19321 if (!is_ref) return sema.addConstant(val); 19322 19323 var anon_decl = try block.startAnonDecl(); 19324 defer anon_decl.deinit(); 19325 const decl = try anon_decl.finish( 19326 ty, 19327 val, 19328 .none, // default alignment 19329 ); 19330 return sema.analyzeDeclRef(decl); 19331 } 19332 19333 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19334 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 19335 const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data; 19336 const ty_src = inst_data.src(); 19337 const field_src = inst_data.src(); 19338 const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); 19339 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, "field name must be comptime-known"); 19340 return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); 19341 } 19342 19343 fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19344 const mod = sema.mod; 19345 const ip = &mod.intern_pool; 19346 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 19347 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; 19348 const ty_src = inst_data.src(); 19349 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; 19350 const aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) { 19351 // Since this is a ZIR instruction that returns a type, encountering 19352 // generic poison should not result in a failed compilation, but the 19353 // generic poison type. This prevents unnecessary failures when 19354 // constructing types at compile-time. 19355 error.GenericPoison => return Air.Inst.Ref.generic_poison_type, 19356 else => |e| return e, 19357 }; 19358 const zir_field_name = sema.code.nullTerminatedString(extra.name_start); 19359 const field_name = try ip.getOrPutString(sema.gpa, zir_field_name); 19360 return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); 19361 } 19362 19363 fn fieldType( 19364 sema: *Sema, 19365 block: *Block, 19366 aggregate_ty: Type, 19367 field_name: InternPool.NullTerminatedString, 19368 field_src: LazySrcLoc, 19369 ty_src: LazySrcLoc, 19370 ) CompileError!Air.Inst.Ref { 19371 const mod = sema.mod; 19372 var cur_ty = aggregate_ty; 19373 while (true) { 19374 const resolved_ty = try sema.resolveTypeFields(cur_ty); 19375 cur_ty = resolved_ty; 19376 switch (cur_ty.zigTypeTag(mod)) { 19377 .Struct => switch (mod.intern_pool.indexToKey(cur_ty.toIntern())) { 19378 .anon_struct_type => |anon_struct| { 19379 const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); 19380 return sema.addType(anon_struct.types[field_index].toType()); 19381 }, 19382 .struct_type => |struct_type| { 19383 const struct_obj = mod.structPtrUnwrap(struct_type.index).?; 19384 const field = struct_obj.fields.get(field_name) orelse 19385 return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); 19386 return sema.addType(field.ty); 19387 }, 19388 else => unreachable, 19389 }, 19390 .Union => { 19391 const union_obj = mod.typeToUnion(cur_ty).?; 19392 const field = union_obj.fields.get(field_name) orelse 19393 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); 19394 return sema.addType(field.ty); 19395 }, 19396 .Optional => { 19397 // Struct/array init through optional requires the child type to not be a pointer. 19398 // If the child of .optional is a pointer it'll error on the next loop. 19399 cur_ty = mod.intern_pool.indexToKey(cur_ty.toIntern()).opt_type.toType(); 19400 continue; 19401 }, 19402 .ErrorUnion => { 19403 cur_ty = cur_ty.errorUnionPayload(mod); 19404 continue; 19405 }, 19406 else => {}, 19407 } 19408 return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{ 19409 resolved_ty.fmt(sema.mod), 19410 }); 19411 } 19412 } 19413 19414 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 19415 return sema.getErrorReturnTrace(block); 19416 } 19417 19418 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { 19419 const mod = sema.mod; 19420 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 19421 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 19422 const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); 19423 const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern()); 19424 19425 if (sema.owner_func != null and 19426 sema.owner_func.?.calls_or_awaits_errorable_fn and 19427 mod.comp.bin_file.options.error_return_tracing and 19428 mod.backendSupportsFeature(.error_return_trace)) 19429 { 19430 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); 19431 } 19432 return sema.addConstant((try mod.intern(.{ .opt = .{ 19433 .ty = opt_ptr_stack_trace_ty.toIntern(), 19434 .val = .none, 19435 } })).toValue()); 19436 } 19437 19438 fn zirFrame( 19439 sema: *Sema, 19440 block: *Block, 19441 extended: Zir.Inst.Extended.InstData, 19442 ) CompileError!Air.Inst.Ref { 19443 const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand))); 19444 return sema.failWithUseOfAsync(block, src); 19445 } 19446 19447 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19448 const mod = sema.mod; 19449 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19450 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19451 const ty = try sema.resolveType(block, operand_src, inst_data.operand); 19452 if (ty.isNoReturn(mod)) { 19453 return sema.fail(block, operand_src, "no align available for type '{}'", .{ty.fmt(sema.mod)}); 19454 } 19455 const val = try ty.lazyAbiAlignment(mod); 19456 if (val.isLazyAlign(mod)) { 19457 try sema.queueFullTypeResolution(ty); 19458 } 19459 return sema.addConstant(val); 19460 } 19461 19462 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19463 const mod = sema.mod; 19464 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19465 const operand = try sema.resolveInst(inst_data.operand); 19466 if (try sema.resolveMaybeUndefVal(operand)) |val| { 19467 if (val.isUndef(mod)) return sema.addConstUndef(Type.u1); 19468 if (val.toBool()) return sema.addConstant(try mod.intValue(Type.u1, 1)); 19469 return sema.addConstant(try mod.intValue(Type.u1, 0)); 19470 } 19471 return block.addUnOp(.int_from_bool, operand); 19472 } 19473 19474 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19475 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19476 const operand = try sema.resolveInst(inst_data.operand); 19477 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19478 19479 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { 19480 const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name; 19481 return sema.addStrLit(block, sema.mod.intern_pool.stringToSlice(err_name)); 19482 } 19483 19484 // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass 19485 // might be able to resolve the result at compile time. 19486 return block.addUnOp(.error_name, operand); 19487 } 19488 19489 fn zirUnaryMath( 19490 sema: *Sema, 19491 block: *Block, 19492 inst: Zir.Inst.Index, 19493 air_tag: Air.Inst.Tag, 19494 comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value, 19495 ) CompileError!Air.Inst.Ref { 19496 const tracy = trace(@src()); 19497 defer tracy.end(); 19498 19499 const mod = sema.mod; 19500 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19501 const operand = try sema.resolveInst(inst_data.operand); 19502 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19503 const operand_ty = sema.typeOf(operand); 19504 19505 switch (operand_ty.zigTypeTag(mod)) { 19506 .ComptimeFloat, .Float => {}, 19507 .Vector => { 19508 const scalar_ty = operand_ty.scalarType(mod); 19509 switch (scalar_ty.zigTypeTag(mod)) { 19510 .ComptimeFloat, .Float => {}, 19511 else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}), 19512 } 19513 }, 19514 else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}), 19515 } 19516 19517 switch (operand_ty.zigTypeTag(mod)) { 19518 .Vector => { 19519 const scalar_ty = operand_ty.scalarType(mod); 19520 const vec_len = operand_ty.vectorLen(mod); 19521 const result_ty = try mod.vectorType(.{ 19522 .len = vec_len, 19523 .child = scalar_ty.toIntern(), 19524 }); 19525 if (try sema.resolveMaybeUndefVal(operand)) |val| { 19526 if (val.isUndef(mod)) 19527 return sema.addConstUndef(result_ty); 19528 19529 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 19530 for (elems, 0..) |*elem, i| { 19531 const elem_val = try val.elemValue(sema.mod, i); 19532 elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod); 19533 } 19534 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 19535 .ty = result_ty.toIntern(), 19536 .storage = .{ .elems = elems }, 19537 } })).toValue()); 19538 } 19539 19540 try sema.requireRuntimeBlock(block, operand_src, null); 19541 return block.addUnOp(air_tag, operand); 19542 }, 19543 .ComptimeFloat, .Float => { 19544 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { 19545 if (operand_val.isUndef(mod)) 19546 return sema.addConstUndef(operand_ty); 19547 const result_val = try eval(operand_val, operand_ty, sema.arena, sema.mod); 19548 return sema.addConstant(result_val); 19549 } 19550 19551 try sema.requireRuntimeBlock(block, operand_src, null); 19552 return block.addUnOp(air_tag, operand); 19553 }, 19554 else => unreachable, 19555 } 19556 } 19557 19558 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 19559 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 19560 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 19561 const src = inst_data.src(); 19562 const operand = try sema.resolveInst(inst_data.operand); 19563 const operand_ty = sema.typeOf(operand); 19564 const mod = sema.mod; 19565 const ip = &mod.intern_pool; 19566 19567 try sema.resolveTypeLayout(operand_ty); 19568 const enum_ty = switch (operand_ty.zigTypeTag(mod)) { 19569 .EnumLiteral => { 19570 const val = try sema.resolveConstValue(block, .unneeded, operand, ""); 19571 const tag_name = ip.indexToKey(val.toIntern()).enum_literal; 19572 return sema.addStrLit(block, ip.stringToSlice(tag_name)); 19573 }, 19574 .Enum => operand_ty, 19575 .Union => operand_ty.unionTagType(mod) orelse { 19576 const msg = msg: { 19577 const msg = try sema.errMsg(block, src, "union '{}' is untagged", .{ 19578 operand_ty.fmt(sema.mod), 19579 }); 19580 errdefer msg.destroy(sema.gpa); 19581 try sema.addDeclaredHereNote(msg, operand_ty); 19582 break :msg msg; 19583 }; 19584 return sema.failWithOwnedErrorMsg(msg); 19585 }, 19586 else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{ 19587 operand_ty.fmt(mod), 19588 }), 19589 }; 19590 if (enum_ty.enumFieldCount(mod) == 0) { 19591 // TODO I don't think this is the correct way to handle this but 19592 // it prevents a crash. 19593 return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{}'", .{ 19594 enum_ty.fmt(mod), 19595 }); 19596 } 19597 const enum_decl_index = enum_ty.getOwnerDecl(mod); 19598 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); 19599 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { 19600 const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse { 19601 const enum_decl = mod.declPtr(enum_decl_index); 19602 const msg = msg: { 19603 const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{}'", .{ 19604 val.fmtValue(enum_ty, sema.mod), enum_decl.name.fmt(ip), 19605 }); 19606 errdefer msg.destroy(sema.gpa); 19607 try mod.errNoteNonLazy(enum_decl.srcLoc(mod), msg, "declared here", .{}); 19608 break :msg msg; 19609 }; 19610 return sema.failWithOwnedErrorMsg(msg); 19611 }; 19612 // TODO: write something like getCoercedInts to avoid needing to dupe 19613 const field_name = enum_ty.enumFieldName(field_index, mod); 19614 return sema.addStrLit(block, ip.stringToSlice(field_name)); 19615 } 19616 try sema.requireRuntimeBlock(block, src, operand_src); 19617 if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) { 19618 const ok = try block.addUnOp(.is_named_enum_value, casted_operand); 19619 try sema.addSafetyCheck(block, ok, .invalid_enum_value); 19620 } 19621 // In case the value is runtime-known, we have an AIR instruction for this instead 19622 // of trying to lower it in Sema because an optimization pass may result in the operand 19623 // being comptime-known, which would let us elide the `tag_name` AIR instruction. 19624 return block.addUnOp(.tag_name, casted_operand); 19625 } 19626 19627 fn zirReify( 19628 sema: *Sema, 19629 block: *Block, 19630 extended: Zir.Inst.Extended.InstData, 19631 inst: Zir.Inst.Index, 19632 ) CompileError!Air.Inst.Ref { 19633 const mod = sema.mod; 19634 const gpa = sema.gpa; 19635 const ip = &mod.intern_pool; 19636 const name_strategy = @as(Zir.Inst.NameStrategy, @enumFromInt(extended.small)); 19637 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 19638 const src = LazySrcLoc.nodeOffset(extra.node); 19639 const type_info_ty = try sema.getBuiltinType("Type"); 19640 const uncasted_operand = try sema.resolveInst(extra.operand); 19641 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 19642 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); 19643 const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime-known"); 19644 const union_val = ip.indexToKey(val.toIntern()).un; 19645 const target = mod.getTarget(); 19646 if (try union_val.val.toValue().anyUndef(mod)) return sema.failWithUseOfUndef(block, src); 19647 const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag.toValue(), mod).?; 19648 switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { 19649 .Type => return Air.Inst.Ref.type_type, 19650 .Void => return Air.Inst.Ref.void_type, 19651 .Bool => return Air.Inst.Ref.bool_type, 19652 .NoReturn => return Air.Inst.Ref.noreturn_type, 19653 .ComptimeFloat => return Air.Inst.Ref.comptime_float_type, 19654 .ComptimeInt => return Air.Inst.Ref.comptime_int_type, 19655 .Undefined => return Air.Inst.Ref.undefined_type, 19656 .Null => return Air.Inst.Ref.null_type, 19657 .AnyFrame => return sema.failWithUseOfAsync(block, src), 19658 .EnumLiteral => return Air.Inst.Ref.enum_literal_type, 19659 .Int => { 19660 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19661 const signedness_val = try union_val.val.toValue().fieldValue( 19662 mod, 19663 fields.getIndex(try ip.getOrPutString(gpa, "signedness")).?, 19664 ); 19665 const bits_val = try union_val.val.toValue().fieldValue( 19666 mod, 19667 fields.getIndex(try ip.getOrPutString(gpa, "bits")).?, 19668 ); 19669 19670 const signedness = mod.toEnum(std.builtin.Signedness, signedness_val); 19671 const bits = @as(u16, @intCast(bits_val.toUnsignedInt(mod))); 19672 const ty = try mod.intType(signedness, bits); 19673 return sema.addType(ty); 19674 }, 19675 .Vector => { 19676 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19677 const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19678 try ip.getOrPutString(gpa, "len"), 19679 ).?); 19680 const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19681 try ip.getOrPutString(gpa, "child"), 19682 ).?); 19683 19684 const len = @as(u32, @intCast(len_val.toUnsignedInt(mod))); 19685 const child_ty = child_val.toType(); 19686 19687 try sema.checkVectorElemType(block, src, child_ty); 19688 19689 const ty = try mod.vectorType(.{ 19690 .len = len, 19691 .child = child_ty.toIntern(), 19692 }); 19693 return sema.addType(ty); 19694 }, 19695 .Float => { 19696 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19697 const bits_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19698 try ip.getOrPutString(gpa, "bits"), 19699 ).?); 19700 19701 const bits = @as(u16, @intCast(bits_val.toUnsignedInt(mod))); 19702 const ty = switch (bits) { 19703 16 => Type.f16, 19704 32 => Type.f32, 19705 64 => Type.f64, 19706 80 => Type.f80, 19707 128 => Type.f128, 19708 else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}), 19709 }; 19710 return sema.addType(ty); 19711 }, 19712 .Pointer => { 19713 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19714 const size_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19715 try ip.getOrPutString(gpa, "size"), 19716 ).?); 19717 const is_const_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19718 try ip.getOrPutString(gpa, "is_const"), 19719 ).?); 19720 const is_volatile_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19721 try ip.getOrPutString(gpa, "is_volatile"), 19722 ).?); 19723 const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19724 try ip.getOrPutString(gpa, "alignment"), 19725 ).?); 19726 const address_space_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19727 try ip.getOrPutString(gpa, "address_space"), 19728 ).?); 19729 const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19730 try ip.getOrPutString(gpa, "child"), 19731 ).?); 19732 const is_allowzero_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19733 try ip.getOrPutString(gpa, "is_allowzero"), 19734 ).?); 19735 const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19736 try ip.getOrPutString(gpa, "sentinel"), 19737 ).?); 19738 19739 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { 19740 return sema.fail(block, src, "alignment must fit in 'u32'", .{}); 19741 } 19742 19743 const abi_align = Alignment.fromByteUnits( 19744 (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?, 19745 ); 19746 19747 const unresolved_elem_ty = child_val.toType(); 19748 const elem_ty = if (abi_align == .none) 19749 unresolved_elem_ty 19750 else t: { 19751 const elem_ty = try sema.resolveTypeFields(unresolved_elem_ty); 19752 try sema.resolveTypeLayout(elem_ty); 19753 break :t elem_ty; 19754 }; 19755 19756 const ptr_size = mod.toEnum(std.builtin.Type.Pointer.Size, size_val); 19757 19758 const actual_sentinel: InternPool.Index = s: { 19759 if (!sentinel_val.isNull(mod)) { 19760 if (ptr_size == .One or ptr_size == .C) { 19761 return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); 19762 } 19763 const sentinel_ptr_val = sentinel_val.optionalValue(mod).?; 19764 const ptr_ty = try mod.singleMutPtrType(elem_ty); 19765 const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; 19766 break :s sent_val.toIntern(); 19767 } 19768 break :s .none; 19769 }; 19770 19771 if (elem_ty.zigTypeTag(mod) == .NoReturn) { 19772 return sema.fail(block, src, "pointer to noreturn not allowed", .{}); 19773 } else if (elem_ty.zigTypeTag(mod) == .Fn) { 19774 if (ptr_size != .One) { 19775 return sema.fail(block, src, "function pointers must be single pointers", .{}); 19776 } 19777 const fn_align = mod.typeToFunc(elem_ty).?.alignment; 19778 if (abi_align != .none and fn_align != .none and 19779 abi_align != fn_align) 19780 { 19781 return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{}); 19782 } 19783 } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { 19784 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); 19785 } else if (ptr_size == .C) { 19786 if (!try sema.validateExternType(elem_ty, .other)) { 19787 const msg = msg: { 19788 const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)}); 19789 errdefer msg.destroy(gpa); 19790 19791 const src_decl = mod.declPtr(block.src_decl); 19792 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other); 19793 19794 try sema.addDeclaredHereNote(msg, elem_ty); 19795 break :msg msg; 19796 }; 19797 return sema.failWithOwnedErrorMsg(msg); 19798 } 19799 if (elem_ty.zigTypeTag(mod) == .Opaque) { 19800 return sema.fail(block, src, "C pointers cannot point to opaque types", .{}); 19801 } 19802 } 19803 19804 const ty = try mod.ptrType(.{ 19805 .child = elem_ty.toIntern(), 19806 .sentinel = actual_sentinel, 19807 .flags = .{ 19808 .size = ptr_size, 19809 .is_const = is_const_val.toBool(), 19810 .is_volatile = is_volatile_val.toBool(), 19811 .alignment = abi_align, 19812 .address_space = mod.toEnum(std.builtin.AddressSpace, address_space_val), 19813 .is_allowzero = is_allowzero_val.toBool(), 19814 }, 19815 }); 19816 return sema.addType(ty); 19817 }, 19818 .Array => { 19819 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19820 const len_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19821 try ip.getOrPutString(gpa, "len"), 19822 ).?); 19823 const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19824 try ip.getOrPutString(gpa, "child"), 19825 ).?); 19826 const sentinel_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19827 try ip.getOrPutString(gpa, "sentinel"), 19828 ).?); 19829 19830 const len = len_val.toUnsignedInt(mod); 19831 const child_ty = child_val.toType(); 19832 const sentinel = if (sentinel_val.optionalValue(mod)) |p| blk: { 19833 const ptr_ty = try mod.singleMutPtrType(child_ty); 19834 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?; 19835 } else null; 19836 19837 const ty = try mod.arrayType(.{ 19838 .len = len, 19839 .sentinel = if (sentinel) |s| s.toIntern() else .none, 19840 .child = child_ty.toIntern(), 19841 }); 19842 return sema.addType(ty); 19843 }, 19844 .Optional => { 19845 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19846 const child_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19847 try ip.getOrPutString(gpa, "child"), 19848 ).?); 19849 19850 const child_ty = child_val.toType(); 19851 19852 const ty = try mod.optionalType(child_ty.toIntern()); 19853 return sema.addType(ty); 19854 }, 19855 .ErrorUnion => { 19856 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19857 const error_set_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19858 try ip.getOrPutString(gpa, "error_set"), 19859 ).?); 19860 const payload_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19861 try ip.getOrPutString(gpa, "payload"), 19862 ).?); 19863 19864 const error_set_ty = error_set_val.toType(); 19865 const payload_ty = payload_val.toType(); 19866 19867 if (error_set_ty.zigTypeTag(mod) != .ErrorSet) { 19868 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{}); 19869 } 19870 19871 const ty = try mod.errorUnionType(error_set_ty, payload_ty); 19872 return sema.addType(ty); 19873 }, 19874 .ErrorSet => { 19875 const payload_val = union_val.val.toValue().optionalValue(mod) orelse 19876 return sema.addType(Type.anyerror); 19877 19878 const len = try sema.usizeCast(block, src, payload_val.sliceLen(mod)); 19879 var names: Module.Fn.InferredErrorSet.NameMap = .{}; 19880 try names.ensureUnusedCapacity(sema.arena, len); 19881 for (0..len) |i| { 19882 const elem_val = try payload_val.elemValue(mod, i); 19883 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 19884 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 19885 try ip.getOrPutString(gpa, "name"), 19886 ).?); 19887 19888 const name = try name_val.toIpString(Type.slice_const_u8, mod); 19889 _ = try mod.getErrorValue(name); 19890 const gop = names.getOrPutAssumeCapacity(name); 19891 if (gop.found_existing) { 19892 return sema.fail(block, src, "duplicate error '{}'", .{ 19893 name.fmt(ip), 19894 }); 19895 } 19896 } 19897 19898 const ty = try mod.errorSetFromUnsortedNames(names.keys()); 19899 return sema.addType(ty); 19900 }, 19901 .Struct => { 19902 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19903 const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19904 try ip.getOrPutString(gpa, "layout"), 19905 ).?); 19906 const backing_integer_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19907 try ip.getOrPutString(gpa, "backing_integer"), 19908 ).?); 19909 const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19910 try ip.getOrPutString(gpa, "fields"), 19911 ).?); 19912 const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19913 try ip.getOrPutString(gpa, "decls"), 19914 ).?); 19915 const is_tuple_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19916 try ip.getOrPutString(gpa, "is_tuple"), 19917 ).?); 19918 19919 const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); 19920 19921 // Decls 19922 if (decls_val.sliceLen(mod) > 0) { 19923 return sema.fail(block, src, "reified structs must have no decls", .{}); 19924 } 19925 19926 if (layout != .Packed and !backing_integer_val.isNull(mod)) { 19927 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); 19928 } 19929 19930 return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool()); 19931 }, 19932 .Enum => { 19933 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 19934 const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19935 try ip.getOrPutString(gpa, "tag_type"), 19936 ).?); 19937 const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19938 try ip.getOrPutString(gpa, "fields"), 19939 ).?); 19940 const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19941 try ip.getOrPutString(gpa, "decls"), 19942 ).?); 19943 const is_exhaustive_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 19944 try ip.getOrPutString(gpa, "is_exhaustive"), 19945 ).?); 19946 19947 // Decls 19948 if (decls_val.sliceLen(mod) > 0) { 19949 return sema.fail(block, src, "reified enums must have no decls", .{}); 19950 } 19951 19952 const int_tag_ty = tag_type_val.toType(); 19953 if (int_tag_ty.zigTypeTag(mod) != .Int) { 19954 return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); 19955 } 19956 19957 // Because these things each reference each other, `undefined` 19958 // placeholders are used before being set after the enum type gains 19959 // an InternPool index. 19960 19961 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 19962 .ty = Type.noreturn, 19963 .val = Value.@"unreachable", 19964 }, name_strategy, "enum", inst); 19965 const new_decl = mod.declPtr(new_decl_index); 19966 new_decl.owns_tv = true; 19967 errdefer { 19968 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 19969 mod.abortAnonDecl(new_decl_index); 19970 } 19971 19972 // Define our empty enum decl 19973 const fields_len = @as(u32, @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod)))); 19974 const incomplete_enum = try ip.getIncompleteEnum(gpa, .{ 19975 .decl = new_decl_index, 19976 .namespace = .none, 19977 .fields_len = fields_len, 19978 .has_values = true, 19979 .tag_mode = if (!is_exhaustive_val.toBool()) 19980 .nonexhaustive 19981 else 19982 .explicit, 19983 .tag_ty = int_tag_ty.toIntern(), 19984 }); 19985 // TODO: figure out InternPool removals for incremental compilation 19986 //errdefer ip.remove(incomplete_enum.index); 19987 19988 new_decl.ty = Type.type; 19989 new_decl.val = incomplete_enum.index.toValue(); 19990 19991 for (0..fields_len) |field_i| { 19992 const elem_val = try fields_val.elemValue(mod, field_i); 19993 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 19994 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 19995 try ip.getOrPutString(gpa, "name"), 19996 ).?); 19997 const value_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 19998 try ip.getOrPutString(gpa, "value"), 19999 ).?); 20000 20001 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 20002 20003 if (!try sema.intFitsInType(value_val, int_tag_ty, null)) { 20004 // TODO: better source location 20005 return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{ 20006 field_name.fmt(ip), 20007 value_val.fmtValue(Type.comptime_int, mod), 20008 int_tag_ty.fmt(mod), 20009 }); 20010 } 20011 20012 if (try incomplete_enum.addFieldName(ip, gpa, field_name)) |other_index| { 20013 const msg = msg: { 20014 const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{ 20015 field_name.fmt(ip), 20016 }); 20017 errdefer msg.destroy(gpa); 20018 _ = other_index; // TODO: this note is incorrect 20019 try sema.errNote(block, src, msg, "other field here", .{}); 20020 break :msg msg; 20021 }; 20022 return sema.failWithOwnedErrorMsg(msg); 20023 } 20024 20025 if (try incomplete_enum.addFieldValue(ip, gpa, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| { 20026 const msg = msg: { 20027 const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)}); 20028 errdefer msg.destroy(gpa); 20029 _ = other; // TODO: this note is incorrect 20030 try sema.errNote(block, src, msg, "other enum tag value here", .{}); 20031 break :msg msg; 20032 }; 20033 return sema.failWithOwnedErrorMsg(msg); 20034 } 20035 } 20036 20037 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20038 try mod.finalizeAnonDecl(new_decl_index); 20039 return decl_val; 20040 }, 20041 .Opaque => { 20042 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 20043 const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20044 try ip.getOrPutString(gpa, "decls"), 20045 ).?); 20046 20047 // Decls 20048 if (decls_val.sliceLen(mod) > 0) { 20049 return sema.fail(block, src, "reified opaque must have no decls", .{}); 20050 } 20051 20052 // Because these three things each reference each other, 20053 // `undefined` placeholders are used in two places before being set 20054 // after the opaque type gains an InternPool index. 20055 20056 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 20057 .ty = Type.noreturn, 20058 .val = Value.@"unreachable", 20059 }, name_strategy, "opaque", inst); 20060 const new_decl = mod.declPtr(new_decl_index); 20061 new_decl.owns_tv = true; 20062 errdefer { 20063 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 20064 mod.abortAnonDecl(new_decl_index); 20065 } 20066 20067 const new_namespace_index = try mod.createNamespace(.{ 20068 .parent = block.namespace.toOptional(), 20069 .ty = undefined, 20070 .file_scope = block.getFileScope(mod), 20071 }); 20072 const new_namespace = mod.namespacePtr(new_namespace_index); 20073 errdefer mod.destroyNamespace(new_namespace_index); 20074 20075 const opaque_ty = try mod.intern(.{ .opaque_type = .{ 20076 .decl = new_decl_index, 20077 .namespace = new_namespace_index, 20078 } }); 20079 // TODO: figure out InternPool removals for incremental compilation 20080 //errdefer ip.remove(opaque_ty); 20081 20082 new_decl.ty = Type.type; 20083 new_decl.val = opaque_ty.toValue(); 20084 new_namespace.ty = opaque_ty.toType(); 20085 20086 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20087 try mod.finalizeAnonDecl(new_decl_index); 20088 return decl_val; 20089 }, 20090 .Union => { 20091 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 20092 const layout_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20093 try ip.getOrPutString(gpa, "layout"), 20094 ).?); 20095 const tag_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20096 try ip.getOrPutString(gpa, "tag_type"), 20097 ).?); 20098 const fields_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20099 try ip.getOrPutString(gpa, "fields"), 20100 ).?); 20101 const decls_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20102 try ip.getOrPutString(gpa, "decls"), 20103 ).?); 20104 20105 // Decls 20106 if (decls_val.sliceLen(mod) > 0) { 20107 return sema.fail(block, src, "reified unions must have no decls", .{}); 20108 } 20109 const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); 20110 20111 // Because these three things each reference each other, `undefined` 20112 // placeholders are used before being set after the union type gains an 20113 // InternPool index. 20114 20115 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 20116 .ty = Type.noreturn, 20117 .val = Value.@"unreachable", 20118 }, name_strategy, "union", inst); 20119 const new_decl = mod.declPtr(new_decl_index); 20120 new_decl.owns_tv = true; 20121 errdefer { 20122 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 20123 mod.abortAnonDecl(new_decl_index); 20124 } 20125 20126 const new_namespace_index = try mod.createNamespace(.{ 20127 .parent = block.namespace.toOptional(), 20128 .ty = undefined, 20129 .file_scope = block.getFileScope(mod), 20130 }); 20131 const new_namespace = mod.namespacePtr(new_namespace_index); 20132 errdefer mod.destroyNamespace(new_namespace_index); 20133 20134 const union_index = try mod.createUnion(.{ 20135 .owner_decl = new_decl_index, 20136 .tag_ty = Type.null, 20137 .fields = .{}, 20138 .zir_index = inst, 20139 .layout = layout, 20140 .status = .have_field_types, 20141 .namespace = new_namespace_index, 20142 }); 20143 const union_obj = mod.unionPtr(union_index); 20144 errdefer mod.destroyUnion(union_index); 20145 20146 const union_ty = try ip.get(gpa, .{ .union_type = .{ 20147 .index = union_index, 20148 .runtime_tag = if (!tag_type_val.isNull(mod)) 20149 .tagged 20150 else if (layout != .Auto) 20151 .none 20152 else switch (mod.optimizeMode()) { 20153 .Debug, .ReleaseSafe => .safety, 20154 .ReleaseFast, .ReleaseSmall => .none, 20155 }, 20156 } }); 20157 // TODO: figure out InternPool removals for incremental compilation 20158 //errdefer ip.remove(union_ty); 20159 20160 new_decl.ty = Type.type; 20161 new_decl.val = union_ty.toValue(); 20162 new_namespace.ty = union_ty.toType(); 20163 20164 // Tag type 20165 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); 20166 var explicit_tags_seen: []bool = &.{}; 20167 var enum_field_names: []InternPool.NullTerminatedString = &.{}; 20168 if (tag_type_val.optionalValue(mod)) |payload_val| { 20169 union_obj.tag_ty = payload_val.toType(); 20170 20171 const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) { 20172 .enum_type => |x| x, 20173 else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), 20174 }; 20175 20176 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); 20177 @memset(explicit_tags_seen, false); 20178 } else { 20179 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 20180 } 20181 20182 // Fields 20183 try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); 20184 20185 for (0..fields_len) |i| { 20186 const elem_val = try fields_val.elemValue(mod, i); 20187 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 20188 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20189 try ip.getOrPutString(gpa, "name"), 20190 ).?); 20191 const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20192 try ip.getOrPutString(gpa, "type"), 20193 ).?); 20194 const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20195 try ip.getOrPutString(gpa, "alignment"), 20196 ).?); 20197 20198 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 20199 20200 if (enum_field_names.len != 0) { 20201 enum_field_names[i] = field_name; 20202 } 20203 20204 if (explicit_tags_seen.len > 0) { 20205 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; 20206 const enum_index = tag_info.nameIndex(ip, field_name) orelse { 20207 const msg = msg: { 20208 const msg = try sema.errMsg(block, src, "no field named '{}' in enum '{}'", .{ 20209 field_name.fmt(ip), 20210 union_obj.tag_ty.fmt(mod), 20211 }); 20212 errdefer msg.destroy(gpa); 20213 try sema.addDeclaredHereNote(msg, union_obj.tag_ty); 20214 break :msg msg; 20215 }; 20216 return sema.failWithOwnedErrorMsg(msg); 20217 }; 20218 // No check for duplicate because the check already happened in order 20219 // to create the enum type in the first place. 20220 assert(!explicit_tags_seen[enum_index]); 20221 explicit_tags_seen[enum_index] = true; 20222 } 20223 20224 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); 20225 if (gop.found_existing) { 20226 // TODO: better source location 20227 return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); 20228 } 20229 20230 const field_ty = type_val.toType(); 20231 gop.value_ptr.* = .{ 20232 .ty = field_ty, 20233 .abi_align = Alignment.fromByteUnits((try alignment_val.getUnsignedIntAdvanced(mod, sema)).?), 20234 }; 20235 20236 if (field_ty.zigTypeTag(mod) == .Opaque) { 20237 const msg = msg: { 20238 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 20239 errdefer msg.destroy(gpa); 20240 20241 try sema.addDeclaredHereNote(msg, field_ty); 20242 break :msg msg; 20243 }; 20244 return sema.failWithOwnedErrorMsg(msg); 20245 } 20246 if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { 20247 const msg = msg: { 20248 const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 20249 errdefer msg.destroy(gpa); 20250 20251 const src_decl = mod.declPtr(block.src_decl); 20252 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field); 20253 20254 try sema.addDeclaredHereNote(msg, field_ty); 20255 break :msg msg; 20256 }; 20257 return sema.failWithOwnedErrorMsg(msg); 20258 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { 20259 const msg = msg: { 20260 const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 20261 errdefer msg.destroy(gpa); 20262 20263 const src_decl = mod.declPtr(block.src_decl); 20264 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); 20265 20266 try sema.addDeclaredHereNote(msg, field_ty); 20267 break :msg msg; 20268 }; 20269 return sema.failWithOwnedErrorMsg(msg); 20270 } 20271 } 20272 20273 if (explicit_tags_seen.len > 0) { 20274 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; 20275 if (tag_info.names.len > fields_len) { 20276 const msg = msg: { 20277 const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); 20278 errdefer msg.destroy(gpa); 20279 20280 const enum_ty = union_obj.tag_ty; 20281 for (tag_info.names, 0..) |field_name, field_index| { 20282 if (explicit_tags_seen[field_index]) continue; 20283 try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{ 20284 field_name.fmt(ip), 20285 }); 20286 } 20287 try sema.addDeclaredHereNote(msg, union_obj.tag_ty); 20288 break :msg msg; 20289 }; 20290 return sema.failWithOwnedErrorMsg(msg); 20291 } 20292 } else { 20293 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, null); 20294 } 20295 20296 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20297 try mod.finalizeAnonDecl(new_decl_index); 20298 return decl_val; 20299 }, 20300 .Fn => { 20301 const fields = ip.typeOf(union_val.val).toType().structFields(mod); 20302 const calling_convention_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20303 try ip.getOrPutString(gpa, "calling_convention"), 20304 ).?); 20305 const alignment_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20306 try ip.getOrPutString(gpa, "alignment"), 20307 ).?); 20308 const is_generic_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20309 try ip.getOrPutString(gpa, "is_generic"), 20310 ).?); 20311 const is_var_args_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20312 try ip.getOrPutString(gpa, "is_var_args"), 20313 ).?); 20314 const return_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20315 try ip.getOrPutString(gpa, "return_type"), 20316 ).?); 20317 const params_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex( 20318 try ip.getOrPutString(gpa, "params"), 20319 ).?); 20320 20321 const is_generic = is_generic_val.toBool(); 20322 if (is_generic) { 20323 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{}); 20324 } 20325 20326 const is_var_args = is_var_args_val.toBool(); 20327 const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val); 20328 if (is_var_args and cc != .C) { 20329 return sema.fail(block, src, "varargs functions must have C calling convention", .{}); 20330 } 20331 20332 const alignment = alignment: { 20333 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { 20334 return sema.fail(block, src, "alignment must fit in 'u32'", .{}); 20335 } 20336 const alignment = @as(u29, @intCast(alignment_val.toUnsignedInt(mod))); 20337 if (alignment == target_util.defaultFunctionAlignment(target)) { 20338 break :alignment .none; 20339 } else { 20340 break :alignment Alignment.fromByteUnits(alignment); 20341 } 20342 }; 20343 const return_type = return_type_val.optionalValue(mod) orelse 20344 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); 20345 20346 const args_len = try sema.usizeCast(block, src, params_val.sliceLen(mod)); 20347 const param_types = try sema.arena.alloc(InternPool.Index, args_len); 20348 20349 var noalias_bits: u32 = 0; 20350 for (param_types, 0..) |*param_type, i| { 20351 const elem_val = try params_val.elemValue(mod, i); 20352 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 20353 const param_is_generic_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20354 try ip.getOrPutString(gpa, "is_generic"), 20355 ).?); 20356 const param_is_noalias_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20357 try ip.getOrPutString(gpa, "is_noalias"), 20358 ).?); 20359 const opt_param_type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20360 try ip.getOrPutString(gpa, "type"), 20361 ).?); 20362 20363 if (param_is_generic_val.toBool()) { 20364 return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{}); 20365 } 20366 20367 const param_type_val = opt_param_type_val.optionalValue(mod) orelse 20368 return sema.fail(block, src, "Type.Fn.Param.arg_type must be non-null for @Type", .{}); 20369 param_type.* = param_type_val.toIntern(); 20370 20371 if (param_is_noalias_val.toBool()) { 20372 if (!param_type.toType().isPtrAtRuntime(mod)) { 20373 return sema.fail(block, src, "non-pointer parameter declared noalias", .{}); 20374 } 20375 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse 20376 return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 20377 } 20378 } 20379 20380 const ty = try mod.funcType(.{ 20381 .param_types = param_types, 20382 .comptime_bits = 0, 20383 .noalias_bits = noalias_bits, 20384 .return_type = return_type.toIntern(), 20385 .alignment = alignment, 20386 .cc = cc, 20387 .is_var_args = is_var_args, 20388 .is_generic = false, 20389 .is_noinline = false, 20390 .align_is_generic = false, 20391 .cc_is_generic = false, 20392 .section_is_generic = false, 20393 .addrspace_is_generic = false, 20394 }); 20395 return sema.addType(ty); 20396 }, 20397 .Frame => return sema.failWithUseOfAsync(block, src), 20398 } 20399 } 20400 20401 fn reifyStruct( 20402 sema: *Sema, 20403 block: *Block, 20404 inst: Zir.Inst.Index, 20405 src: LazySrcLoc, 20406 layout: std.builtin.Type.ContainerLayout, 20407 backing_int_val: Value, 20408 fields_val: Value, 20409 name_strategy: Zir.Inst.NameStrategy, 20410 is_tuple: bool, 20411 ) CompileError!Air.Inst.Ref { 20412 const mod = sema.mod; 20413 const gpa = sema.gpa; 20414 const ip = &mod.intern_pool; 20415 20416 // Because these three things each reference each other, `undefined` 20417 // placeholders are used before being set after the struct type gains an 20418 // InternPool index. 20419 20420 const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ 20421 .ty = Type.noreturn, 20422 .val = Value.@"unreachable", 20423 }, name_strategy, "struct", inst); 20424 const new_decl = mod.declPtr(new_decl_index); 20425 new_decl.owns_tv = true; 20426 errdefer { 20427 new_decl.has_tv = false; // namespace and val were destroyed by later errdefers 20428 mod.abortAnonDecl(new_decl_index); 20429 } 20430 20431 const new_namespace_index = try mod.createNamespace(.{ 20432 .parent = block.namespace.toOptional(), 20433 .ty = undefined, 20434 .file_scope = block.getFileScope(mod), 20435 }); 20436 const new_namespace = mod.namespacePtr(new_namespace_index); 20437 errdefer mod.destroyNamespace(new_namespace_index); 20438 20439 const struct_index = try mod.createStruct(.{ 20440 .owner_decl = new_decl_index, 20441 .fields = .{}, 20442 .zir_index = inst, 20443 .layout = layout, 20444 .status = .have_field_types, 20445 .known_non_opv = false, 20446 .is_tuple = is_tuple, 20447 .namespace = new_namespace_index, 20448 }); 20449 const struct_obj = mod.structPtr(struct_index); 20450 errdefer mod.destroyStruct(struct_index); 20451 20452 const struct_ty = try ip.get(gpa, .{ .struct_type = .{ 20453 .index = struct_index.toOptional(), 20454 .namespace = new_namespace_index.toOptional(), 20455 } }); 20456 // TODO: figure out InternPool removals for incremental compilation 20457 //errdefer ip.remove(struct_ty); 20458 20459 new_decl.ty = Type.type; 20460 new_decl.val = struct_ty.toValue(); 20461 new_namespace.ty = struct_ty.toType(); 20462 20463 // Fields 20464 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); 20465 try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); 20466 var i: usize = 0; 20467 while (i < fields_len) : (i += 1) { 20468 const elem_val = try fields_val.elemValue(mod, i); 20469 const elem_fields = ip.typeOf(elem_val.toIntern()).toType().structFields(mod); 20470 const name_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20471 try ip.getOrPutString(gpa, "name"), 20472 ).?); 20473 const type_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20474 try ip.getOrPutString(gpa, "type"), 20475 ).?); 20476 const default_value_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20477 try ip.getOrPutString(gpa, "default_value"), 20478 ).?); 20479 const is_comptime_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20480 try ip.getOrPutString(gpa, "is_comptime"), 20481 ).?); 20482 const alignment_val = try elem_val.fieldValue(mod, elem_fields.getIndex( 20483 try ip.getOrPutString(gpa, "alignment"), 20484 ).?); 20485 20486 if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { 20487 return sema.fail(block, src, "alignment must fit in 'u32'", .{}); 20488 } 20489 const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; 20490 20491 if (layout == .Packed) { 20492 if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); 20493 if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}); 20494 } 20495 if (layout == .Extern and is_comptime_val.toBool()) { 20496 return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}); 20497 } 20498 20499 const field_name = try name_val.toIpString(Type.slice_const_u8, mod); 20500 20501 if (is_tuple) { 20502 const field_index = field_name.toUnsigned(ip) orelse return sema.fail( 20503 block, 20504 src, 20505 "tuple cannot have non-numeric field '{}'", 20506 .{field_name.fmt(ip)}, 20507 ); 20508 20509 if (field_index >= fields_len) { 20510 return sema.fail( 20511 block, 20512 src, 20513 "tuple field {} exceeds tuple field count", 20514 .{field_index}, 20515 ); 20516 } 20517 } 20518 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); 20519 if (gop.found_existing) { 20520 // TODO: better source location 20521 return sema.fail(block, src, "duplicate struct field {}", .{field_name.fmt(ip)}); 20522 } 20523 20524 const field_ty = type_val.toType(); 20525 const default_val = if (default_value_val.optionalValue(mod)) |opt_val| 20526 (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse 20527 return sema.failWithNeededComptime(block, src, "struct field default value must be comptime-known")).toIntern() 20528 else 20529 .none; 20530 if (is_comptime_val.toBool() and default_val == .none) { 20531 return sema.fail(block, src, "comptime field without default initialization value", .{}); 20532 } 20533 20534 gop.value_ptr.* = .{ 20535 .ty = field_ty, 20536 .abi_align = Alignment.fromByteUnits(abi_align), 20537 .default_val = default_val, 20538 .is_comptime = is_comptime_val.toBool(), 20539 .offset = undefined, 20540 }; 20541 20542 if (field_ty.zigTypeTag(mod) == .Opaque) { 20543 const msg = msg: { 20544 const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 20545 errdefer msg.destroy(gpa); 20546 20547 try sema.addDeclaredHereNote(msg, field_ty); 20548 break :msg msg; 20549 }; 20550 return sema.failWithOwnedErrorMsg(msg); 20551 } 20552 if (field_ty.zigTypeTag(mod) == .NoReturn) { 20553 const msg = msg: { 20554 const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{}); 20555 errdefer msg.destroy(gpa); 20556 20557 try sema.addDeclaredHereNote(msg, field_ty); 20558 break :msg msg; 20559 }; 20560 return sema.failWithOwnedErrorMsg(msg); 20561 } 20562 if (struct_obj.layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) { 20563 const msg = msg: { 20564 const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); 20565 errdefer msg.destroy(gpa); 20566 20567 const src_decl = sema.mod.declPtr(block.src_decl); 20568 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field); 20569 20570 try sema.addDeclaredHereNote(msg, field_ty); 20571 break :msg msg; 20572 }; 20573 return sema.failWithOwnedErrorMsg(msg); 20574 } else if (struct_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { 20575 const msg = msg: { 20576 const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); 20577 errdefer msg.destroy(gpa); 20578 20579 const src_decl = sema.mod.declPtr(block.src_decl); 20580 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); 20581 20582 try sema.addDeclaredHereNote(msg, field_ty); 20583 break :msg msg; 20584 }; 20585 return sema.failWithOwnedErrorMsg(msg); 20586 } 20587 } 20588 20589 if (layout == .Packed) { 20590 struct_obj.status = .layout_wip; 20591 20592 for (struct_obj.fields.values(), 0..) |field, index| { 20593 sema.resolveTypeLayout(field.ty) catch |err| switch (err) { 20594 error.AnalysisFail => { 20595 const msg = sema.err orelse return err; 20596 try sema.addFieldErrNote(struct_ty.toType(), index, msg, "while checking this field", .{}); 20597 return err; 20598 }, 20599 else => return err, 20600 }; 20601 } 20602 20603 var fields_bit_sum: u64 = 0; 20604 for (struct_obj.fields.values()) |field| { 20605 fields_bit_sum += field.ty.bitSize(mod); 20606 } 20607 20608 if (backing_int_val.optionalValue(mod)) |payload| { 20609 const backing_int_ty = payload.toType(); 20610 try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); 20611 struct_obj.backing_int_ty = backing_int_ty; 20612 } else { 20613 struct_obj.backing_int_ty = try mod.intType(.unsigned, @as(u16, @intCast(fields_bit_sum))); 20614 } 20615 20616 struct_obj.status = .have_layout; 20617 } 20618 20619 const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); 20620 try mod.finalizeAnonDecl(new_decl_index); 20621 return decl_val; 20622 } 20623 20624 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { 20625 const va_list_ty = try sema.getBuiltinType("VaList"); 20626 const va_list_ptr = try sema.mod.singleMutPtrType(va_list_ty); 20627 20628 const inst = try sema.resolveInst(zir_ref); 20629 return sema.coerce(block, va_list_ptr, inst, src); 20630 } 20631 20632 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20633 const mod = sema.mod; 20634 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 20635 const src = LazySrcLoc.nodeOffset(extra.node); 20636 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20637 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 20638 20639 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs); 20640 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs); 20641 20642 if (!try sema.validateExternType(arg_ty, .param_ty)) { 20643 const msg = msg: { 20644 const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)}); 20645 errdefer msg.destroy(sema.gpa); 20646 20647 const src_decl = sema.mod.declPtr(block.src_decl); 20648 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty); 20649 20650 try sema.addDeclaredHereNote(msg, arg_ty); 20651 break :msg msg; 20652 }; 20653 return sema.failWithOwnedErrorMsg(msg); 20654 } 20655 20656 try sema.requireRuntimeBlock(block, src, null); 20657 return block.addTyOp(.c_va_arg, arg_ty, va_list_ref); 20658 } 20659 20660 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20661 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 20662 const src = LazySrcLoc.nodeOffset(extra.node); 20663 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20664 20665 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 20666 const va_list_ty = try sema.getBuiltinType("VaList"); 20667 20668 try sema.requireRuntimeBlock(block, src, null); 20669 return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref); 20670 } 20671 20672 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20673 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 20674 const src = LazySrcLoc.nodeOffset(extra.node); 20675 const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20676 20677 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); 20678 20679 try sema.requireRuntimeBlock(block, src, null); 20680 return block.addUnOp(.c_va_end, va_list_ref); 20681 } 20682 20683 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20684 const src = LazySrcLoc.nodeOffset(@as(i32, @bitCast(extended.operand))); 20685 20686 const va_list_ty = try sema.getBuiltinType("VaList"); 20687 try sema.requireRuntimeBlock(block, src, null); 20688 return block.addInst(.{ 20689 .tag = .c_va_start, 20690 .data = .{ .ty = va_list_ty }, 20691 }); 20692 } 20693 20694 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20695 const mod = sema.mod; 20696 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 20697 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20698 const ty = try sema.resolveType(block, ty_src, inst_data.operand); 20699 20700 var anon_decl = try block.startAnonDecl(); 20701 defer anon_decl.deinit(); 20702 20703 var bytes = std.ArrayList(u8).init(sema.arena); 20704 defer bytes.deinit(); 20705 try ty.print(bytes.writer(), mod); 20706 20707 const decl_ty = try mod.arrayType(.{ 20708 .len = bytes.items.len, 20709 .sentinel = .zero_u8, 20710 .child = .u8_type, 20711 }); 20712 const new_decl = try anon_decl.finish( 20713 decl_ty, 20714 (try mod.intern(.{ .aggregate = .{ 20715 .ty = decl_ty.toIntern(), 20716 .storage = .{ .bytes = bytes.items }, 20717 } })).toValue(), 20718 .none, // default alignment 20719 ); 20720 20721 return sema.analyzeDeclRef(new_decl); 20722 } 20723 20724 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20725 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 20726 const src = inst_data.src(); 20727 return sema.failWithUseOfAsync(block, src); 20728 } 20729 20730 fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20731 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 20732 const src = inst_data.src(); 20733 return sema.failWithUseOfAsync(block, src); 20734 } 20735 20736 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20737 const mod = sema.mod; 20738 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 20739 const src = inst_data.src(); 20740 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 20741 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20742 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat"); 20743 const operand = try sema.resolveInst(extra.rhs); 20744 const operand_ty = sema.typeOf(operand); 20745 20746 _ = try sema.checkIntType(block, src, dest_ty); 20747 try sema.checkFloatType(block, operand_src, operand_ty); 20748 20749 if (try sema.resolveMaybeUndefVal(operand)) |val| { 20750 const result_val = try sema.intFromFloat(block, operand_src, val, operand_ty, dest_ty); 20751 return sema.addConstant(result_val); 20752 } else if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { 20753 return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime-known"); 20754 } 20755 20756 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 20757 if (dest_ty.intInfo(mod).bits == 0) { 20758 if (block.wantSafety()) { 20759 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))); 20760 try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds); 20761 } 20762 return sema.addConstant(try mod.intValue(dest_ty, 0)); 20763 } 20764 const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_ty, operand); 20765 if (block.wantSafety()) { 20766 const back = try block.addTyOp(.float_from_int, operand_ty, result); 20767 const diff = try block.addBinOp(.sub, operand, back); 20768 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))); 20769 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))); 20770 const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg); 20771 try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds); 20772 } 20773 return result; 20774 } 20775 20776 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20777 const mod = sema.mod; 20778 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 20779 const src = inst_data.src(); 20780 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 20781 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20782 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt"); 20783 const operand = try sema.resolveInst(extra.rhs); 20784 const operand_ty = sema.typeOf(operand); 20785 20786 try sema.checkFloatType(block, src, dest_ty); 20787 _ = try sema.checkIntType(block, operand_src, operand_ty); 20788 20789 if (try sema.resolveMaybeUndefVal(operand)) |val| { 20790 const result_val = try val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, sema.mod, sema); 20791 return sema.addConstant(result_val); 20792 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { 20793 return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime-known"); 20794 } 20795 20796 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 20797 return block.addTyOp(.float_from_int, dest_ty, operand); 20798 } 20799 20800 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20801 const mod = sema.mod; 20802 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 20803 const src = inst_data.src(); 20804 20805 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 20806 20807 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20808 const operand_res = try sema.resolveInst(extra.rhs); 20809 const operand_coerced = try sema.coerce(block, Type.usize, operand_res, operand_src); 20810 20811 const ptr_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt"); 20812 try sema.checkPtrType(block, src, ptr_ty); 20813 const elem_ty = ptr_ty.elemType2(mod); 20814 const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema); 20815 20816 if (ptr_ty.isSlice(mod)) { 20817 const msg = msg: { 20818 const msg = try sema.errMsg(block, src, "integer cannot be converted to slice type '{}'", .{ptr_ty.fmt(sema.mod)}); 20819 errdefer msg.destroy(sema.gpa); 20820 try sema.errNote(block, src, msg, "slice length cannot be inferred from address", .{}); 20821 break :msg msg; 20822 }; 20823 return sema.failWithOwnedErrorMsg(msg); 20824 } 20825 20826 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { 20827 const addr = val.toUnsignedInt(mod); 20828 if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0) 20829 return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)}); 20830 if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0) 20831 return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)}); 20832 20833 const ptr_val = switch (ptr_ty.zigTypeTag(mod)) { 20834 .Optional => (try mod.intern(.{ .opt = .{ 20835 .ty = ptr_ty.toIntern(), 20836 .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(), 20837 } })).toValue(), 20838 .Pointer => try mod.ptrIntValue(ptr_ty, addr), 20839 else => unreachable, 20840 }; 20841 return sema.addConstant(ptr_val); 20842 } 20843 20844 try sema.requireRuntimeBlock(block, src, operand_src); 20845 if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) { 20846 if (!ptr_ty.isAllowzeroPtr(mod)) { 20847 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); 20848 try sema.addSafetyCheck(block, is_non_zero, .cast_to_null); 20849 } 20850 20851 if (ptr_align > 1) { 20852 const align_minus_1 = try sema.addConstant( 20853 try mod.intValue(Type.usize, ptr_align - 1), 20854 ); 20855 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1); 20856 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 20857 try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment); 20858 } 20859 } 20860 return block.addBitCast(ptr_ty, operand_coerced); 20861 } 20862 20863 fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20864 const mod = sema.mod; 20865 const ip = &mod.intern_pool; 20866 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 20867 const src = LazySrcLoc.nodeOffset(extra.node); 20868 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 20869 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@errSetCast"); 20870 const operand = try sema.resolveInst(extra.rhs); 20871 const operand_ty = sema.typeOf(operand); 20872 try sema.checkErrorSetType(block, src, dest_ty); 20873 try sema.checkErrorSetType(block, operand_src, operand_ty); 20874 20875 // operand must be defined since it can be an invalid error value 20876 const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand); 20877 20878 if (disjoint: { 20879 // Try avoiding resolving inferred error sets if we can 20880 if (!dest_ty.isAnyError(mod) and dest_ty.errorSetNames(mod).len == 0) break :disjoint true; 20881 if (!operand_ty.isAnyError(mod) and operand_ty.errorSetNames(mod).len == 0) break :disjoint true; 20882 if (dest_ty.isAnyError(mod)) break :disjoint false; 20883 if (operand_ty.isAnyError(mod)) break :disjoint false; 20884 for (dest_ty.errorSetNames(mod)) |dest_err_name| { 20885 if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) 20886 break :disjoint false; 20887 } 20888 20889 if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and 20890 !ip.isInferredErrorSetType(operand_ty.toIntern())) 20891 { 20892 break :disjoint true; 20893 } 20894 20895 try sema.resolveInferredErrorSetTy(block, src, dest_ty); 20896 try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty); 20897 for (dest_ty.errorSetNames(mod)) |dest_err_name| { 20898 if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) 20899 break :disjoint false; 20900 } 20901 20902 break :disjoint true; 20903 }) { 20904 const msg = msg: { 20905 const msg = try sema.errMsg( 20906 block, 20907 src, 20908 "error sets '{}' and '{}' have no common errors", 20909 .{ operand_ty.fmt(sema.mod), dest_ty.fmt(sema.mod) }, 20910 ); 20911 errdefer msg.destroy(sema.gpa); 20912 try sema.addDeclaredHereNote(msg, operand_ty); 20913 try sema.addDeclaredHereNote(msg, dest_ty); 20914 break :msg msg; 20915 }; 20916 return sema.failWithOwnedErrorMsg(msg); 20917 } 20918 20919 if (maybe_operand_val) |val| { 20920 if (!dest_ty.isAnyError(mod)) { 20921 const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; 20922 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) { 20923 const msg = msg: { 20924 const msg = try sema.errMsg( 20925 block, 20926 src, 20927 "'error.{}' not a member of error set '{}'", 20928 .{ error_name.fmt(ip), dest_ty.fmt(sema.mod) }, 20929 ); 20930 errdefer msg.destroy(sema.gpa); 20931 try sema.addDeclaredHereNote(msg, dest_ty); 20932 break :msg msg; 20933 }; 20934 return sema.failWithOwnedErrorMsg(msg); 20935 } 20936 } 20937 20938 return sema.addConstant(try mod.getCoerced(val, dest_ty)); 20939 } 20940 20941 try sema.requireRuntimeBlock(block, src, operand_src); 20942 if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) { 20943 const err_int_inst = try block.addBitCast(Type.err_int, operand); 20944 const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); 20945 try sema.addSafetyCheck(block, ok, .invalid_error_code); 20946 } 20947 return block.addBitCast(dest_ty, operand); 20948 } 20949 20950 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 20951 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(u5, @truncate(extended.small))); 20952 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 20953 const src = LazySrcLoc.nodeOffset(extra.node); 20954 const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node }; 20955 const operand = try sema.resolveInst(extra.rhs); 20956 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName()); 20957 return sema.ptrCastFull( 20958 block, 20959 flags, 20960 src, 20961 operand, 20962 operand_src, 20963 dest_ty, 20964 ); 20965 } 20966 20967 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 20968 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 20969 const src = inst_data.src(); 20970 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 20971 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 20972 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu, "@ptrCast"); 20973 const operand = try sema.resolveInst(extra.rhs); 20974 20975 return sema.ptrCastFull( 20976 block, 20977 .{ .ptr_cast = true }, 20978 src, 20979 operand, 20980 operand_src, 20981 dest_ty, 20982 ); 20983 } 20984 20985 fn ptrCastFull( 20986 sema: *Sema, 20987 block: *Block, 20988 flags: Zir.Inst.FullPtrCastFlags, 20989 src: LazySrcLoc, 20990 operand: Air.Inst.Ref, 20991 operand_src: LazySrcLoc, 20992 dest_ty: Type, 20993 ) CompileError!Air.Inst.Ref { 20994 const mod = sema.mod; 20995 const operand_ty = sema.typeOf(operand); 20996 20997 try sema.checkPtrType(block, src, dest_ty); 20998 try sema.checkPtrOperand(block, operand_src, operand_ty); 20999 21000 const src_info = operand_ty.ptrInfo(mod); 21001 const dest_info = dest_ty.ptrInfo(mod); 21002 21003 try sema.resolveTypeLayout(src_info.child.toType()); 21004 try sema.resolveTypeLayout(dest_info.child.toType()); 21005 21006 const src_slice_like = src_info.flags.size == .Slice or 21007 (src_info.flags.size == .One and src_info.child.toType().zigTypeTag(mod) == .Array); 21008 21009 const dest_slice_like = dest_info.flags.size == .Slice or 21010 (dest_info.flags.size == .One and dest_info.child.toType().zigTypeTag(mod) == .Array); 21011 21012 if (dest_info.flags.size == .Slice and !src_slice_like) { 21013 return sema.fail(block, src, "illegal pointer cast to slice", .{}); 21014 } 21015 21016 if (dest_info.flags.size == .Slice) { 21017 const src_elem_size = switch (src_info.flags.size) { 21018 .Slice => src_info.child.toType().abiSize(mod), 21019 // pointer to array 21020 .One => src_info.child.toType().childType(mod).abiSize(mod), 21021 else => unreachable, 21022 }; 21023 const dest_elem_size = dest_info.child.toType().abiSize(mod); 21024 if (src_elem_size != dest_elem_size) { 21025 return sema.fail(block, src, "TODO: implement @ptrCast between slices changing the length", .{}); 21026 } 21027 } 21028 21029 // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs 21030 21031 if (!flags.ptr_cast) { 21032 check_size: { 21033 if (src_info.flags.size == dest_info.flags.size) break :check_size; 21034 if (src_slice_like and dest_slice_like) break :check_size; 21035 if (src_info.flags.size == .C) break :check_size; 21036 if (dest_info.flags.size == .C) break :check_size; 21037 return sema.failWithOwnedErrorMsg(msg: { 21038 const msg = try sema.errMsg(block, src, "cannot implicitly convert {s} pointer to {s} pointer", .{ 21039 pointerSizeString(src_info.flags.size), 21040 pointerSizeString(dest_info.flags.size), 21041 }); 21042 errdefer msg.destroy(sema.gpa); 21043 if (dest_info.flags.size == .Many and 21044 (src_info.flags.size == .Slice or 21045 (src_info.flags.size == .One and src_info.child.toType().zigTypeTag(mod) == .Array))) 21046 { 21047 try sema.errNote(block, src, msg, "use 'ptr' field to convert slice to many pointer", .{}); 21048 } else { 21049 try sema.errNote(block, src, msg, "use @ptrCast to change pointer size", .{}); 21050 } 21051 break :msg msg; 21052 }); 21053 } 21054 21055 check_child: { 21056 const src_child = if (dest_info.flags.size == .Slice and src_info.flags.size == .One) blk: { 21057 // *[n]T -> []T 21058 break :blk src_info.child.toType().childType(mod); 21059 } else src_info.child.toType(); 21060 21061 const dest_child = dest_info.child.toType(); 21062 21063 const imc_res = try sema.coerceInMemoryAllowed( 21064 block, 21065 dest_child, 21066 src_child, 21067 !dest_info.flags.is_const, 21068 mod.getTarget(), 21069 src, 21070 operand_src, 21071 ); 21072 if (imc_res == .ok) break :check_child; 21073 return sema.failWithOwnedErrorMsg(msg: { 21074 const msg = try sema.errMsg(block, src, "pointer element type '{}' cannot coerce into element type '{}'", .{ 21075 src_child.fmt(mod), 21076 dest_child.fmt(mod), 21077 }); 21078 errdefer msg.destroy(sema.gpa); 21079 try imc_res.report(sema, block, src, msg); 21080 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer element type", .{}); 21081 break :msg msg; 21082 }); 21083 } 21084 21085 check_sent: { 21086 if (dest_info.sentinel == .none) break :check_sent; 21087 if (src_info.flags.size == .C) break :check_sent; 21088 if (src_info.sentinel != .none) { 21089 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child); 21090 if (dest_info.sentinel == coerced_sent) break :check_sent; 21091 } 21092 if (src_slice_like and src_info.flags.size == .One and dest_info.flags.size == .Slice) { 21093 // [*]nT -> []T 21094 const arr_ty = src_info.child.toType(); 21095 if (arr_ty.sentinel(mod)) |src_sentinel| { 21096 const coerced_sent = try mod.intern_pool.getCoerced(sema.gpa, src_sentinel.toIntern(), dest_info.child); 21097 if (dest_info.sentinel == coerced_sent) break :check_sent; 21098 } 21099 } 21100 return sema.failWithOwnedErrorMsg(msg: { 21101 const msg = if (src_info.sentinel == .none) blk: { 21102 break :blk try sema.errMsg(block, src, "destination pointer requires '{}' sentinel", .{ 21103 dest_info.sentinel.toValue().fmtValue(dest_info.child.toType(), mod), 21104 }); 21105 } else blk: { 21106 break :blk try sema.errMsg(block, src, "pointer sentinel '{}' cannot coerce into pointer sentinel '{}'", .{ 21107 src_info.sentinel.toValue().fmtValue(src_info.child.toType(), mod), 21108 dest_info.sentinel.toValue().fmtValue(dest_info.child.toType(), mod), 21109 }); 21110 }; 21111 errdefer msg.destroy(sema.gpa); 21112 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer sentinel", .{}); 21113 break :msg msg; 21114 }); 21115 } 21116 21117 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) { 21118 return sema.failWithOwnedErrorMsg(msg: { 21119 const msg = try sema.errMsg(block, src, "pointer host size '{}' cannot coerce into pointer host size '{}'", .{ 21120 src_info.packed_offset.host_size, 21121 dest_info.packed_offset.host_size, 21122 }); 21123 errdefer msg.destroy(sema.gpa); 21124 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer host size", .{}); 21125 break :msg msg; 21126 }); 21127 } 21128 21129 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) { 21130 return sema.failWithOwnedErrorMsg(msg: { 21131 const msg = try sema.errMsg(block, src, "pointer bit offset '{}' cannot coerce into pointer bit offset '{}'", .{ 21132 src_info.packed_offset.bit_offset, 21133 dest_info.packed_offset.bit_offset, 21134 }); 21135 errdefer msg.destroy(sema.gpa); 21136 try sema.errNote(block, src, msg, "use @ptrCast to cast pointer bit offset", .{}); 21137 break :msg msg; 21138 }); 21139 } 21140 21141 check_allowzero: { 21142 const src_allows_zero = operand_ty.ptrAllowsZero(mod); 21143 const dest_allows_zero = dest_ty.ptrAllowsZero(mod); 21144 if (!src_allows_zero) break :check_allowzero; 21145 if (dest_allows_zero) break :check_allowzero; 21146 21147 return sema.failWithOwnedErrorMsg(msg: { 21148 const msg = try sema.errMsg(block, src, "'{}' could have null values which are illegal in type '{}'", .{ 21149 operand_ty.fmt(mod), 21150 dest_ty.fmt(mod), 21151 }); 21152 errdefer msg.destroy(sema.gpa); 21153 try sema.errNote(block, src, msg, "use @ptrCast to assert the pointer is not null", .{}); 21154 break :msg msg; 21155 }); 21156 } 21157 21158 // TODO: vector index? 21159 } 21160 21161 const src_align = src_info.flags.alignment.toByteUnitsOptional() orelse src_info.child.toType().abiAlignment(mod); 21162 const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse dest_info.child.toType().abiAlignment(mod); 21163 if (!flags.align_cast) { 21164 if (dest_align > src_align) { 21165 return sema.failWithOwnedErrorMsg(msg: { 21166 const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{}); 21167 errdefer msg.destroy(sema.gpa); 21168 try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{ 21169 operand_ty.fmt(mod), src_align, 21170 }); 21171 try sema.errNote(block, src, msg, "'{}' has alignment '{d}'", .{ 21172 dest_ty.fmt(mod), dest_align, 21173 }); 21174 try sema.errNote(block, src, msg, "use @alignCast to assert pointer alignment", .{}); 21175 break :msg msg; 21176 }); 21177 } 21178 } 21179 21180 if (!flags.addrspace_cast) { 21181 if (src_info.flags.address_space != dest_info.flags.address_space) { 21182 return sema.failWithOwnedErrorMsg(msg: { 21183 const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{}); 21184 errdefer msg.destroy(sema.gpa); 21185 try sema.errNote(block, operand_src, msg, "'{}' has address space '{s}'", .{ 21186 operand_ty.fmt(mod), @tagName(src_info.flags.address_space), 21187 }); 21188 try sema.errNote(block, src, msg, "'{}' has address space '{s}'", .{ 21189 dest_ty.fmt(mod), @tagName(dest_info.flags.address_space), 21190 }); 21191 try sema.errNote(block, src, msg, "use @addrSpaceCast to cast pointer address space", .{}); 21192 break :msg msg; 21193 }); 21194 } 21195 } else { 21196 // Some address space casts are always disallowed 21197 if (!target_util.addrSpaceCastIsValid(mod.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) { 21198 return sema.failWithOwnedErrorMsg(msg: { 21199 const msg = try sema.errMsg(block, src, "invalid address space cast", .{}); 21200 errdefer msg.destroy(sema.gpa); 21201 try sema.errNote(block, operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{ 21202 @tagName(src_info.flags.address_space), 21203 @tagName(dest_info.flags.address_space), 21204 }); 21205 break :msg msg; 21206 }); 21207 } 21208 } 21209 21210 if (!flags.const_cast) { 21211 if (src_info.flags.is_const and !dest_info.flags.is_const) { 21212 return sema.failWithOwnedErrorMsg(msg: { 21213 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{}); 21214 errdefer msg.destroy(sema.gpa); 21215 try sema.errNote(block, src, msg, "use @constCast to discard const qualifier", .{}); 21216 break :msg msg; 21217 }); 21218 } 21219 } 21220 21221 if (!flags.volatile_cast) { 21222 if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) { 21223 return sema.failWithOwnedErrorMsg(msg: { 21224 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{}); 21225 errdefer msg.destroy(sema.gpa); 21226 try sema.errNote(block, src, msg, "use @volatileCast to discard volatile qualifier", .{}); 21227 break :msg msg; 21228 }); 21229 } 21230 } 21231 21232 const ptr = if (src_info.flags.size == .Slice and dest_info.flags.size != .Slice) ptr: { 21233 break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); 21234 } else operand; 21235 21236 const dest_ptr_ty = if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) blk: { 21237 // Only convert to a many-pointer at first 21238 var info = dest_info; 21239 info.flags.size = .Many; 21240 const ty = try mod.ptrType(info); 21241 if (dest_ty.zigTypeTag(mod) == .Optional) { 21242 break :blk try mod.optionalType(ty.toIntern()); 21243 } else { 21244 break :blk ty; 21245 } 21246 } else dest_ty; 21247 21248 // Cannot do @addrSpaceCast at comptime 21249 if (!flags.addrspace_cast) { 21250 if (try sema.resolveMaybeUndefVal(ptr)) |ptr_val| { 21251 if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isUndef(mod)) { 21252 return sema.failWithUseOfUndef(block, operand_src); 21253 } 21254 if (!dest_ty.ptrAllowsZero(mod) and ptr_val.isNull(mod)) { 21255 return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)}); 21256 } 21257 if (dest_align > src_align) { 21258 if (try ptr_val.getUnsignedIntAdvanced(mod, null)) |addr| { 21259 if (addr % dest_align != 0) { 21260 return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ addr, dest_align }); 21261 } 21262 } 21263 } 21264 if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) { 21265 if (ptr_val.isUndef(mod)) return sema.addConstUndef(dest_ty); 21266 const arr_len = try mod.intValue(Type.usize, src_info.child.toType().arrayLen(mod)); 21267 return sema.addConstant((try mod.intern(.{ .ptr = .{ 21268 .ty = dest_ty.toIntern(), 21269 .addr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr, 21270 .len = arr_len.toIntern(), 21271 } })).toValue()); 21272 } else { 21273 assert(dest_ptr_ty.eql(dest_ty, mod)); 21274 return sema.addConstant(try mod.getCoerced(ptr_val, dest_ty)); 21275 } 21276 } 21277 } 21278 21279 try sema.requireRuntimeBlock(block, src, null); 21280 21281 if (block.wantSafety() and operand_ty.ptrAllowsZero(mod) and !dest_ty.ptrAllowsZero(mod) and 21282 (try sema.typeHasRuntimeBits(dest_info.child.toType()) or dest_info.child.toType().zigTypeTag(mod) == .Fn)) 21283 { 21284 const ptr_int = try block.addUnOp(.int_from_ptr, ptr); 21285 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); 21286 const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: { 21287 const len = try sema.analyzeSliceLen(block, operand_src, ptr); 21288 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 21289 break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero); 21290 } else is_non_zero; 21291 try sema.addSafetyCheck(block, ok, .cast_to_null); 21292 } 21293 21294 if (block.wantSafety() and dest_align > src_align and try sema.typeHasRuntimeBits(dest_info.child.toType())) { 21295 const align_minus_1 = try sema.addConstant( 21296 try mod.intValue(Type.usize, dest_align - 1), 21297 ); 21298 const ptr_int = try block.addUnOp(.int_from_ptr, ptr); 21299 const remainder = try block.addBinOp(.bit_and, ptr_int, align_minus_1); 21300 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); 21301 const ok = if (src_info.flags.size == .Slice and dest_info.flags.size == .Slice) ok: { 21302 const len = try sema.analyzeSliceLen(block, operand_src, ptr); 21303 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 21304 break :ok try block.addBinOp(.bit_or, len_zero, is_aligned); 21305 } else is_aligned; 21306 try sema.addSafetyCheck(block, ok, .incorrect_alignment); 21307 } 21308 21309 // If we're going from an array pointer to a slice, this will only be the pointer part! 21310 const result_ptr = if (flags.addrspace_cast) ptr: { 21311 // We can't change address spaces with a bitcast, so this requires two instructions 21312 var intermediate_info = src_info; 21313 intermediate_info.flags.address_space = dest_info.flags.address_space; 21314 const intermediate_ptr_ty = try mod.ptrType(intermediate_info); 21315 const intermediate_ty = if (dest_ptr_ty.zigTypeTag(mod) == .Optional) blk: { 21316 break :blk try mod.optionalType(intermediate_ptr_ty.toIntern()); 21317 } else intermediate_ptr_ty; 21318 const intermediate = try block.addInst(.{ 21319 .tag = .addrspace_cast, 21320 .data = .{ .ty_op = .{ 21321 .ty = try sema.addType(intermediate_ty), 21322 .operand = ptr, 21323 } }, 21324 }); 21325 if (intermediate_ty.eql(dest_ptr_ty, mod)) { 21326 // We only changed the address space, so no need for a bitcast 21327 break :ptr intermediate; 21328 } 21329 break :ptr try block.addBitCast(dest_ptr_ty, intermediate); 21330 } else ptr: { 21331 break :ptr try block.addBitCast(dest_ptr_ty, ptr); 21332 }; 21333 21334 if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) { 21335 // We have to construct a slice using the operand's child's array length 21336 // Note that we know from the check at the start of the function that operand_ty is slice-like 21337 const arr_len = try sema.addConstant( 21338 try mod.intValue(Type.usize, src_info.child.toType().arrayLen(mod)), 21339 ); 21340 return block.addInst(.{ 21341 .tag = .slice, 21342 .data = .{ .ty_pl = .{ 21343 .ty = try sema.addType(dest_ty), 21344 .payload = try sema.addExtra(Air.Bin{ 21345 .lhs = result_ptr, 21346 .rhs = arr_len, 21347 }), 21348 } }, 21349 }); 21350 } else { 21351 assert(dest_ptr_ty.eql(dest_ty, mod)); 21352 return result_ptr; 21353 } 21354 } 21355 21356 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 21357 const mod = sema.mod; 21358 const flags = @as(Zir.Inst.FullPtrCastFlags, @bitCast(@as(u5, @truncate(extended.small)))); 21359 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 21360 const src = LazySrcLoc.nodeOffset(extra.node); 21361 const operand_src: LazySrcLoc = .{ .node_offset_ptrcast_operand = extra.node }; 21362 const operand = try sema.resolveInst(extra.operand); 21363 const operand_ty = sema.typeOf(operand); 21364 try sema.checkPtrOperand(block, operand_src, operand_ty); 21365 21366 var ptr_info = operand_ty.ptrInfo(mod); 21367 if (flags.const_cast) ptr_info.flags.is_const = false; 21368 if (flags.volatile_cast) ptr_info.flags.is_volatile = false; 21369 const dest_ty = try mod.ptrType(ptr_info); 21370 21371 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { 21372 return sema.addConstant(try mod.getCoerced(operand_val, dest_ty)); 21373 } 21374 21375 try sema.requireRuntimeBlock(block, src, null); 21376 return block.addBitCast(dest_ty, operand); 21377 } 21378 21379 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21380 const mod = sema.mod; 21381 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 21382 const src = inst_data.src(); 21383 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21384 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21385 const dest_ty = try sema.resolveCastDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate"); 21386 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src); 21387 const operand = try sema.resolveInst(extra.rhs); 21388 const operand_ty = sema.typeOf(operand); 21389 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); 21390 21391 const operand_is_vector = operand_ty.zigTypeTag(mod) == .Vector; 21392 const dest_is_vector = dest_ty.zigTypeTag(mod) == .Vector; 21393 if (operand_is_vector != dest_is_vector) { 21394 return sema.fail(block, operand_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), operand_ty.fmt(mod) }); 21395 } 21396 21397 if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) { 21398 return sema.coerce(block, dest_ty, operand, operand_src); 21399 } 21400 21401 const dest_info = dest_scalar_ty.intInfo(mod); 21402 21403 if (try sema.typeHasOnePossibleValue(dest_ty)) |val| { 21404 return sema.addConstant(val); 21405 } 21406 21407 if (operand_scalar_ty.zigTypeTag(mod) != .ComptimeInt) { 21408 const operand_info = operand_ty.intInfo(mod); 21409 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 21410 return sema.addConstant(val); 21411 } 21412 21413 if (operand_info.signedness != dest_info.signedness) { 21414 return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{ 21415 @tagName(dest_info.signedness), operand_ty.fmt(mod), 21416 }); 21417 } 21418 if (operand_info.bits < dest_info.bits) { 21419 const msg = msg: { 21420 const msg = try sema.errMsg( 21421 block, 21422 src, 21423 "destination type '{}' has more bits than source type '{}'", 21424 .{ dest_ty.fmt(mod), operand_ty.fmt(mod) }, 21425 ); 21426 errdefer msg.destroy(sema.gpa); 21427 try sema.errNote(block, src, msg, "destination type has {d} bits", .{ 21428 dest_info.bits, 21429 }); 21430 try sema.errNote(block, operand_src, msg, "operand type has {d} bits", .{ 21431 operand_info.bits, 21432 }); 21433 break :msg msg; 21434 }; 21435 return sema.failWithOwnedErrorMsg(msg); 21436 } 21437 } 21438 21439 if (try sema.resolveMaybeUndefValIntable(operand)) |val| { 21440 if (val.isUndef(mod)) return sema.addConstUndef(dest_ty); 21441 if (!dest_is_vector) { 21442 return sema.addConstant(try mod.getCoerced( 21443 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, mod), 21444 dest_ty, 21445 )); 21446 } 21447 const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(mod)); 21448 for (elems, 0..) |*elem, i| { 21449 const elem_val = try val.elemValue(mod, i); 21450 elem.* = try (try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, mod)).intern(dest_scalar_ty, mod); 21451 } 21452 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 21453 .ty = dest_ty.toIntern(), 21454 .storage = .{ .elems = elems }, 21455 } })).toValue()); 21456 } 21457 21458 try sema.requireRuntimeBlock(block, src, operand_src); 21459 return block.addTyOp(.trunc, dest_ty, operand); 21460 } 21461 21462 fn zirBitCount( 21463 sema: *Sema, 21464 block: *Block, 21465 inst: Zir.Inst.Index, 21466 air_tag: Air.Inst.Tag, 21467 comptime comptimeOp: fn (val: Value, ty: Type, mod: *Module) u64, 21468 ) CompileError!Air.Inst.Ref { 21469 const mod = sema.mod; 21470 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 21471 const src = inst_data.src(); 21472 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21473 const operand = try sema.resolveInst(inst_data.operand); 21474 const operand_ty = sema.typeOf(operand); 21475 _ = try sema.checkIntOrVector(block, operand, operand_src); 21476 const bits = operand_ty.intInfo(mod).bits; 21477 21478 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 21479 return sema.addConstant(val); 21480 } 21481 21482 const result_scalar_ty = try mod.smallestUnsignedInt(bits); 21483 switch (operand_ty.zigTypeTag(mod)) { 21484 .Vector => { 21485 const vec_len = operand_ty.vectorLen(mod); 21486 const result_ty = try mod.vectorType(.{ 21487 .len = vec_len, 21488 .child = result_scalar_ty.toIntern(), 21489 }); 21490 if (try sema.resolveMaybeUndefVal(operand)) |val| { 21491 if (val.isUndef(mod)) return sema.addConstUndef(result_ty); 21492 21493 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 21494 const scalar_ty = operand_ty.scalarType(mod); 21495 for (elems, 0..) |*elem, i| { 21496 const elem_val = try val.elemValue(mod, i); 21497 const count = comptimeOp(elem_val, scalar_ty, mod); 21498 elem.* = (try mod.intValue(result_scalar_ty, count)).toIntern(); 21499 } 21500 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 21501 .ty = result_ty.toIntern(), 21502 .storage = .{ .elems = elems }, 21503 } })).toValue()); 21504 } else { 21505 try sema.requireRuntimeBlock(block, src, operand_src); 21506 return block.addTyOp(air_tag, result_ty, operand); 21507 } 21508 }, 21509 .Int => { 21510 if (try sema.resolveMaybeUndefLazyVal(operand)) |val| { 21511 if (val.isUndef(mod)) return sema.addConstUndef(result_scalar_ty); 21512 return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, mod)); 21513 } else { 21514 try sema.requireRuntimeBlock(block, src, operand_src); 21515 return block.addTyOp(air_tag, result_scalar_ty, operand); 21516 } 21517 }, 21518 else => unreachable, 21519 } 21520 } 21521 21522 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21523 const mod = sema.mod; 21524 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 21525 const src = inst_data.src(); 21526 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21527 const operand = try sema.resolveInst(inst_data.operand); 21528 const operand_ty = sema.typeOf(operand); 21529 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 21530 const bits = scalar_ty.intInfo(mod).bits; 21531 if (bits % 8 != 0) { 21532 return sema.fail( 21533 block, 21534 operand_src, 21535 "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", 21536 .{ scalar_ty.fmt(mod), bits }, 21537 ); 21538 } 21539 21540 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 21541 return sema.addConstant(val); 21542 } 21543 21544 switch (operand_ty.zigTypeTag(mod)) { 21545 .Int => { 21546 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { 21547 if (val.isUndef(mod)) return sema.addConstUndef(operand_ty); 21548 const result_val = try val.byteSwap(operand_ty, mod, sema.arena); 21549 return sema.addConstant(result_val); 21550 } else operand_src; 21551 21552 try sema.requireRuntimeBlock(block, src, runtime_src); 21553 return block.addTyOp(.byte_swap, operand_ty, operand); 21554 }, 21555 .Vector => { 21556 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { 21557 if (val.isUndef(mod)) 21558 return sema.addConstUndef(operand_ty); 21559 21560 const vec_len = operand_ty.vectorLen(mod); 21561 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 21562 for (elems, 0..) |*elem, i| { 21563 const elem_val = try val.elemValue(mod, i); 21564 elem.* = try (try elem_val.byteSwap(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod); 21565 } 21566 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 21567 .ty = operand_ty.toIntern(), 21568 .storage = .{ .elems = elems }, 21569 } })).toValue()); 21570 } else operand_src; 21571 21572 try sema.requireRuntimeBlock(block, src, runtime_src); 21573 return block.addTyOp(.byte_swap, operand_ty, operand); 21574 }, 21575 else => unreachable, 21576 } 21577 } 21578 21579 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21580 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 21581 const src = inst_data.src(); 21582 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 21583 const operand = try sema.resolveInst(inst_data.operand); 21584 const operand_ty = sema.typeOf(operand); 21585 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); 21586 21587 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| { 21588 return sema.addConstant(val); 21589 } 21590 21591 const mod = sema.mod; 21592 switch (operand_ty.zigTypeTag(mod)) { 21593 .Int => { 21594 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { 21595 if (val.isUndef(mod)) return sema.addConstUndef(operand_ty); 21596 const result_val = try val.bitReverse(operand_ty, mod, sema.arena); 21597 return sema.addConstant(result_val); 21598 } else operand_src; 21599 21600 try sema.requireRuntimeBlock(block, src, runtime_src); 21601 return block.addTyOp(.bit_reverse, operand_ty, operand); 21602 }, 21603 .Vector => { 21604 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| { 21605 if (val.isUndef(mod)) 21606 return sema.addConstUndef(operand_ty); 21607 21608 const vec_len = operand_ty.vectorLen(mod); 21609 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 21610 for (elems, 0..) |*elem, i| { 21611 const elem_val = try val.elemValue(mod, i); 21612 elem.* = try (try elem_val.bitReverse(scalar_ty, mod, sema.arena)).intern(scalar_ty, mod); 21613 } 21614 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 21615 .ty = operand_ty.toIntern(), 21616 .storage = .{ .elems = elems }, 21617 } })).toValue()); 21618 } else operand_src; 21619 21620 try sema.requireRuntimeBlock(block, src, runtime_src); 21621 return block.addTyOp(.bit_reverse, operand_ty, operand); 21622 }, 21623 else => unreachable, 21624 } 21625 } 21626 21627 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21628 const offset = try sema.bitOffsetOf(block, inst); 21629 return sema.addIntUnsigned(Type.comptime_int, offset); 21630 } 21631 21632 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 21633 const offset = try sema.bitOffsetOf(block, inst); 21634 // TODO reminder to make this a compile error for packed structs 21635 return sema.addIntUnsigned(Type.comptime_int, offset / 8); 21636 } 21637 21638 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 { 21639 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 21640 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; 21641 sema.src = src; 21642 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 21643 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 21644 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 21645 21646 const ty = try sema.resolveType(block, lhs_src, extra.lhs); 21647 const field_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, "name of field must be comptime-known"); 21648 21649 const mod = sema.mod; 21650 try sema.resolveTypeLayout(ty); 21651 switch (ty.zigTypeTag(mod)) { 21652 .Struct => {}, 21653 else => { 21654 const msg = msg: { 21655 const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(mod)}); 21656 errdefer msg.destroy(sema.gpa); 21657 try sema.addDeclaredHereNote(msg, ty); 21658 break :msg msg; 21659 }; 21660 return sema.failWithOwnedErrorMsg(msg); 21661 }, 21662 } 21663 21664 const field_index = if (ty.isTuple(mod)) blk: { 21665 if (mod.intern_pool.stringEqlSlice(field_name, "len")) { 21666 return sema.fail(block, src, "no offset available for 'len' field of tuple", .{}); 21667 } 21668 break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src); 21669 } else try sema.structFieldIndex(block, ty, field_name, rhs_src); 21670 21671 if (ty.structFieldIsComptime(field_index, mod)) { 21672 return sema.fail(block, src, "no offset available for comptime field", .{}); 21673 } 21674 21675 switch (ty.containerLayout(mod)) { 21676 .Packed => { 21677 var bit_sum: u64 = 0; 21678 const fields = ty.structFields(mod); 21679 for (fields.values(), 0..) |field, i| { 21680 if (i == field_index) { 21681 return bit_sum; 21682 } 21683 bit_sum += field.ty.bitSize(mod); 21684 } else unreachable; 21685 }, 21686 else => return ty.structFieldOffset(field_index, mod) * 8, 21687 } 21688 } 21689 21690 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 21691 const mod = sema.mod; 21692 switch (ty.zigTypeTag(mod)) { 21693 .Struct, .Enum, .Union, .Opaque => return, 21694 else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(mod)}), 21695 } 21696 } 21697 21698 /// Returns `true` if the type was a comptime_int. 21699 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { 21700 const mod = sema.mod; 21701 switch (try ty.zigTypeTagOrPoison(mod)) { 21702 .ComptimeInt => return true, 21703 .Int => return false, 21704 else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(mod)}), 21705 } 21706 } 21707 21708 fn checkInvalidPtrArithmetic( 21709 sema: *Sema, 21710 block: *Block, 21711 src: LazySrcLoc, 21712 ty: Type, 21713 ) CompileError!void { 21714 const mod = sema.mod; 21715 switch (try ty.zigTypeTagOrPoison(mod)) { 21716 .Pointer => switch (ty.ptrSize(mod)) { 21717 .One, .Slice => return, 21718 .Many, .C => return sema.fail( 21719 block, 21720 src, 21721 "invalid pointer arithmetic operator", 21722 .{}, 21723 ), 21724 }, 21725 else => return, 21726 } 21727 } 21728 21729 fn checkArithmeticOp( 21730 sema: *Sema, 21731 block: *Block, 21732 src: LazySrcLoc, 21733 scalar_tag: std.builtin.TypeId, 21734 lhs_zig_ty_tag: std.builtin.TypeId, 21735 rhs_zig_ty_tag: std.builtin.TypeId, 21736 zir_tag: Zir.Inst.Tag, 21737 ) CompileError!void { 21738 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; 21739 const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; 21740 21741 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { 21742 return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ 21743 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), 21744 }); 21745 } 21746 } 21747 21748 fn checkPtrOperand( 21749 sema: *Sema, 21750 block: *Block, 21751 ty_src: LazySrcLoc, 21752 ty: Type, 21753 ) CompileError!void { 21754 const mod = sema.mod; 21755 switch (ty.zigTypeTag(mod)) { 21756 .Pointer => return, 21757 .Fn => { 21758 const msg = msg: { 21759 const msg = try sema.errMsg( 21760 block, 21761 ty_src, 21762 "expected pointer, found '{}'", 21763 .{ty.fmt(mod)}, 21764 ); 21765 errdefer msg.destroy(sema.gpa); 21766 21767 try sema.errNote(block, ty_src, msg, "use '&' to obtain a function pointer", .{}); 21768 21769 break :msg msg; 21770 }; 21771 return sema.failWithOwnedErrorMsg(msg); 21772 }, 21773 .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return, 21774 else => {}, 21775 } 21776 return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)}); 21777 } 21778 21779 fn checkPtrType( 21780 sema: *Sema, 21781 block: *Block, 21782 ty_src: LazySrcLoc, 21783 ty: Type, 21784 ) CompileError!void { 21785 const mod = sema.mod; 21786 switch (ty.zigTypeTag(mod)) { 21787 .Pointer => return, 21788 .Fn => { 21789 const msg = msg: { 21790 const msg = try sema.errMsg( 21791 block, 21792 ty_src, 21793 "expected pointer type, found '{}'", 21794 .{ty.fmt(mod)}, 21795 ); 21796 errdefer msg.destroy(sema.gpa); 21797 21798 try sema.errNote(block, ty_src, msg, "use '*const ' to make a function pointer type", .{}); 21799 21800 break :msg msg; 21801 }; 21802 return sema.failWithOwnedErrorMsg(msg); 21803 }, 21804 .Optional => if (ty.childType(mod).zigTypeTag(mod) == .Pointer) return, 21805 else => {}, 21806 } 21807 return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(mod)}); 21808 } 21809 21810 fn checkVectorElemType( 21811 sema: *Sema, 21812 block: *Block, 21813 ty_src: LazySrcLoc, 21814 ty: Type, 21815 ) CompileError!void { 21816 const mod = sema.mod; 21817 switch (ty.zigTypeTag(mod)) { 21818 .Int, .Float, .Bool => return, 21819 else => if (ty.isPtrAtRuntime(mod)) return, 21820 } 21821 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(mod)}); 21822 } 21823 21824 fn checkFloatType( 21825 sema: *Sema, 21826 block: *Block, 21827 ty_src: LazySrcLoc, 21828 ty: Type, 21829 ) CompileError!void { 21830 const mod = sema.mod; 21831 switch (ty.zigTypeTag(mod)) { 21832 .ComptimeInt, .ComptimeFloat, .Float => {}, 21833 else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(mod)}), 21834 } 21835 } 21836 21837 fn checkNumericType( 21838 sema: *Sema, 21839 block: *Block, 21840 ty_src: LazySrcLoc, 21841 ty: Type, 21842 ) CompileError!void { 21843 const mod = sema.mod; 21844 switch (ty.zigTypeTag(mod)) { 21845 .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, 21846 .Vector => switch (ty.childType(mod).zigTypeTag(mod)) { 21847 .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, 21848 else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}), 21849 }, 21850 else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(mod)}), 21851 } 21852 } 21853 21854 /// Returns the casted pointer. 21855 fn checkAtomicPtrOperand( 21856 sema: *Sema, 21857 block: *Block, 21858 elem_ty: Type, 21859 elem_ty_src: LazySrcLoc, 21860 ptr: Air.Inst.Ref, 21861 ptr_src: LazySrcLoc, 21862 ptr_const: bool, 21863 ) CompileError!Air.Inst.Ref { 21864 const mod = sema.mod; 21865 var diag: Module.AtomicPtrAlignmentDiagnostics = .{}; 21866 const alignment = mod.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) { 21867 error.OutOfMemory => return error.OutOfMemory, 21868 error.FloatTooBig => return sema.fail( 21869 block, 21870 elem_ty_src, 21871 "expected {d}-bit float type or smaller; found {d}-bit float type", 21872 .{ diag.max_bits, diag.bits }, 21873 ), 21874 error.IntTooBig => return sema.fail( 21875 block, 21876 elem_ty_src, 21877 "expected {d}-bit integer type or smaller; found {d}-bit integer type", 21878 .{ diag.max_bits, diag.bits }, 21879 ), 21880 error.BadType => return sema.fail( 21881 block, 21882 elem_ty_src, 21883 "expected bool, integer, float, enum, or pointer type; found '{}'", 21884 .{elem_ty.fmt(mod)}, 21885 ), 21886 }; 21887 21888 var wanted_ptr_data: InternPool.Key.PtrType = .{ 21889 .child = elem_ty.toIntern(), 21890 .flags = .{ 21891 .alignment = alignment, 21892 .is_const = ptr_const, 21893 }, 21894 }; 21895 21896 const ptr_ty = sema.typeOf(ptr); 21897 const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison(mod)) { 21898 .Pointer => ptr_ty.ptrInfo(mod), 21899 else => { 21900 const wanted_ptr_ty = try mod.ptrType(wanted_ptr_data); 21901 _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 21902 unreachable; 21903 }, 21904 }; 21905 21906 wanted_ptr_data.flags.address_space = ptr_data.flags.address_space; 21907 wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero; 21908 wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile; 21909 21910 const wanted_ptr_ty = try mod.ptrType(wanted_ptr_data); 21911 const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); 21912 21913 return casted_ptr; 21914 } 21915 21916 fn checkPtrIsNotComptimeMutable( 21917 sema: *Sema, 21918 block: *Block, 21919 ptr_val: Value, 21920 ptr_src: LazySrcLoc, 21921 operand_src: LazySrcLoc, 21922 ) CompileError!void { 21923 _ = operand_src; 21924 if (ptr_val.isComptimeMutablePtr(sema.mod)) { 21925 return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); 21926 } 21927 } 21928 21929 fn checkComptimeVarStore( 21930 sema: *Sema, 21931 block: *Block, 21932 src: LazySrcLoc, 21933 decl_ref_mut: InternPool.Key.Ptr.Addr.MutDecl, 21934 ) CompileError!void { 21935 if (@intFromEnum(decl_ref_mut.runtime_index) < @intFromEnum(block.runtime_index)) { 21936 if (block.runtime_cond) |cond_src| { 21937 const msg = msg: { 21938 const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{}); 21939 errdefer msg.destroy(sema.gpa); 21940 try sema.errNote(block, cond_src, msg, "runtime condition here", .{}); 21941 break :msg msg; 21942 }; 21943 return sema.failWithOwnedErrorMsg(msg); 21944 } 21945 if (block.runtime_loop) |loop_src| { 21946 const msg = msg: { 21947 const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{}); 21948 errdefer msg.destroy(sema.gpa); 21949 try sema.errNote(block, loop_src, msg, "non-inline loop here", .{}); 21950 break :msg msg; 21951 }; 21952 return sema.failWithOwnedErrorMsg(msg); 21953 } 21954 unreachable; 21955 } 21956 } 21957 21958 fn checkIntOrVector( 21959 sema: *Sema, 21960 block: *Block, 21961 operand: Air.Inst.Ref, 21962 operand_src: LazySrcLoc, 21963 ) CompileError!Type { 21964 const mod = sema.mod; 21965 const operand_ty = sema.typeOf(operand); 21966 switch (try operand_ty.zigTypeTagOrPoison(mod)) { 21967 .Int => return operand_ty, 21968 .Vector => { 21969 const elem_ty = operand_ty.childType(mod); 21970 switch (try elem_ty.zigTypeTagOrPoison(mod)) { 21971 .Int => return elem_ty, 21972 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ 21973 elem_ty.fmt(mod), 21974 }), 21975 } 21976 }, 21977 else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ 21978 operand_ty.fmt(mod), 21979 }), 21980 } 21981 } 21982 21983 fn checkIntOrVectorAllowComptime( 21984 sema: *Sema, 21985 block: *Block, 21986 operand_ty: Type, 21987 operand_src: LazySrcLoc, 21988 ) CompileError!Type { 21989 const mod = sema.mod; 21990 switch (try operand_ty.zigTypeTagOrPoison(mod)) { 21991 .Int, .ComptimeInt => return operand_ty, 21992 .Vector => { 21993 const elem_ty = operand_ty.childType(mod); 21994 switch (try elem_ty.zigTypeTagOrPoison(mod)) { 21995 .Int, .ComptimeInt => return elem_ty, 21996 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ 21997 elem_ty.fmt(mod), 21998 }), 21999 } 22000 }, 22001 else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ 22002 operand_ty.fmt(mod), 22003 }), 22004 } 22005 } 22006 22007 fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { 22008 const mod = sema.mod; 22009 switch (ty.zigTypeTag(mod)) { 22010 .ErrorSet => return, 22011 else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}), 22012 } 22013 } 22014 22015 const SimdBinOp = struct { 22016 len: ?usize, 22017 /// Coerced to `result_ty`. 22018 lhs: Air.Inst.Ref, 22019 /// Coerced to `result_ty`. 22020 rhs: Air.Inst.Ref, 22021 lhs_val: ?Value, 22022 rhs_val: ?Value, 22023 /// Only different than `scalar_ty` when it is a vector operation. 22024 result_ty: Type, 22025 scalar_ty: Type, 22026 }; 22027 22028 fn checkSimdBinOp( 22029 sema: *Sema, 22030 block: *Block, 22031 src: LazySrcLoc, 22032 uncasted_lhs: Air.Inst.Ref, 22033 uncasted_rhs: Air.Inst.Ref, 22034 lhs_src: LazySrcLoc, 22035 rhs_src: LazySrcLoc, 22036 ) CompileError!SimdBinOp { 22037 const mod = sema.mod; 22038 const lhs_ty = sema.typeOf(uncasted_lhs); 22039 const rhs_ty = sema.typeOf(uncasted_rhs); 22040 22041 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 22042 var vec_len: ?usize = if (lhs_ty.zigTypeTag(mod) == .Vector) lhs_ty.vectorLen(mod) else null; 22043 const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ 22044 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, 22045 }); 22046 const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); 22047 const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); 22048 22049 return SimdBinOp{ 22050 .len = vec_len, 22051 .lhs = lhs, 22052 .rhs = rhs, 22053 .lhs_val = try sema.resolveMaybeUndefVal(lhs), 22054 .rhs_val = try sema.resolveMaybeUndefVal(rhs), 22055 .result_ty = result_ty, 22056 .scalar_ty = result_ty.scalarType(mod), 22057 }; 22058 } 22059 22060 fn checkVectorizableBinaryOperands( 22061 sema: *Sema, 22062 block: *Block, 22063 src: LazySrcLoc, 22064 lhs_ty: Type, 22065 rhs_ty: Type, 22066 lhs_src: LazySrcLoc, 22067 rhs_src: LazySrcLoc, 22068 ) CompileError!void { 22069 const mod = sema.mod; 22070 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(mod); 22071 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(mod); 22072 if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return; 22073 22074 const lhs_is_vector = switch (lhs_zig_ty_tag) { 22075 .Vector, .Array => true, 22076 else => false, 22077 }; 22078 const rhs_is_vector = switch (rhs_zig_ty_tag) { 22079 .Vector, .Array => true, 22080 else => false, 22081 }; 22082 22083 if (lhs_is_vector and rhs_is_vector) { 22084 const lhs_len = lhs_ty.arrayLen(mod); 22085 const rhs_len = rhs_ty.arrayLen(mod); 22086 if (lhs_len != rhs_len) { 22087 const msg = msg: { 22088 const msg = try sema.errMsg(block, src, "vector length mismatch", .{}); 22089 errdefer msg.destroy(sema.gpa); 22090 try sema.errNote(block, lhs_src, msg, "length {d} here", .{lhs_len}); 22091 try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len}); 22092 break :msg msg; 22093 }; 22094 return sema.failWithOwnedErrorMsg(msg); 22095 } 22096 } else { 22097 const msg = msg: { 22098 const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{ 22099 lhs_ty.fmt(mod), rhs_ty.fmt(mod), 22100 }); 22101 errdefer msg.destroy(sema.gpa); 22102 if (lhs_is_vector) { 22103 try sema.errNote(block, lhs_src, msg, "vector here", .{}); 22104 try sema.errNote(block, rhs_src, msg, "scalar here", .{}); 22105 } else { 22106 try sema.errNote(block, lhs_src, msg, "scalar here", .{}); 22107 try sema.errNote(block, rhs_src, msg, "vector here", .{}); 22108 } 22109 break :msg msg; 22110 }; 22111 return sema.failWithOwnedErrorMsg(msg); 22112 } 22113 } 22114 22115 fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc { 22116 if (base_src == .unneeded) return .unneeded; 22117 const mod = sema.mod; 22118 return mod.optionsSrc(mod.declPtr(block.src_decl), base_src, wanted); 22119 } 22120 22121 fn resolveExportOptions( 22122 sema: *Sema, 22123 block: *Block, 22124 src: LazySrcLoc, 22125 zir_ref: Zir.Inst.Ref, 22126 ) CompileError!Module.Export.Options { 22127 const mod = sema.mod; 22128 const gpa = sema.gpa; 22129 const ip = &mod.intern_pool; 22130 const export_options_ty = try sema.getBuiltinType("ExportOptions"); 22131 const air_ref = try sema.resolveInst(zir_ref); 22132 const options = try sema.coerce(block, export_options_ty, air_ref, src); 22133 22134 const name_src = sema.maybeOptionsSrc(block, src, "name"); 22135 const linkage_src = sema.maybeOptionsSrc(block, src, "linkage"); 22136 const section_src = sema.maybeOptionsSrc(block, src, "section"); 22137 const visibility_src = sema.maybeOptionsSrc(block, src, "visibility"); 22138 22139 const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); 22140 const name_val = try sema.resolveConstValue(block, name_src, name_operand, "name of exported value must be comptime-known"); 22141 const name_ty = Type.slice_const_u8; 22142 const name = try name_val.toAllocatedBytes(name_ty, sema.arena, mod); 22143 22144 const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); 22145 const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_operand, "linkage of exported value must be comptime-known"); 22146 const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); 22147 22148 const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section"), section_src); 22149 const section_opt_val = try sema.resolveConstValue(block, section_src, section_operand, "linksection of exported value must be comptime-known"); 22150 const section_ty = Type.slice_const_u8; 22151 const section = if (section_opt_val.optionalValue(mod)) |section_val| 22152 try section_val.toAllocatedBytes(section_ty, sema.arena, mod) 22153 else 22154 null; 22155 22156 const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility"), visibility_src); 22157 const visibility_val = try sema.resolveConstValue(block, visibility_src, visibility_operand, "visibility of exported value must be comptime-known"); 22158 const visibility = mod.toEnum(std.builtin.SymbolVisibility, visibility_val); 22159 22160 if (name.len < 1) { 22161 return sema.fail(block, name_src, "exported symbol name cannot be empty", .{}); 22162 } 22163 22164 if (visibility != .default and linkage == .Internal) { 22165 return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{ 22166 name, @tagName(visibility), 22167 }); 22168 } 22169 22170 return .{ 22171 .name = try ip.getOrPutString(gpa, name), 22172 .linkage = linkage, 22173 .section = try ip.getOrPutStringOpt(gpa, section), 22174 .visibility = visibility, 22175 }; 22176 } 22177 22178 fn resolveBuiltinEnum( 22179 sema: *Sema, 22180 block: *Block, 22181 src: LazySrcLoc, 22182 zir_ref: Zir.Inst.Ref, 22183 comptime name: []const u8, 22184 reason: []const u8, 22185 ) CompileError!@field(std.builtin, name) { 22186 const mod = sema.mod; 22187 const ty = try sema.getBuiltinType(name); 22188 const air_ref = try sema.resolveInst(zir_ref); 22189 const coerced = try sema.coerce(block, ty, air_ref, src); 22190 const val = try sema.resolveConstValue(block, src, coerced, reason); 22191 return mod.toEnum(@field(std.builtin, name), val); 22192 } 22193 22194 fn resolveAtomicOrder( 22195 sema: *Sema, 22196 block: *Block, 22197 src: LazySrcLoc, 22198 zir_ref: Zir.Inst.Ref, 22199 reason: []const u8, 22200 ) CompileError!std.builtin.AtomicOrder { 22201 return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicOrder", reason); 22202 } 22203 22204 fn resolveAtomicRmwOp( 22205 sema: *Sema, 22206 block: *Block, 22207 src: LazySrcLoc, 22208 zir_ref: Zir.Inst.Ref, 22209 ) CompileError!std.builtin.AtomicRmwOp { 22210 return sema.resolveBuiltinEnum(block, src, zir_ref, "AtomicRmwOp", "@atomicRmW operation must be comptime-known"); 22211 } 22212 22213 fn zirCmpxchg( 22214 sema: *Sema, 22215 block: *Block, 22216 extended: Zir.Inst.Extended.InstData, 22217 ) CompileError!Air.Inst.Ref { 22218 const mod = sema.mod; 22219 const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data; 22220 const air_tag: Air.Inst.Tag = switch (extended.small) { 22221 0 => .cmpxchg_weak, 22222 1 => .cmpxchg_strong, 22223 else => unreachable, 22224 }; 22225 const src = LazySrcLoc.nodeOffset(extra.node); 22226 // zig fmt: off 22227 const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 22228 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 22229 const expected_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; 22230 const new_value_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node }; 22231 const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = extra.node }; 22232 const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = extra.node }; 22233 // zig fmt: on 22234 const expected_value = try sema.resolveInst(extra.expected_value); 22235 const elem_ty = sema.typeOf(expected_value); 22236 if (elem_ty.zigTypeTag(mod) == .Float) { 22237 return sema.fail( 22238 block, 22239 elem_ty_src, 22240 "expected bool, integer, enum, or pointer type; found '{}'", 22241 .{elem_ty.fmt(mod)}, 22242 ); 22243 } 22244 const uncasted_ptr = try sema.resolveInst(extra.ptr); 22245 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 22246 const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); 22247 const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, "atomic order of cmpxchg success must be comptime-known"); 22248 const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, "atomic order of cmpxchg failure must be comptime-known"); 22249 22250 if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) { 22251 return sema.fail(block, success_order_src, "success atomic ordering must be Monotonic or stricter", .{}); 22252 } 22253 if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.Monotonic)) { 22254 return sema.fail(block, failure_order_src, "failure atomic ordering must be Monotonic or stricter", .{}); 22255 } 22256 if (@intFromEnum(failure_order) > @intFromEnum(success_order)) { 22257 return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{}); 22258 } 22259 if (failure_order == .Release or failure_order == .AcqRel) { 22260 return sema.fail(block, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{}); 22261 } 22262 22263 const result_ty = try mod.optionalType(elem_ty.toIntern()); 22264 22265 // special case zero bit types 22266 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { 22267 return sema.addConstant((try mod.intern(.{ .opt = .{ 22268 .ty = result_ty.toIntern(), 22269 .val = .none, 22270 } })).toValue()); 22271 } 22272 22273 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 22274 if (try sema.resolveMaybeUndefVal(expected_value)) |expected_val| { 22275 if (try sema.resolveMaybeUndefVal(new_value)) |new_val| { 22276 if (expected_val.isUndef(mod) or new_val.isUndef(mod)) { 22277 // TODO: this should probably cause the memory stored at the pointer 22278 // to become undef as well 22279 return sema.addConstUndef(result_ty); 22280 } 22281 const ptr_ty = sema.typeOf(ptr); 22282 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 22283 const result_val = try mod.intern(.{ .opt = .{ 22284 .ty = result_ty.toIntern(), 22285 .val = if (stored_val.eql(expected_val, elem_ty, mod)) blk: { 22286 try sema.storePtr(block, src, ptr, new_value); 22287 break :blk .none; 22288 } else stored_val.toIntern(), 22289 } }); 22290 return sema.addConstant(result_val.toValue()); 22291 } else break :rs new_value_src; 22292 } else break :rs expected_src; 22293 } else ptr_src; 22294 22295 const flags: u32 = @as(u32, @intFromEnum(success_order)) | 22296 (@as(u32, @intFromEnum(failure_order)) << 3); 22297 22298 try sema.requireRuntimeBlock(block, src, runtime_src); 22299 return block.addInst(.{ 22300 .tag = air_tag, 22301 .data = .{ .ty_pl = .{ 22302 .ty = try sema.addType(result_ty), 22303 .payload = try sema.addExtra(Air.Cmpxchg{ 22304 .ptr = ptr, 22305 .expected_value = expected_value, 22306 .new_value = new_value, 22307 .flags = flags, 22308 }), 22309 } }, 22310 }); 22311 } 22312 22313 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22314 const mod = sema.mod; 22315 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22316 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22317 const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; 22318 const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; 22319 const len = @as(u32, @intCast(try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector splat destination length must be comptime-known"))); 22320 const scalar = try sema.resolveInst(extra.rhs); 22321 const scalar_ty = sema.typeOf(scalar); 22322 try sema.checkVectorElemType(block, scalar_src, scalar_ty); 22323 const vector_ty = try mod.vectorType(.{ 22324 .len = len, 22325 .child = scalar_ty.toIntern(), 22326 }); 22327 if (try sema.resolveMaybeUndefVal(scalar)) |scalar_val| { 22328 if (scalar_val.isUndef(mod)) return sema.addConstUndef(vector_ty); 22329 return sema.addConstant(try sema.splat(vector_ty, scalar_val)); 22330 } 22331 22332 try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src); 22333 return block.addTyOp(.splat, vector_ty, scalar); 22334 } 22335 22336 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22337 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22338 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 22339 const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22340 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22341 const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", "@reduce operation must be comptime-known"); 22342 const operand = try sema.resolveInst(extra.rhs); 22343 const operand_ty = sema.typeOf(operand); 22344 const mod = sema.mod; 22345 22346 if (operand_ty.zigTypeTag(mod) != .Vector) { 22347 return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(mod)}); 22348 } 22349 22350 const scalar_ty = operand_ty.childType(mod); 22351 22352 // Type-check depending on operation. 22353 switch (operation) { 22354 .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(mod)) { 22355 .Int, .Bool => {}, 22356 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{ 22357 @tagName(operation), operand_ty.fmt(mod), 22358 }), 22359 }, 22360 .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(mod)) { 22361 .Int, .Float => {}, 22362 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{ 22363 @tagName(operation), operand_ty.fmt(mod), 22364 }), 22365 }, 22366 } 22367 22368 const vec_len = operand_ty.vectorLen(mod); 22369 if (vec_len == 0) { 22370 // TODO re-evaluate if we should introduce a "neutral value" for some operations, 22371 // e.g. zero for add and one for mul. 22372 return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{}); 22373 } 22374 22375 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { 22376 if (operand_val.isUndef(mod)) return sema.addConstUndef(scalar_ty); 22377 22378 var accum: Value = try operand_val.elemValue(mod, 0); 22379 var i: u32 = 1; 22380 while (i < vec_len) : (i += 1) { 22381 const elem_val = try operand_val.elemValue(mod, i); 22382 switch (operation) { 22383 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, mod), 22384 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, mod), 22385 .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, mod), 22386 .Min => accum = accum.numberMin(elem_val, mod), 22387 .Max => accum = accum.numberMax(elem_val, mod), 22388 .Add => accum = try sema.numberAddWrapScalar(accum, elem_val, scalar_ty), 22389 .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, mod), 22390 } 22391 } 22392 return sema.addConstant(accum); 22393 } 22394 22395 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); 22396 return block.addInst(.{ 22397 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, 22398 .data = .{ .reduce = .{ 22399 .operand = operand, 22400 .operation = operation, 22401 } }, 22402 }); 22403 } 22404 22405 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22406 const mod = sema.mod; 22407 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22408 const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; 22409 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22410 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 22411 22412 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 22413 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 22414 var a = try sema.resolveInst(extra.a); 22415 var b = try sema.resolveInst(extra.b); 22416 var mask = try sema.resolveInst(extra.mask); 22417 var mask_ty = sema.typeOf(mask); 22418 22419 const mask_len = switch (sema.typeOf(mask).zigTypeTag(mod)) { 22420 .Array, .Vector => sema.typeOf(mask).arrayLen(mod), 22421 else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}), 22422 }; 22423 mask_ty = try mod.vectorType(.{ 22424 .len = @as(u32, @intCast(mask_len)), 22425 .child = .i32_type, 22426 }); 22427 mask = try sema.coerce(block, mask_ty, mask, mask_src); 22428 const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask, "shuffle mask must be comptime-known"); 22429 return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @as(u32, @intCast(mask_len))); 22430 } 22431 22432 fn analyzeShuffle( 22433 sema: *Sema, 22434 block: *Block, 22435 src_node: i32, 22436 elem_ty: Type, 22437 a_arg: Air.Inst.Ref, 22438 b_arg: Air.Inst.Ref, 22439 mask: Value, 22440 mask_len: u32, 22441 ) CompileError!Air.Inst.Ref { 22442 const mod = sema.mod; 22443 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node }; 22444 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node }; 22445 const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node }; 22446 var a = a_arg; 22447 var b = b_arg; 22448 22449 const res_ty = try mod.vectorType(.{ 22450 .len = mask_len, 22451 .child = elem_ty.toIntern(), 22452 }); 22453 22454 var maybe_a_len = switch (sema.typeOf(a).zigTypeTag(mod)) { 22455 .Array, .Vector => sema.typeOf(a).arrayLen(mod), 22456 .Undefined => null, 22457 else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{ 22458 elem_ty.fmt(sema.mod), 22459 sema.typeOf(a).fmt(sema.mod), 22460 }), 22461 }; 22462 var maybe_b_len = switch (sema.typeOf(b).zigTypeTag(mod)) { 22463 .Array, .Vector => sema.typeOf(b).arrayLen(mod), 22464 .Undefined => null, 22465 else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{ 22466 elem_ty.fmt(sema.mod), 22467 sema.typeOf(b).fmt(sema.mod), 22468 }), 22469 }; 22470 if (maybe_a_len == null and maybe_b_len == null) { 22471 return sema.addConstUndef(res_ty); 22472 } 22473 const a_len = @as(u32, @intCast(maybe_a_len orelse maybe_b_len.?)); 22474 const b_len = @as(u32, @intCast(maybe_b_len orelse a_len)); 22475 22476 const a_ty = try mod.vectorType(.{ 22477 .len = a_len, 22478 .child = elem_ty.toIntern(), 22479 }); 22480 const b_ty = try mod.vectorType(.{ 22481 .len = b_len, 22482 .child = elem_ty.toIntern(), 22483 }); 22484 22485 if (maybe_a_len == null) a = try sema.addConstUndef(a_ty) else a = try sema.coerce(block, a_ty, a, a_src); 22486 if (maybe_b_len == null) b = try sema.addConstUndef(b_ty) else b = try sema.coerce(block, b_ty, b, b_src); 22487 22488 const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){ 22489 .{ a_len, a_src, a_ty }, 22490 .{ b_len, b_src, b_ty }, 22491 }; 22492 22493 for (0..@as(usize, @intCast(mask_len))) |i| { 22494 const elem = try mask.elemValue(sema.mod, i); 22495 if (elem.isUndef(mod)) continue; 22496 const int = elem.toSignedInt(mod); 22497 var unsigned: u32 = undefined; 22498 var chosen: u32 = undefined; 22499 if (int >= 0) { 22500 unsigned = @as(u32, @intCast(int)); 22501 chosen = 0; 22502 } else { 22503 unsigned = @as(u32, @intCast(~int)); 22504 chosen = 1; 22505 } 22506 if (unsigned >= operand_info[chosen][0]) { 22507 const msg = msg: { 22508 const msg = try sema.errMsg(block, mask_src, "mask index '{d}' has out-of-bounds selection", .{i}); 22509 errdefer msg.destroy(sema.gpa); 22510 22511 try sema.errNote(block, operand_info[chosen][1], msg, "selected index '{d}' out of bounds of '{}'", .{ 22512 unsigned, 22513 operand_info[chosen][2].fmt(sema.mod), 22514 }); 22515 22516 if (chosen == 0) { 22517 try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{}); 22518 } 22519 22520 break :msg msg; 22521 }; 22522 return sema.failWithOwnedErrorMsg(msg); 22523 } 22524 } 22525 22526 if (try sema.resolveMaybeUndefVal(a)) |a_val| { 22527 if (try sema.resolveMaybeUndefVal(b)) |b_val| { 22528 const values = try sema.arena.alloc(InternPool.Index, mask_len); 22529 for (values, 0..) |*value, i| { 22530 const mask_elem_val = try mask.elemValue(sema.mod, i); 22531 if (mask_elem_val.isUndef(mod)) { 22532 value.* = try mod.intern(.{ .undef = elem_ty.toIntern() }); 22533 continue; 22534 } 22535 const int = mask_elem_val.toSignedInt(mod); 22536 const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int)); 22537 values[i] = try (try (if (int >= 0) a_val else b_val).elemValue(mod, unsigned)).intern(elem_ty, mod); 22538 } 22539 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 22540 .ty = res_ty.toIntern(), 22541 .storage = .{ .elems = values }, 22542 } })).toValue()); 22543 } 22544 } 22545 22546 // All static analysis passed, and not comptime. 22547 // For runtime codegen, vectors a and b must be the same length. Here we 22548 // recursively @shuffle the smaller vector to append undefined elements 22549 // to it up to the length of the longer vector. This recursion terminates 22550 // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len. 22551 if (a_len != b_len) { 22552 const min_len = @min(a_len, b_len); 22553 const max_src = if (a_len > b_len) a_src else b_src; 22554 const max_len = try sema.usizeCast(block, max_src, @max(a_len, b_len)); 22555 22556 const expand_mask_values = try sema.arena.alloc(InternPool.Index, max_len); 22557 for (@as(usize, @intCast(0))..@as(usize, @intCast(min_len))) |i| { 22558 expand_mask_values[i] = (try mod.intValue(Type.comptime_int, i)).toIntern(); 22559 } 22560 for (@as(usize, @intCast(min_len))..@as(usize, @intCast(max_len))) |i| { 22561 expand_mask_values[i] = (try mod.intValue(Type.comptime_int, -1)).toIntern(); 22562 } 22563 const expand_mask = try mod.intern(.{ .aggregate = .{ 22564 .ty = (try mod.vectorType(.{ .len = @as(u32, @intCast(max_len)), .child = .comptime_int_type })).toIntern(), 22565 .storage = .{ .elems = expand_mask_values }, 22566 } }); 22567 22568 if (a_len < b_len) { 22569 const undef = try sema.addConstUndef(a_ty); 22570 a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask.toValue(), @as(u32, @intCast(max_len))); 22571 } else { 22572 const undef = try sema.addConstUndef(b_ty); 22573 b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask.toValue(), @as(u32, @intCast(max_len))); 22574 } 22575 } 22576 22577 return block.addInst(.{ 22578 .tag = .shuffle, 22579 .data = .{ .ty_pl = .{ 22580 .ty = try sema.addType(res_ty), 22581 .payload = try block.sema.addExtra(Air.Shuffle{ 22582 .a = a, 22583 .b = b, 22584 .mask = mask.toIntern(), 22585 .mask_len = mask_len, 22586 }), 22587 } }, 22588 }); 22589 } 22590 22591 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 22592 const mod = sema.mod; 22593 const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; 22594 22595 const src = LazySrcLoc.nodeOffset(extra.node); 22596 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 22597 const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 22598 const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; 22599 const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = extra.node }; 22600 22601 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 22602 try sema.checkVectorElemType(block, elem_ty_src, elem_ty); 22603 const pred_uncoerced = try sema.resolveInst(extra.pred); 22604 const pred_ty = sema.typeOf(pred_uncoerced); 22605 22606 const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison(mod)) { 22607 .Vector, .Array => pred_ty.arrayLen(mod), 22608 else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(mod)}), 22609 }; 22610 const vec_len = @as(u32, @intCast(try sema.usizeCast(block, pred_src, vec_len_u64))); 22611 22612 const bool_vec_ty = try mod.vectorType(.{ 22613 .len = vec_len, 22614 .child = .bool_type, 22615 }); 22616 const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); 22617 22618 const vec_ty = try mod.vectorType(.{ 22619 .len = vec_len, 22620 .child = elem_ty.toIntern(), 22621 }); 22622 const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src); 22623 const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src); 22624 22625 const maybe_pred = try sema.resolveMaybeUndefVal(pred); 22626 const maybe_a = try sema.resolveMaybeUndefVal(a); 22627 const maybe_b = try sema.resolveMaybeUndefVal(b); 22628 22629 const runtime_src = if (maybe_pred) |pred_val| rs: { 22630 if (pred_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22631 22632 if (maybe_a) |a_val| { 22633 if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22634 22635 if (maybe_b) |b_val| { 22636 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22637 22638 const elems = try sema.gpa.alloc(InternPool.Index, vec_len); 22639 for (elems, 0..) |*elem, i| { 22640 const pred_elem_val = try pred_val.elemValue(mod, i); 22641 const should_choose_a = pred_elem_val.toBool(); 22642 elem.* = try (try (if (should_choose_a) a_val else b_val).elemValue(mod, i)).intern(elem_ty, mod); 22643 } 22644 22645 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 22646 .ty = vec_ty.toIntern(), 22647 .storage = .{ .elems = elems }, 22648 } })).toValue()); 22649 } else { 22650 break :rs b_src; 22651 } 22652 } else { 22653 if (maybe_b) |b_val| { 22654 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22655 } 22656 break :rs a_src; 22657 } 22658 } else rs: { 22659 if (maybe_a) |a_val| { 22660 if (a_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22661 } 22662 if (maybe_b) |b_val| { 22663 if (b_val.isUndef(mod)) return sema.addConstUndef(vec_ty); 22664 } 22665 break :rs pred_src; 22666 }; 22667 22668 try sema.requireRuntimeBlock(block, src, runtime_src); 22669 return block.addInst(.{ 22670 .tag = .select, 22671 .data = .{ .pl_op = .{ 22672 .operand = pred, 22673 .payload = try block.sema.addExtra(Air.Bin{ 22674 .lhs = a, 22675 .rhs = b, 22676 }), 22677 } }, 22678 }); 22679 } 22680 22681 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22682 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22683 const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; 22684 // zig fmt: off 22685 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22686 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22687 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22688 // zig fmt: on 22689 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); 22690 const uncasted_ptr = try sema.resolveInst(extra.ptr); 22691 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); 22692 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicLoad must be comptime-known"); 22693 22694 switch (order) { 22695 .Release, .AcqRel => { 22696 return sema.fail( 22697 block, 22698 order_src, 22699 "@atomicLoad atomic ordering must not be Release or AcqRel", 22700 .{}, 22701 ); 22702 }, 22703 else => {}, 22704 } 22705 22706 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 22707 return sema.addConstant(val); 22708 } 22709 22710 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 22711 if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { 22712 return sema.addConstant(elem_val); 22713 } 22714 } 22715 22716 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); 22717 return block.addInst(.{ 22718 .tag = .atomic_load, 22719 .data = .{ .atomic_load = .{ 22720 .ptr = ptr, 22721 .order = order, 22722 } }, 22723 }); 22724 } 22725 22726 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22727 const mod = sema.mod; 22728 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22729 const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; 22730 const src = inst_data.src(); 22731 // zig fmt: off 22732 const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22733 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22734 const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22735 const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 22736 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; 22737 // zig fmt: on 22738 const operand = try sema.resolveInst(extra.operand); 22739 const elem_ty = sema.typeOf(operand); 22740 const uncasted_ptr = try sema.resolveInst(extra.ptr); 22741 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 22742 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); 22743 22744 switch (elem_ty.zigTypeTag(mod)) { 22745 .Enum => if (op != .Xchg) { 22746 return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); 22747 }, 22748 .Bool => if (op != .Xchg) { 22749 return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{}); 22750 }, 22751 .Float => switch (op) { 22752 .Xchg, .Add, .Sub, .Max, .Min => {}, 22753 else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}), 22754 }, 22755 else => {}, 22756 } 22757 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicRmW must be comptime-known"); 22758 22759 if (order == .Unordered) { 22760 return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be Unordered", .{}); 22761 } 22762 22763 // special case zero bit types 22764 if (try sema.typeHasOnePossibleValue(elem_ty)) |val| { 22765 return sema.addConstant(val); 22766 } 22767 22768 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 22769 const maybe_operand_val = try sema.resolveMaybeUndefVal(operand); 22770 const operand_val = maybe_operand_val orelse { 22771 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); 22772 break :rs operand_src; 22773 }; 22774 if (ptr_val.isComptimeMutablePtr(mod)) { 22775 const ptr_ty = sema.typeOf(ptr); 22776 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; 22777 const new_val = switch (op) { 22778 // zig fmt: off 22779 .Xchg => operand_val, 22780 .Add => try sema.numberAddWrapScalar(stored_val, operand_val, elem_ty), 22781 .Sub => try sema.numberSubWrapScalar(stored_val, operand_val, elem_ty), 22782 .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, mod), 22783 .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, mod), 22784 .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, mod), 22785 .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, mod), 22786 .Max => stored_val.numberMax (operand_val, mod), 22787 .Min => stored_val.numberMin (operand_val, mod), 22788 // zig fmt: on 22789 }; 22790 try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); 22791 return sema.addConstant(stored_val); 22792 } else break :rs ptr_src; 22793 } else ptr_src; 22794 22795 const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3); 22796 22797 try sema.requireRuntimeBlock(block, src, runtime_src); 22798 return block.addInst(.{ 22799 .tag = .atomic_rmw, 22800 .data = .{ .pl_op = .{ 22801 .operand = ptr, 22802 .payload = try sema.addExtra(Air.AtomicRmw{ 22803 .operand = operand, 22804 .flags = flags, 22805 }), 22806 } }, 22807 }); 22808 } 22809 22810 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 22811 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22812 const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; 22813 const src = inst_data.src(); 22814 // zig fmt: off 22815 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22816 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22817 const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22818 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 22819 // zig fmt: on 22820 const operand = try sema.resolveInst(extra.operand); 22821 const elem_ty = sema.typeOf(operand); 22822 const uncasted_ptr = try sema.resolveInst(extra.ptr); 22823 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); 22824 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicStore must be comptime-known"); 22825 22826 const air_tag: Air.Inst.Tag = switch (order) { 22827 .Acquire, .AcqRel => { 22828 return sema.fail( 22829 block, 22830 order_src, 22831 "@atomicStore atomic ordering must not be Acquire or AcqRel", 22832 .{}, 22833 ); 22834 }, 22835 .Unordered => .atomic_store_unordered, 22836 .Monotonic => .atomic_store_monotonic, 22837 .Release => .atomic_store_release, 22838 .SeqCst => .atomic_store_seq_cst, 22839 }; 22840 22841 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); 22842 } 22843 22844 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22845 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22846 const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data; 22847 const src = inst_data.src(); 22848 22849 const mulend1_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22850 const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22851 const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; 22852 22853 const addend = try sema.resolveInst(extra.addend); 22854 const ty = sema.typeOf(addend); 22855 const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src); 22856 const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src); 22857 22858 const maybe_mulend1 = try sema.resolveMaybeUndefVal(mulend1); 22859 const maybe_mulend2 = try sema.resolveMaybeUndefVal(mulend2); 22860 const maybe_addend = try sema.resolveMaybeUndefVal(addend); 22861 const mod = sema.mod; 22862 22863 switch (ty.scalarType(mod).zigTypeTag(mod)) { 22864 .ComptimeFloat, .Float => {}, 22865 else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}), 22866 } 22867 22868 const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { 22869 if (maybe_mulend2) |mulend2_val| { 22870 if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty); 22871 22872 if (maybe_addend) |addend_val| { 22873 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); 22874 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, sema.mod); 22875 return sema.addConstant(result_val); 22876 } else { 22877 break :rs addend_src; 22878 } 22879 } else { 22880 if (maybe_addend) |addend_val| { 22881 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); 22882 } 22883 break :rs mulend2_src; 22884 } 22885 } else rs: { 22886 if (maybe_mulend2) |mulend2_val| { 22887 if (mulend2_val.isUndef(mod)) return sema.addConstUndef(ty); 22888 } 22889 if (maybe_addend) |addend_val| { 22890 if (addend_val.isUndef(mod)) return sema.addConstUndef(ty); 22891 } 22892 break :rs mulend1_src; 22893 }; 22894 22895 try sema.requireRuntimeBlock(block, src, runtime_src); 22896 return block.addInst(.{ 22897 .tag = .mul_add, 22898 .data = .{ .pl_op = .{ 22899 .operand = addend, 22900 .payload = try sema.addExtra(Air.Bin{ 22901 .lhs = mulend1, 22902 .rhs = mulend2, 22903 }), 22904 } }, 22905 }); 22906 } 22907 22908 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22909 const tracy = trace(@src()); 22910 defer tracy.end(); 22911 22912 const mod = sema.mod; 22913 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22914 const modifier_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22915 const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22916 const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22917 const call_src = inst_data.src(); 22918 22919 const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; 22920 var func = try sema.resolveInst(extra.callee); 22921 22922 const modifier_ty = try sema.getBuiltinType("CallModifier"); 22923 const air_ref = try sema.resolveInst(extra.modifier); 22924 const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); 22925 const modifier_val = try sema.resolveConstValue(block, modifier_src, modifier_ref, "call modifier must be comptime-known"); 22926 var modifier = mod.toEnum(std.builtin.CallModifier, modifier_val); 22927 switch (modifier) { 22928 // These can be upgraded to comptime or nosuspend calls. 22929 .auto, .never_tail, .no_async => { 22930 if (block.is_comptime) { 22931 if (modifier == .never_tail) { 22932 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{}); 22933 } 22934 modifier = .compile_time; 22935 } else if (extra.flags.is_nosuspend) { 22936 modifier = .no_async; 22937 } 22938 }, 22939 // These can be upgraded to comptime. nosuspend bit can be safely ignored. 22940 .always_inline, .compile_time => { 22941 _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { 22942 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)}); 22943 }; 22944 22945 if (block.is_comptime) { 22946 modifier = .compile_time; 22947 } 22948 }, 22949 .always_tail => { 22950 if (block.is_comptime) { 22951 modifier = .compile_time; 22952 } 22953 }, 22954 .async_kw => { 22955 if (extra.flags.is_nosuspend) { 22956 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{}); 22957 } 22958 if (block.is_comptime) { 22959 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{}); 22960 } 22961 }, 22962 .never_inline => { 22963 if (block.is_comptime) { 22964 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{}); 22965 } 22966 }, 22967 } 22968 22969 const args = try sema.resolveInst(extra.args); 22970 22971 const args_ty = sema.typeOf(args); 22972 if (!args_ty.isTuple(mod) and args_ty.toIntern() != .empty_struct_type) { 22973 return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)}); 22974 } 22975 22976 var resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(mod)); 22977 for (resolved_args, 0..) |*resolved, i| { 22978 resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @as(u32, @intCast(i)), args_ty); 22979 } 22980 22981 const callee_ty = sema.typeOf(func); 22982 const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false); 22983 const ensure_result_used = extra.flags.ensure_result_used; 22984 return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null); 22985 } 22986 22987 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 22988 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 22989 const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data; 22990 const src = inst_data.src(); 22991 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 22992 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 22993 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; 22994 22995 const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type); 22996 const field_name = try sema.resolveConstStringIntern(block, name_src, extra.field_name, "field name must be comptime-known"); 22997 const field_ptr = try sema.resolveInst(extra.field_ptr); 22998 const field_ptr_ty = sema.typeOf(field_ptr); 22999 const mod = sema.mod; 23000 const ip = &mod.intern_pool; 23001 23002 if (parent_ty.zigTypeTag(mod) != .Struct and parent_ty.zigTypeTag(mod) != .Union) { 23003 return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)}); 23004 } 23005 try sema.resolveTypeLayout(parent_ty); 23006 23007 const field_index = switch (parent_ty.zigTypeTag(mod)) { 23008 .Struct => blk: { 23009 if (parent_ty.isTuple(mod)) { 23010 if (ip.stringEqlSlice(field_name, "len")) { 23011 return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); 23012 } 23013 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src); 23014 } else { 23015 break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src); 23016 } 23017 }, 23018 .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src), 23019 else => unreachable, 23020 }; 23021 23022 if (parent_ty.zigTypeTag(mod) == .Struct and parent_ty.structFieldIsComptime(field_index, mod)) { 23023 return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{}); 23024 } 23025 23026 try sema.checkPtrOperand(block, ptr_src, field_ptr_ty); 23027 const field_ptr_ty_info = field_ptr_ty.ptrInfo(mod); 23028 23029 var ptr_ty_data: InternPool.Key.PtrType = .{ 23030 .child = parent_ty.structFieldType(field_index, mod).toIntern(), 23031 .flags = .{ 23032 .address_space = field_ptr_ty_info.flags.address_space, 23033 .is_const = field_ptr_ty_info.flags.is_const, 23034 }, 23035 }; 23036 23037 if (parent_ty.containerLayout(mod) == .Packed) { 23038 return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{}); 23039 } else { 23040 ptr_ty_data.flags.alignment = blk: { 23041 if (mod.typeToStruct(parent_ty)) |struct_obj| { 23042 break :blk struct_obj.fields.values()[field_index].abi_align; 23043 } else if (mod.typeToUnion(parent_ty)) |union_obj| { 23044 break :blk union_obj.fields.values()[field_index].abi_align; 23045 } else { 23046 break :blk .none; 23047 } 23048 }; 23049 } 23050 23051 const actual_field_ptr_ty = try mod.ptrType(ptr_ty_data); 23052 const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src); 23053 23054 ptr_ty_data.child = parent_ty.toIntern(); 23055 const result_ptr = try mod.ptrType(ptr_ty_data); 23056 23057 if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { 23058 const field = switch (ip.indexToKey(field_ptr_val.toIntern())) { 23059 .ptr => |ptr| switch (ptr.addr) { 23060 .field => |field| field, 23061 else => null, 23062 }, 23063 else => null, 23064 } orelse return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{}); 23065 23066 if (field.index != field_index) { 23067 const msg = msg: { 23068 const msg = try sema.errMsg( 23069 block, 23070 src, 23071 "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'", 23072 .{ 23073 field_name.fmt(ip), 23074 field_index, 23075 field.index, 23076 parent_ty.fmt(sema.mod), 23077 }, 23078 ); 23079 errdefer msg.destroy(sema.gpa); 23080 try sema.addDeclaredHereNote(msg, parent_ty); 23081 break :msg msg; 23082 }; 23083 return sema.failWithOwnedErrorMsg(msg); 23084 } 23085 return sema.addConstant(field.base.toValue()); 23086 } 23087 23088 try sema.requireRuntimeBlock(block, src, ptr_src); 23089 try sema.queueFullTypeResolution(result_ptr); 23090 return block.addInst(.{ 23091 .tag = .field_parent_ptr, 23092 .data = .{ .ty_pl = .{ 23093 .ty = try sema.addType(result_ptr), 23094 .payload = try block.sema.addExtra(Air.FieldParentPtr{ 23095 .field_ptr = casted_field_ptr, 23096 .field_index = @as(u32, @intCast(field_index)), 23097 }), 23098 } }, 23099 }); 23100 } 23101 23102 fn zirMinMax( 23103 sema: *Sema, 23104 block: *Block, 23105 inst: Zir.Inst.Index, 23106 comptime air_tag: Air.Inst.Tag, 23107 ) CompileError!Air.Inst.Ref { 23108 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 23109 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23110 const src = inst_data.src(); 23111 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23112 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23113 const lhs = try sema.resolveInst(extra.lhs); 23114 const rhs = try sema.resolveInst(extra.rhs); 23115 try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs)); 23116 try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs)); 23117 return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src }); 23118 } 23119 23120 fn zirMinMaxMulti( 23121 sema: *Sema, 23122 block: *Block, 23123 extended: Zir.Inst.Extended.InstData, 23124 comptime air_tag: Air.Inst.Tag, 23125 ) CompileError!Air.Inst.Ref { 23126 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); 23127 const src_node = extra.data.src_node; 23128 const src = LazySrcLoc.nodeOffset(src_node); 23129 const operands = sema.code.refSlice(extra.end, extended.small); 23130 23131 const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); 23132 const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len); 23133 23134 for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| { 23135 op_src.* = switch (i) { 23136 0 => .{ .node_offset_builtin_call_arg0 = src_node }, 23137 1 => .{ .node_offset_builtin_call_arg1 = src_node }, 23138 2 => .{ .node_offset_builtin_call_arg2 = src_node }, 23139 3 => .{ .node_offset_builtin_call_arg3 = src_node }, 23140 4 => .{ .node_offset_builtin_call_arg4 = src_node }, 23141 5 => .{ .node_offset_builtin_call_arg5 = src_node }, 23142 else => src, // TODO: better source location 23143 }; 23144 air_ref.* = try sema.resolveInst(zir_ref); 23145 try sema.checkNumericType(block, op_src.*, sema.typeOf(air_ref.*)); 23146 } 23147 23148 return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs); 23149 } 23150 23151 fn analyzeMinMax( 23152 sema: *Sema, 23153 block: *Block, 23154 src: LazySrcLoc, 23155 comptime air_tag: Air.Inst.Tag, 23156 operands: []const Air.Inst.Ref, 23157 operand_srcs: []const LazySrcLoc, 23158 ) CompileError!Air.Inst.Ref { 23159 assert(operands.len == operand_srcs.len); 23160 assert(operands.len > 0); 23161 const mod = sema.mod; 23162 23163 if (operands.len == 1) return operands[0]; 23164 23165 const opFunc = switch (air_tag) { 23166 .min => Value.numberMin, 23167 .max => Value.numberMax, 23168 else => @compileError("unreachable"), 23169 }; 23170 23171 // The set of runtime-known operands. Set up in the loop below. 23172 var runtime_known = try std.DynamicBitSet.initFull(sema.arena, operands.len); 23173 // The current minmax value - initially this will always be comptime-known, then we'll add 23174 // runtime values into the mix later. 23175 var cur_minmax: ?Air.Inst.Ref = null; 23176 var cur_minmax_src: LazySrcLoc = undefined; // defined if cur_minmax not null 23177 // The current known scalar bounds of the value. 23178 var bounds_status: enum { 23179 unknown, // We've only seen undef comptime_ints so far, so do not know the bounds. 23180 defined, // We've seen only integers, so the bounds are defined. 23181 non_integral, // There are floats in the mix, so the bounds aren't defined. 23182 } = .unknown; 23183 var cur_min_scalar: Value = undefined; 23184 var cur_max_scalar: Value = undefined; 23185 23186 // First, find all comptime-known arguments, and get their min/max 23187 23188 for (operands, operand_srcs, 0..) |operand, operand_src, operand_idx| { 23189 // Resolve the value now to avoid redundant calls to `checkSimdBinOp` - we'll have to call 23190 // it in the runtime path anyway since the result type may have been refined 23191 const unresolved_uncoerced_val = try sema.resolveMaybeUndefVal(operand) orelse continue; 23192 const uncoerced_val = try sema.resolveLazyValue(unresolved_uncoerced_val); 23193 23194 runtime_known.unset(operand_idx); 23195 23196 switch (bounds_status) { 23197 .unknown, .defined => refine_bounds: { 23198 const ty = sema.typeOf(operand); 23199 if (!ty.scalarType(mod).isInt(mod) and !ty.scalarType(mod).eql(Type.comptime_int, mod)) { 23200 bounds_status = .non_integral; 23201 break :refine_bounds; 23202 } 23203 const scalar_bounds: ?[2]Value = bounds: { 23204 if (!ty.isVector(mod)) break :bounds try uncoerced_val.intValueBounds(mod); 23205 var cur_bounds: [2]Value = try Value.intValueBounds(try uncoerced_val.elemValue(mod, 0), mod) orelse break :bounds null; 23206 const len = try sema.usizeCast(block, src, ty.vectorLen(mod)); 23207 for (1..len) |i| { 23208 const elem = try uncoerced_val.elemValue(mod, i); 23209 const elem_bounds = try elem.intValueBounds(mod) orelse break :bounds null; 23210 cur_bounds = .{ 23211 Value.numberMin(elem_bounds[0], cur_bounds[0], mod), 23212 Value.numberMax(elem_bounds[1], cur_bounds[1], mod), 23213 }; 23214 } 23215 break :bounds cur_bounds; 23216 }; 23217 if (scalar_bounds) |bounds| { 23218 if (bounds_status == .unknown) { 23219 cur_min_scalar = bounds[0]; 23220 cur_max_scalar = bounds[1]; 23221 bounds_status = .defined; 23222 } else { 23223 cur_min_scalar = opFunc(cur_min_scalar, bounds[0], mod); 23224 cur_max_scalar = opFunc(cur_max_scalar, bounds[1], mod); 23225 } 23226 } 23227 }, 23228 .non_integral => {}, 23229 } 23230 23231 const cur = cur_minmax orelse { 23232 cur_minmax = operand; 23233 cur_minmax_src = operand_src; 23234 continue; 23235 }; 23236 23237 const simd_op = try sema.checkSimdBinOp(block, src, cur, operand, cur_minmax_src, operand_src); 23238 const cur_val = try sema.resolveLazyValue(simd_op.lhs_val.?); // cur_minmax is comptime-known 23239 const operand_val = try sema.resolveLazyValue(simd_op.rhs_val.?); // we checked the operand was resolvable above 23240 23241 const vec_len = simd_op.len orelse { 23242 const result_val = opFunc(cur_val, operand_val, mod); 23243 cur_minmax = try sema.addConstant(result_val); 23244 continue; 23245 }; 23246 const elems = try sema.arena.alloc(InternPool.Index, vec_len); 23247 for (elems, 0..) |*elem, i| { 23248 const lhs_elem_val = try cur_val.elemValue(mod, i); 23249 const rhs_elem_val = try operand_val.elemValue(mod, i); 23250 const uncoerced_elem = opFunc(lhs_elem_val, rhs_elem_val, mod); 23251 elem.* = (try mod.getCoerced(uncoerced_elem, simd_op.scalar_ty)).toIntern(); 23252 } 23253 cur_minmax = try sema.addConstant((try mod.intern(.{ .aggregate = .{ 23254 .ty = simd_op.result_ty.toIntern(), 23255 .storage = .{ .elems = elems }, 23256 } })).toValue()); 23257 } 23258 23259 const opt_runtime_idx = runtime_known.findFirstSet(); 23260 23261 if (cur_minmax) |ct_minmax_ref| refine: { 23262 // Refine the comptime-known result type based on the bounds. This isn't strictly necessary 23263 // in the runtime case, since we'll refine the type again later, but keeping things as small 23264 // as possible will allow us to emit more optimal AIR (if all the runtime operands have 23265 // smaller types than the non-refined comptime type). 23266 23267 const val = (try sema.resolveMaybeUndefVal(ct_minmax_ref)).?; 23268 const orig_ty = sema.typeOf(ct_minmax_ref); 23269 23270 if (opt_runtime_idx == null and orig_ty.scalarType(mod).eql(Type.comptime_int, mod)) { 23271 // If all arguments were `comptime_int`, and there are no runtime args, we'll preserve that type 23272 break :refine; 23273 } 23274 23275 // We can't refine float types 23276 if (orig_ty.scalarType(mod).isAnyFloat()) break :refine; 23277 23278 assert(bounds_status == .defined); // there was a non-comptime-int integral comptime-known arg 23279 23280 const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar); 23281 const refined_ty = if (orig_ty.isVector(mod)) try mod.vectorType(.{ 23282 .len = orig_ty.vectorLen(mod), 23283 .child = refined_scalar_ty.toIntern(), 23284 }) else refined_scalar_ty; 23285 23286 // Apply the refined type to the current value 23287 if (std.debug.runtime_safety) { 23288 assert(try sema.intFitsInType(val, refined_ty, null)); 23289 } 23290 cur_minmax = try sema.coerceInMemory(val, refined_ty); 23291 } 23292 23293 const runtime_idx = opt_runtime_idx orelse return cur_minmax.?; 23294 const runtime_src = operand_srcs[runtime_idx]; 23295 try sema.requireRuntimeBlock(block, src, runtime_src); 23296 23297 // Now, iterate over runtime operands, emitting a min/max instruction for each. We'll refine the 23298 // type again at the end, based on the comptime-known bound. 23299 23300 // If the comptime-known part is undef we can avoid emitting actual instructions later 23301 const known_undef = if (cur_minmax) |operand| blk: { 23302 const val = (try sema.resolveMaybeUndefVal(operand)).?; 23303 break :blk val.isUndef(mod); 23304 } else false; 23305 23306 if (cur_minmax == null) { 23307 // No comptime operands - use the first operand as the starting value 23308 assert(bounds_status == .unknown); 23309 assert(runtime_idx == 0); 23310 cur_minmax = operands[0]; 23311 cur_minmax_src = runtime_src; 23312 runtime_known.unset(0); // don't look at this operand in the loop below 23313 const scalar_ty = sema.typeOf(cur_minmax.?).scalarType(mod); 23314 if (scalar_ty.isInt(mod)) { 23315 cur_min_scalar = try scalar_ty.minInt(mod, scalar_ty); 23316 cur_max_scalar = try scalar_ty.maxInt(mod, scalar_ty); 23317 bounds_status = .defined; 23318 } else { 23319 bounds_status = .non_integral; 23320 } 23321 } 23322 23323 var it = runtime_known.iterator(.{}); 23324 while (it.next()) |idx| { 23325 const lhs = cur_minmax.?; 23326 const lhs_src = cur_minmax_src; 23327 const rhs = operands[idx]; 23328 const rhs_src = operand_srcs[idx]; 23329 const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src); 23330 if (known_undef) { 23331 cur_minmax = try sema.addConstUndef(simd_op.result_ty); 23332 } else { 23333 cur_minmax = try block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); 23334 } 23335 // Compute the bounds of this type 23336 switch (bounds_status) { 23337 .unknown, .defined => refine_bounds: { 23338 const scalar_ty = sema.typeOf(rhs).scalarType(mod); 23339 if (scalar_ty.isAnyFloat()) { 23340 bounds_status = .non_integral; 23341 break :refine_bounds; 23342 } 23343 const scalar_min = try scalar_ty.minInt(mod, scalar_ty); 23344 const scalar_max = try scalar_ty.maxInt(mod, scalar_ty); 23345 if (bounds_status == .unknown) { 23346 cur_min_scalar = scalar_min; 23347 cur_max_scalar = scalar_max; 23348 bounds_status = .defined; 23349 } else { 23350 cur_min_scalar = opFunc(cur_min_scalar, scalar_min, mod); 23351 cur_max_scalar = opFunc(cur_max_scalar, scalar_max, mod); 23352 } 23353 }, 23354 .non_integral => {}, 23355 } 23356 } 23357 23358 // Finally, refine the type based on the known bounds. 23359 const unrefined_ty = sema.typeOf(cur_minmax.?); 23360 if (unrefined_ty.scalarType(mod).isAnyFloat()) { 23361 // We can't refine floats, so we're done. 23362 return cur_minmax.?; 23363 } 23364 assert(bounds_status == .defined); // there were integral runtime operands 23365 const refined_scalar_ty = try mod.intFittingRange(cur_min_scalar, cur_max_scalar); 23366 const refined_ty = if (unrefined_ty.isVector(mod)) try mod.vectorType(.{ 23367 .len = unrefined_ty.vectorLen(mod), 23368 .child = refined_scalar_ty.toIntern(), 23369 }) else refined_scalar_ty; 23370 23371 if (!refined_ty.eql(unrefined_ty, mod)) { 23372 // We've reduced the type - cast the result down 23373 return block.addTyOp(.intcast, refined_ty, cur_minmax.?); 23374 } 23375 23376 return cur_minmax.?; 23377 } 23378 23379 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { 23380 const mod = sema.mod; 23381 const info = sema.typeOf(ptr).ptrInfo(mod); 23382 if (info.flags.size == .One) { 23383 // Already an array pointer. 23384 return ptr; 23385 } 23386 const new_ty = try mod.ptrType(.{ 23387 .child = (try mod.arrayType(.{ 23388 .len = len, 23389 .sentinel = info.sentinel, 23390 .child = info.child, 23391 })).toIntern(), 23392 .flags = .{ 23393 .alignment = info.flags.alignment, 23394 .is_const = info.flags.is_const, 23395 .is_volatile = info.flags.is_volatile, 23396 .is_allowzero = info.flags.is_allowzero, 23397 .address_space = info.flags.address_space, 23398 }, 23399 }); 23400 if (info.flags.size == .Slice) { 23401 return block.addTyOp(.slice_ptr, new_ty, ptr); 23402 } 23403 return block.addBitCast(new_ty, ptr); 23404 } 23405 23406 fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 23407 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 23408 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23409 const src = inst_data.src(); 23410 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23411 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23412 const dest_ptr = try sema.resolveInst(extra.lhs); 23413 const src_ptr = try sema.resolveInst(extra.rhs); 23414 const dest_ty = sema.typeOf(dest_ptr); 23415 const src_ty = sema.typeOf(src_ptr); 23416 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); 23417 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); 23418 const target = sema.mod.getTarget(); 23419 const mod = sema.mod; 23420 23421 if (dest_ty.isConstPtr(mod)) { 23422 return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{}); 23423 } 23424 23425 if (dest_len == .none and src_len == .none) { 23426 const msg = msg: { 23427 const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{}); 23428 errdefer msg.destroy(sema.gpa); 23429 try sema.errNote(block, dest_src, msg, "destination type '{}' provides no length", .{ 23430 dest_ty.fmt(sema.mod), 23431 }); 23432 try sema.errNote(block, src_src, msg, "source type '{}' provides no length", .{ 23433 src_ty.fmt(sema.mod), 23434 }); 23435 break :msg msg; 23436 }; 23437 return sema.failWithOwnedErrorMsg(msg); 23438 } 23439 23440 var len_val: ?Value = null; 23441 23442 if (dest_len != .none and src_len != .none) check: { 23443 // If we can check at compile-time, no need for runtime safety. 23444 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 23445 len_val = dest_len_val; 23446 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 23447 if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { 23448 const msg = msg: { 23449 const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{}); 23450 errdefer msg.destroy(sema.gpa); 23451 try sema.errNote(block, dest_src, msg, "length {} here", .{ 23452 dest_len_val.fmtValue(Type.usize, sema.mod), 23453 }); 23454 try sema.errNote(block, src_src, msg, "length {} here", .{ 23455 src_len_val.fmtValue(Type.usize, sema.mod), 23456 }); 23457 break :msg msg; 23458 }; 23459 return sema.failWithOwnedErrorMsg(msg); 23460 } 23461 break :check; 23462 } 23463 } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 23464 len_val = src_len_val; 23465 } 23466 23467 if (block.wantSafety()) { 23468 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); 23469 try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch); 23470 } 23471 } else if (dest_len != .none) { 23472 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { 23473 len_val = dest_len_val; 23474 } 23475 } else if (src_len != .none) { 23476 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { 23477 len_val = src_len_val; 23478 } 23479 } 23480 23481 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { 23482 if (!dest_ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src; 23483 if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |_| { 23484 const len_u64 = (try len_val.?.getUnsignedIntAdvanced(mod, sema)).?; 23485 const len = try sema.usizeCast(block, dest_src, len_u64); 23486 for (0..len) |i| { 23487 const elem_index = try sema.addIntUnsigned(Type.usize, i); 23488 const dest_elem_ptr = try sema.elemPtrOneLayerOnly( 23489 block, 23490 src, 23491 dest_ptr, 23492 elem_index, 23493 src, 23494 true, // init 23495 false, // oob_safety 23496 ); 23497 const src_elem_ptr = try sema.elemPtrOneLayerOnly( 23498 block, 23499 src, 23500 src_ptr, 23501 elem_index, 23502 src, 23503 false, // init 23504 false, // oob_safety 23505 ); 23506 const uncoerced_elem = try sema.analyzeLoad(block, src, src_elem_ptr, src_src); 23507 try sema.storePtr2( 23508 block, 23509 src, 23510 dest_elem_ptr, 23511 dest_src, 23512 uncoerced_elem, 23513 src_src, 23514 .store, 23515 ); 23516 } 23517 return; 23518 } else break :rs src_src; 23519 } else dest_src; 23520 23521 // If in-memory coercion is not allowed, explode this memcpy call into a 23522 // for loop that copies element-wise. 23523 // Likewise if this is an iterable rather than a pointer, do the same 23524 // lowering. The AIR instruction requires pointers with element types of 23525 // equal ABI size. 23526 23527 if (dest_ty.zigTypeTag(mod) != .Pointer or src_ty.zigTypeTag(mod) != .Pointer) { 23528 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{}); 23529 } 23530 23531 const dest_elem_ty = dest_ty.elemType2(mod); 23532 const src_elem_ty = src_ty.elemType2(mod); 23533 if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) { 23534 return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{}); 23535 } 23536 23537 // If the length is comptime-known, then upgrade src and destination types 23538 // into pointer-to-array. At this point we know they are both pointers 23539 // already. 23540 var new_dest_ptr = dest_ptr; 23541 var new_src_ptr = src_ptr; 23542 if (len_val) |val| { 23543 const len = val.toUnsignedInt(mod); 23544 if (len == 0) { 23545 // This AIR instruction guarantees length > 0 if it is comptime-known. 23546 return; 23547 } 23548 new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len); 23549 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len); 23550 } 23551 23552 if (dest_len != .none) { 23553 // Change the src from slice to a many pointer, to avoid multiple ptr 23554 // slice extractions in AIR instructions. 23555 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 23556 if (new_src_ptr_ty.isSlice(mod)) { 23557 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 23558 } 23559 } else if (dest_len == .none and len_val == null) { 23560 // Change the dest to a slice, since its type must have the length. 23561 const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr); 23562 new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, .unneeded, dest_src, dest_src, dest_src, false); 23563 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 23564 if (new_src_ptr_ty.isSlice(mod)) { 23565 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); 23566 } 23567 } 23568 23569 try sema.requireRuntimeBlock(block, src, runtime_src); 23570 23571 // Aliasing safety check. 23572 if (block.wantSafety()) { 23573 const len = if (len_val) |v| 23574 try sema.addConstant(v) 23575 else if (dest_len != .none) 23576 dest_len 23577 else 23578 src_len; 23579 23580 // Extract raw pointer from dest slice. The AIR instructions could support them, but 23581 // it would cause redundant machine code instructions. 23582 const new_dest_ptr_ty = sema.typeOf(new_dest_ptr); 23583 const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(mod)) 23584 try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty) 23585 else if (new_dest_ptr_ty.ptrSize(mod) == .One) ptr: { 23586 var dest_manyptr_ty_key = mod.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type; 23587 assert(dest_manyptr_ty_key.flags.size == .One); 23588 dest_manyptr_ty_key.child = dest_elem_ty.toIntern(); 23589 dest_manyptr_ty_key.flags.size = .Many; 23590 break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src); 23591 } else new_dest_ptr; 23592 23593 const new_src_ptr_ty = sema.typeOf(new_src_ptr); 23594 const raw_src_ptr = if (new_src_ptr_ty.isSlice(mod)) 23595 try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty) 23596 else if (new_src_ptr_ty.ptrSize(mod) == .One) ptr: { 23597 var src_manyptr_ty_key = mod.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type; 23598 assert(src_manyptr_ty_key.flags.size == .One); 23599 src_manyptr_ty_key.child = src_elem_ty.toIntern(); 23600 src_manyptr_ty_key.flags.size = .Many; 23601 break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(src_manyptr_ty_key), new_src_ptr, src_src); 23602 } else new_src_ptr; 23603 23604 // ok1: dest >= src + len 23605 // ok2: src >= dest + len 23606 const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src); 23607 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src); 23608 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len); 23609 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len); 23610 const ok = try block.addBinOp(.bit_or, ok1, ok2); 23611 try sema.addSafetyCheck(block, ok, .memcpy_alias); 23612 } 23613 23614 _ = try block.addInst(.{ 23615 .tag = .memcpy, 23616 .data = .{ .bin_op = .{ 23617 .lhs = new_dest_ptr, 23618 .rhs = new_src_ptr, 23619 } }, 23620 }); 23621 } 23622 23623 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { 23624 const mod = sema.mod; 23625 const gpa = sema.gpa; 23626 const ip = &mod.intern_pool; 23627 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 23628 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; 23629 const src = inst_data.src(); 23630 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; 23631 const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; 23632 const dest_ptr = try sema.resolveInst(extra.lhs); 23633 const uncoerced_elem = try sema.resolveInst(extra.rhs); 23634 const dest_ptr_ty = sema.typeOf(dest_ptr); 23635 try checkMemOperand(sema, block, dest_src, dest_ptr_ty); 23636 23637 if (dest_ptr_ty.isConstPtr(mod)) { 23638 return sema.fail(block, dest_src, "cannot memset constant pointer", .{}); 23639 } 23640 23641 const dest_elem_ty = dest_ptr_ty.elemType2(mod); 23642 23643 const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { 23644 const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len"), dest_src); 23645 const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse 23646 break :rs dest_src; 23647 const len_u64 = (try len_val.getUnsignedIntAdvanced(mod, sema)).?; 23648 const len = try sema.usizeCast(block, dest_src, len_u64); 23649 if (len == 0) { 23650 // This AIR instruction guarantees length > 0 if it is comptime-known. 23651 return; 23652 } 23653 23654 if (!ptr_val.isComptimeMutablePtr(mod)) break :rs dest_src; 23655 if (try sema.resolveMaybeUndefVal(uncoerced_elem)) |_| { 23656 for (0..len) |i| { 23657 const elem_index = try sema.addIntUnsigned(Type.usize, i); 23658 const elem_ptr = try sema.elemPtrOneLayerOnly( 23659 block, 23660 src, 23661 dest_ptr, 23662 elem_index, 23663 src, 23664 true, // init 23665 false, // oob_safety 23666 ); 23667 try sema.storePtr2( 23668 block, 23669 src, 23670 elem_ptr, 23671 dest_src, 23672 uncoerced_elem, 23673 value_src, 23674 .store, 23675 ); 23676 } 23677 return; 23678 } else break :rs value_src; 23679 } else dest_src; 23680 23681 const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); 23682 23683 try sema.requireRuntimeBlock(block, src, runtime_src); 23684 _ = try block.addInst(.{ 23685 .tag = if (block.wantSafety()) .memset_safe else .memset, 23686 .data = .{ .bin_op = .{ 23687 .lhs = dest_ptr, 23688 .rhs = elem, 23689 } }, 23690 }); 23691 } 23692 23693 fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { 23694 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 23695 const src = LazySrcLoc.nodeOffset(extra.node); 23696 return sema.failWithUseOfAsync(block, src); 23697 } 23698 23699 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23700 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 23701 const src = inst_data.src(); 23702 return sema.failWithUseOfAsync(block, src); 23703 } 23704 23705 fn zirAwait( 23706 sema: *Sema, 23707 block: *Block, 23708 inst: Zir.Inst.Index, 23709 ) CompileError!Air.Inst.Ref { 23710 const inst_data = sema.code.instructions.items(.data)[inst].un_node; 23711 const src = inst_data.src(); 23712 23713 return sema.failWithUseOfAsync(block, src); 23714 } 23715 23716 fn zirAwaitNosuspend( 23717 sema: *Sema, 23718 block: *Block, 23719 extended: Zir.Inst.Extended.InstData, 23720 ) CompileError!Air.Inst.Ref { 23721 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 23722 const src = LazySrcLoc.nodeOffset(extra.node); 23723 23724 return sema.failWithUseOfAsync(block, src); 23725 } 23726 23727 fn zirVarExtended( 23728 sema: *Sema, 23729 block: *Block, 23730 extended: Zir.Inst.Extended.InstData, 23731 ) CompileError!Air.Inst.Ref { 23732 const mod = sema.mod; 23733 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand); 23734 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 }; 23735 const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 }; 23736 const small = @as(Zir.Inst.ExtendedVar.Small, @bitCast(extended.small)); 23737 23738 var extra_index: usize = extra.end; 23739 23740 const lib_name: ?[]const u8 = if (small.has_lib_name) blk: { 23741 const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]); 23742 extra_index += 1; 23743 break :blk lib_name; 23744 } else null; 23745 23746 // ZIR supports encoding this information but it is not used; the information 23747 // is encoded via the Decl entry. 23748 assert(!small.has_align); 23749 23750 const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: { 23751 const init_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23752 extra_index += 1; 23753 break :blk try sema.resolveInst(init_ref); 23754 } else .none; 23755 23756 const have_ty = extra.data.var_type != .none; 23757 const var_ty = if (have_ty) 23758 try sema.resolveType(block, ty_src, extra.data.var_type) 23759 else 23760 sema.typeOf(uncasted_init); 23761 23762 const init_val = if (uncasted_init != .none) blk: { 23763 const init = if (have_ty) 23764 try sema.coerce(block, var_ty, uncasted_init, init_src) 23765 else 23766 uncasted_init; 23767 23768 break :blk ((try sema.resolveMaybeUndefVal(init)) orelse 23769 return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known")).toIntern(); 23770 } else .none; 23771 23772 try sema.validateVarType(block, ty_src, var_ty, small.is_extern); 23773 23774 return sema.addConstant((try mod.intern(.{ .variable = .{ 23775 .ty = var_ty.toIntern(), 23776 .init = init_val, 23777 .decl = sema.owner_decl_index, 23778 .lib_name = if (lib_name) |lname| (try mod.intern_pool.getOrPutString( 23779 sema.gpa, 23780 try sema.handleExternLibName(block, ty_src, lname), 23781 )).toOptional() else .none, 23782 .is_extern = small.is_extern, 23783 .is_threadlocal = small.is_threadlocal, 23784 } })).toValue()); 23785 } 23786 23787 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { 23788 const tracy = trace(@src()); 23789 defer tracy.end(); 23790 23791 const mod = sema.mod; 23792 const inst_data = sema.code.instructions.items(.data)[inst].pl_node; 23793 const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); 23794 const target = mod.getTarget(); 23795 23796 const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node }; 23797 const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node }; 23798 const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node }; 23799 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; 23800 const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node }; 23801 const has_body = extra.data.body_len != 0; 23802 23803 var extra_index: usize = extra.end; 23804 23805 const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: { 23806 const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]); 23807 extra_index += 1; 23808 break :blk lib_name; 23809 } else null; 23810 23811 if (has_body and 23812 (extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and 23813 !target_util.supportsFunctionAlignment(target)) 23814 { 23815 return sema.fail(block, align_src, "target does not support function alignment", .{}); 23816 } 23817 23818 const @"align": ?Alignment = if (extra.data.bits.has_align_body) blk: { 23819 const body_len = sema.code.extra[extra_index]; 23820 extra_index += 1; 23821 const body = sema.code.extra[extra_index..][0..body_len]; 23822 extra_index += body.len; 23823 23824 const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, "alignment must be comptime-known"); 23825 if (val.isGenericPoison()) { 23826 break :blk null; 23827 } 23828 const alignment = @as(u32, @intCast(val.toUnsignedInt(mod))); 23829 try sema.validateAlign(block, align_src, alignment); 23830 if (alignment == target_util.defaultFunctionAlignment(target)) { 23831 break :blk .none; 23832 } else { 23833 break :blk Alignment.fromNonzeroByteUnits(alignment); 23834 } 23835 } else if (extra.data.bits.has_align_ref) blk: { 23836 const align_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23837 extra_index += 1; 23838 const align_tv = sema.resolveInstConst(block, align_src, align_ref, "alignment must be comptime-known") catch |err| switch (err) { 23839 error.GenericPoison => { 23840 break :blk null; 23841 }, 23842 else => |e| return e, 23843 }; 23844 const alignment = @as(u32, @intCast(align_tv.val.toUnsignedInt(mod))); 23845 try sema.validateAlign(block, align_src, alignment); 23846 if (alignment == target_util.defaultFunctionAlignment(target)) { 23847 break :blk .none; 23848 } else { 23849 break :blk Alignment.fromNonzeroByteUnits(alignment); 23850 } 23851 } else .none; 23852 23853 const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: { 23854 const body_len = sema.code.extra[extra_index]; 23855 extra_index += 1; 23856 const body = sema.code.extra[extra_index..][0..body_len]; 23857 extra_index += body.len; 23858 23859 const addrspace_ty = try sema.getBuiltinType("AddressSpace"); 23860 const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, "addrespace must be comptime-known"); 23861 if (val.isGenericPoison()) { 23862 break :blk null; 23863 } 23864 break :blk mod.toEnum(std.builtin.AddressSpace, val); 23865 } else if (extra.data.bits.has_addrspace_ref) blk: { 23866 const addrspace_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23867 extra_index += 1; 23868 const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref, "addrespace must be comptime-known") catch |err| switch (err) { 23869 error.GenericPoison => { 23870 break :blk null; 23871 }, 23872 else => |e| return e, 23873 }; 23874 break :blk mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); 23875 } else target_util.defaultAddressSpace(target, .function); 23876 23877 const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: { 23878 const body_len = sema.code.extra[extra_index]; 23879 extra_index += 1; 23880 const body = sema.code.extra[extra_index..][0..body_len]; 23881 extra_index += body.len; 23882 23883 const ty = Type.slice_const_u8; 23884 const val = try sema.resolveGenericBody(block, section_src, body, inst, ty, "linksection must be comptime-known"); 23885 if (val.isGenericPoison()) { 23886 break :blk FuncLinkSection{ .generic = {} }; 23887 } 23888 break :blk FuncLinkSection{ .explicit = try val.toIpString(ty, mod) }; 23889 } else if (extra.data.bits.has_section_ref) blk: { 23890 const section_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23891 extra_index += 1; 23892 const section_name = sema.resolveConstStringIntern(block, section_src, section_ref, "linksection must be comptime-known") catch |err| switch (err) { 23893 error.GenericPoison => { 23894 break :blk FuncLinkSection{ .generic = {} }; 23895 }, 23896 else => |e| return e, 23897 }; 23898 break :blk FuncLinkSection{ .explicit = section_name }; 23899 } else FuncLinkSection{ .default = {} }; 23900 23901 const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: { 23902 const body_len = sema.code.extra[extra_index]; 23903 extra_index += 1; 23904 const body = sema.code.extra[extra_index..][0..body_len]; 23905 extra_index += body.len; 23906 23907 const cc_ty = try sema.getBuiltinType("CallingConvention"); 23908 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, "calling convention must be comptime-known"); 23909 if (val.isGenericPoison()) { 23910 break :blk null; 23911 } 23912 break :blk mod.toEnum(std.builtin.CallingConvention, val); 23913 } else if (extra.data.bits.has_cc_ref) blk: { 23914 const cc_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23915 extra_index += 1; 23916 const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref, "calling convention must be comptime-known") catch |err| switch (err) { 23917 error.GenericPoison => { 23918 break :blk null; 23919 }, 23920 else => |e| return e, 23921 }; 23922 break :blk mod.toEnum(std.builtin.CallingConvention, cc_tv.val); 23923 } else if (sema.owner_decl.is_exported and has_body) 23924 .C 23925 else 23926 .Unspecified; 23927 23928 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { 23929 const body_len = sema.code.extra[extra_index]; 23930 extra_index += 1; 23931 const body = sema.code.extra[extra_index..][0..body_len]; 23932 extra_index += body.len; 23933 23934 const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, "return type must be comptime-known"); 23935 const ty = val.toType(); 23936 break :blk ty; 23937 } else if (extra.data.bits.has_ret_ty_ref) blk: { 23938 const ret_ty_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); 23939 extra_index += 1; 23940 const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, "return type must be comptime-known") catch |err| switch (err) { 23941 error.GenericPoison => { 23942 break :blk Type.generic_poison; 23943 }, 23944 else => |e| return e, 23945 }; 23946 const ty = ret_ty_tv.val.toType(); 23947 break :blk ty; 23948 } else Type.void; 23949 23950 const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { 23951 const x = sema.code.extra[extra_index]; 23952 extra_index += 1; 23953 break :blk x; 23954 } else 0; 23955 23956 var src_locs: Zir.Inst.Func.SrcLocs = undefined; 23957 if (has_body) { 23958 extra_index += extra.data.body_len; 23959 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; 23960 } 23961 23962 const is_var_args = extra.data.bits.is_var_args; 23963 const is_inferred_error = extra.data.bits.is_inferred_error; 23964 const is_extern = extra.data.bits.is_extern; 23965 const is_noinline = extra.data.bits.is_noinline; 23966 23967 return sema.funcCommon( 23968 block, 23969 inst_data.src_node, 23970 inst, 23971 @"align", 23972 @"addrspace", 23973 @"linksection", 23974 cc, 23975 ret_ty, 23976 is_var_args, 23977 is_inferred_error, 23978 is_extern, 23979 has_body, 23980 src_locs, 23981 lib_name, 23982 noalias_bits, 23983 is_noinline, 23984 ); 23985 } 23986 23987 fn zirCUndef( 23988 sema: *Sema, 23989 block: *Block, 23990 extended: Zir.Inst.Extended.InstData, 23991 ) CompileError!Air.Inst.Ref { 23992 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 23993 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 23994 23995 const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known"); 23996 try block.c_import_buf.?.writer().print("#undef {s}\n", .{name}); 23997 return Air.Inst.Ref.void_value; 23998 } 23999 24000 fn zirCInclude( 24001 sema: *Sema, 24002 block: *Block, 24003 extended: Zir.Inst.Extended.InstData, 24004 ) CompileError!Air.Inst.Ref { 24005 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24006 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24007 24008 const name = try sema.resolveConstString(block, src, extra.operand, "path being included must be comptime-known"); 24009 try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name}); 24010 return Air.Inst.Ref.void_value; 24011 } 24012 24013 fn zirCDefine( 24014 sema: *Sema, 24015 block: *Block, 24016 extended: Zir.Inst.Extended.InstData, 24017 ) CompileError!Air.Inst.Ref { 24018 const mod = sema.mod; 24019 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 24020 const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24021 const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 24022 24023 const name = try sema.resolveConstString(block, name_src, extra.lhs, "name of macro being undefined must be comptime-known"); 24024 const rhs = try sema.resolveInst(extra.rhs); 24025 if (sema.typeOf(rhs).zigTypeTag(mod) != .Void) { 24026 const value = try sema.resolveConstString(block, val_src, extra.rhs, "value of macro being undefined must be comptime-known"); 24027 try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); 24028 } else { 24029 try block.c_import_buf.?.writer().print("#define {s}\n", .{name}); 24030 } 24031 return Air.Inst.Ref.void_value; 24032 } 24033 24034 fn zirWasmMemorySize( 24035 sema: *Sema, 24036 block: *Block, 24037 extended: Zir.Inst.Extended.InstData, 24038 ) CompileError!Air.Inst.Ref { 24039 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24040 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24041 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 24042 const target = sema.mod.getTarget(); 24043 if (!target.isWasm()) { 24044 return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 24045 } 24046 24047 const index = @as(u32, @intCast(try sema.resolveInt(block, index_src, extra.operand, Type.u32, "wasm memory size index must be comptime-known"))); 24048 try sema.requireRuntimeBlock(block, builtin_src, null); 24049 return block.addInst(.{ 24050 .tag = .wasm_memory_size, 24051 .data = .{ .pl_op = .{ 24052 .operand = .none, 24053 .payload = index, 24054 } }, 24055 }); 24056 } 24057 24058 fn zirWasmMemoryGrow( 24059 sema: *Sema, 24060 block: *Block, 24061 extended: Zir.Inst.Extended.InstData, 24062 ) CompileError!Air.Inst.Ref { 24063 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 24064 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 24065 const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24066 const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 24067 const target = sema.mod.getTarget(); 24068 if (!target.isWasm()) { 24069 return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); 24070 } 24071 24072 const index = @as(u32, @intCast(try sema.resolveInt(block, index_src, extra.lhs, Type.u32, "wasm memory size index must be comptime-known"))); 24073 const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src); 24074 24075 try sema.requireRuntimeBlock(block, builtin_src, null); 24076 return block.addInst(.{ 24077 .tag = .wasm_memory_grow, 24078 .data = .{ .pl_op = .{ 24079 .operand = delta, 24080 .payload = index, 24081 } }, 24082 }); 24083 } 24084 24085 fn resolvePrefetchOptions( 24086 sema: *Sema, 24087 block: *Block, 24088 src: LazySrcLoc, 24089 zir_ref: Zir.Inst.Ref, 24090 ) CompileError!std.builtin.PrefetchOptions { 24091 const mod = sema.mod; 24092 const gpa = sema.gpa; 24093 const ip = &mod.intern_pool; 24094 const options_ty = try sema.getBuiltinType("PrefetchOptions"); 24095 const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src); 24096 24097 const rw_src = sema.maybeOptionsSrc(block, src, "rw"); 24098 const locality_src = sema.maybeOptionsSrc(block, src, "locality"); 24099 const cache_src = sema.maybeOptionsSrc(block, src, "cache"); 24100 24101 const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw"), rw_src); 24102 const rw_val = try sema.resolveConstValue(block, rw_src, rw, "prefetch read/write must be comptime-known"); 24103 24104 const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality"), locality_src); 24105 const locality_val = try sema.resolveConstValue(block, locality_src, locality, "prefetch locality must be comptime-known"); 24106 24107 const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache"), cache_src); 24108 const cache_val = try sema.resolveConstValue(block, cache_src, cache, "prefetch cache must be comptime-known"); 24109 24110 return std.builtin.PrefetchOptions{ 24111 .rw = mod.toEnum(std.builtin.PrefetchOptions.Rw, rw_val), 24112 .locality = @as(u2, @intCast(locality_val.toUnsignedInt(mod))), 24113 .cache = mod.toEnum(std.builtin.PrefetchOptions.Cache, cache_val), 24114 }; 24115 } 24116 24117 fn zirPrefetch( 24118 sema: *Sema, 24119 block: *Block, 24120 extended: Zir.Inst.Extended.InstData, 24121 ) CompileError!Air.Inst.Ref { 24122 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 24123 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24124 const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 24125 const ptr = try sema.resolveInst(extra.lhs); 24126 try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); 24127 24128 const options = sema.resolvePrefetchOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { 24129 error.NeededSourceLocation => { 24130 _ = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs); 24131 unreachable; 24132 }, 24133 else => |e| return e, 24134 }; 24135 24136 if (!block.is_comptime) { 24137 _ = try block.addInst(.{ 24138 .tag = .prefetch, 24139 .data = .{ .prefetch = .{ 24140 .ptr = ptr, 24141 .rw = options.rw, 24142 .locality = options.locality, 24143 .cache = options.cache, 24144 } }, 24145 }); 24146 } 24147 24148 return Air.Inst.Ref.void_value; 24149 } 24150 24151 fn resolveExternOptions( 24152 sema: *Sema, 24153 block: *Block, 24154 src: LazySrcLoc, 24155 zir_ref: Zir.Inst.Ref, 24156 ) CompileError!struct { 24157 name: InternPool.NullTerminatedString, 24158 library_name: InternPool.OptionalNullTerminatedString = .none, 24159 linkage: std.builtin.GlobalLinkage = .Strong, 24160 is_thread_local: bool = false, 24161 } { 24162 const mod = sema.mod; 24163 const gpa = sema.gpa; 24164 const ip = &mod.intern_pool; 24165 const options_inst = try sema.resolveInst(zir_ref); 24166 const extern_options_ty = try sema.getBuiltinType("ExternOptions"); 24167 const options = try sema.coerce(block, extern_options_ty, options_inst, src); 24168 24169 const name_src = sema.maybeOptionsSrc(block, src, "name"); 24170 const library_src = sema.maybeOptionsSrc(block, src, "library"); 24171 const linkage_src = sema.maybeOptionsSrc(block, src, "linkage"); 24172 const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local"); 24173 24174 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); 24175 const name_val = try sema.resolveConstValue(block, name_src, name_ref, "name of the extern symbol must be comptime-known"); 24176 const name = try name_val.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); 24177 24178 const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src); 24179 const library_name_val = try sema.resolveConstValue(block, library_src, library_name_inst, "library in which extern symbol is must be comptime-known"); 24180 24181 const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); 24182 const linkage_val = try sema.resolveConstValue(block, linkage_src, linkage_ref, "linkage of the extern symbol must be comptime-known"); 24183 const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); 24184 24185 const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local"), thread_local_src); 24186 const is_thread_local_val = try sema.resolveConstValue(block, thread_local_src, is_thread_local, "threadlocality of the extern symbol must be comptime-known"); 24187 24188 const library_name = if (library_name_val.optionalValue(mod)) |payload| blk: { 24189 const library_name = try payload.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); 24190 if (library_name.len == 0) { 24191 return sema.fail(block, library_src, "library name cannot be empty", .{}); 24192 } 24193 break :blk try sema.handleExternLibName(block, library_src, library_name); 24194 } else null; 24195 24196 if (name.len == 0) { 24197 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{}); 24198 } 24199 24200 if (linkage != .Weak and linkage != .Strong) { 24201 return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{}); 24202 } 24203 24204 return .{ 24205 .name = try ip.getOrPutString(gpa, name), 24206 .library_name = try ip.getOrPutStringOpt(gpa, library_name), 24207 .linkage = linkage, 24208 .is_thread_local = is_thread_local_val.toBool(), 24209 }; 24210 } 24211 24212 fn zirBuiltinExtern( 24213 sema: *Sema, 24214 block: *Block, 24215 extended: Zir.Inst.Extended.InstData, 24216 ) CompileError!Air.Inst.Ref { 24217 const mod = sema.mod; 24218 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; 24219 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24220 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; 24221 24222 var ty = try sema.resolveType(block, ty_src, extra.lhs); 24223 if (!ty.isPtrAtRuntime(mod)) { 24224 return sema.fail(block, ty_src, "expected (optional) pointer", .{}); 24225 } 24226 if (!try sema.validateExternType(ty.childType(mod), .other)) { 24227 const msg = msg: { 24228 const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)}); 24229 errdefer msg.destroy(sema.gpa); 24230 const src_decl = sema.mod.declPtr(block.src_decl); 24231 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other); 24232 break :msg msg; 24233 }; 24234 return sema.failWithOwnedErrorMsg(msg); 24235 } 24236 24237 const options = sema.resolveExternOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { 24238 error.NeededSourceLocation => { 24239 _ = try sema.resolveExternOptions(block, options_src, extra.rhs); 24240 unreachable; 24241 }, 24242 else => |e| return e, 24243 }; 24244 24245 if (options.linkage == .Weak and !ty.ptrAllowsZero(mod)) { 24246 ty = try mod.optionalType(ty.toIntern()); 24247 } 24248 24249 // TODO check duplicate extern 24250 24251 const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null); 24252 errdefer mod.destroyDecl(new_decl_index); 24253 const new_decl = mod.declPtr(new_decl_index); 24254 new_decl.name = options.name; 24255 24256 { 24257 const new_var = try mod.intern(.{ .variable = .{ 24258 .ty = ty.toIntern(), 24259 .init = .none, 24260 .decl = sema.owner_decl_index, 24261 .is_extern = true, 24262 .is_const = true, 24263 .is_threadlocal = options.is_thread_local, 24264 .is_weak_linkage = options.linkage == .Weak, 24265 } }); 24266 24267 new_decl.src_line = sema.owner_decl.src_line; 24268 // We only access this decl through the decl_ref with the correct type created 24269 // below, so this type doesn't matter 24270 new_decl.ty = ty; 24271 new_decl.val = new_var.toValue(); 24272 new_decl.alignment = .none; 24273 new_decl.@"linksection" = .none; 24274 new_decl.has_tv = true; 24275 new_decl.analysis = .complete; 24276 new_decl.generation = mod.generation; 24277 } 24278 24279 try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); 24280 try sema.ensureDeclAnalyzed(new_decl_index); 24281 24282 return sema.addConstant(try mod.getCoerced((try mod.intern(.{ .ptr = .{ 24283 .ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) { 24284 .ptr_type => ty.toIntern(), 24285 .opt_type => |child_type| child_type, 24286 else => unreachable, 24287 }, 24288 .addr = .{ .decl = new_decl_index }, 24289 } })).toValue(), ty)); 24290 } 24291 24292 fn zirWorkItem( 24293 sema: *Sema, 24294 block: *Block, 24295 extended: Zir.Inst.Extended.InstData, 24296 zir_tag: Zir.Inst.Extended, 24297 ) CompileError!Air.Inst.Ref { 24298 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; 24299 const dimension_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; 24300 const builtin_src = LazySrcLoc.nodeOffset(extra.node); 24301 const target = sema.mod.getTarget(); 24302 24303 switch (target.cpu.arch) { 24304 // TODO: Allow for other GPU targets. 24305 .amdgcn => {}, 24306 else => { 24307 return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)}); 24308 }, 24309 } 24310 24311 const dimension = @as(u32, @intCast(try sema.resolveInt(block, dimension_src, extra.operand, Type.u32, "dimension must be comptime-known"))); 24312 try sema.requireRuntimeBlock(block, builtin_src, null); 24313 24314 return block.addInst(.{ 24315 .tag = switch (zir_tag) { 24316 .work_item_id => .work_item_id, 24317 .work_group_size => .work_group_size, 24318 .work_group_id => .work_group_id, 24319 else => unreachable, 24320 }, 24321 .data = .{ .pl_op = .{ 24322 .operand = .none, 24323 .payload = dimension, 24324 } }, 24325 }); 24326 } 24327 24328 fn zirInComptime( 24329 sema: *Sema, 24330 block: *Block, 24331 ) CompileError!Air.Inst.Ref { 24332 _ = sema; 24333 if (block.is_comptime) { 24334 return Air.Inst.Ref.bool_true; 24335 } else { 24336 return Air.Inst.Ref.bool_false; 24337 } 24338 } 24339 24340 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { 24341 if (block.is_comptime) { 24342 const msg = msg: { 24343 const msg = try sema.errMsg(block, src, "unable to evaluate comptime expression", .{}); 24344 errdefer msg.destroy(sema.gpa); 24345 24346 if (runtime_src) |some| { 24347 try sema.errNote(block, some, msg, "operation is runtime due to this operand", .{}); 24348 } 24349 if (block.comptime_reason) |some| { 24350 try some.explain(sema, msg); 24351 } 24352 break :msg msg; 24353 }; 24354 return sema.failWithOwnedErrorMsg(msg); 24355 } 24356 } 24357 24358 /// Emit a compile error if type cannot be used for a runtime variable. 24359 fn validateVarType( 24360 sema: *Sema, 24361 block: *Block, 24362 src: LazySrcLoc, 24363 var_ty: Type, 24364 is_extern: bool, 24365 ) CompileError!void { 24366 const mod = sema.mod; 24367 if (is_extern and !try sema.validateExternType(var_ty, .other)) { 24368 const msg = msg: { 24369 const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)}); 24370 errdefer msg.destroy(sema.gpa); 24371 const src_decl = mod.declPtr(block.src_decl); 24372 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other); 24373 break :msg msg; 24374 }; 24375 return sema.failWithOwnedErrorMsg(msg); 24376 } 24377 24378 if (try sema.validateRunTimeType(var_ty, is_extern)) return; 24379 24380 const msg = msg: { 24381 const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)}); 24382 errdefer msg.destroy(sema.gpa); 24383 24384 const src_decl = mod.declPtr(block.src_decl); 24385 try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty); 24386 if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) { 24387 try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{}); 24388 } 24389 24390 break :msg msg; 24391 }; 24392 return sema.failWithOwnedErrorMsg(msg); 24393 } 24394 24395 fn validateRunTimeType( 24396 sema: *Sema, 24397 var_ty: Type, 24398 is_extern: bool, 24399 ) CompileError!bool { 24400 const mod = sema.mod; 24401 var ty = var_ty; 24402 while (true) switch (ty.zigTypeTag(mod)) { 24403 .Bool, 24404 .Int, 24405 .Float, 24406 .ErrorSet, 24407 .Frame, 24408 .AnyFrame, 24409 .Void, 24410 => return true, 24411 24412 .Enum => return !(try sema.typeRequiresComptime(ty)), 24413 24414 .ComptimeFloat, 24415 .ComptimeInt, 24416 .EnumLiteral, 24417 .NoReturn, 24418 .Type, 24419 .Undefined, 24420 .Null, 24421 .Fn, 24422 => return false, 24423 24424 .Pointer => { 24425 const elem_ty = ty.childType(mod); 24426 switch (elem_ty.zigTypeTag(mod)) { 24427 .Opaque => return true, 24428 .Fn => return elem_ty.isFnOrHasRuntimeBits(mod), 24429 else => ty = elem_ty, 24430 } 24431 }, 24432 .Opaque => return is_extern, 24433 24434 .Optional => { 24435 const child_ty = ty.optionalChild(mod); 24436 return sema.validateRunTimeType(child_ty, is_extern); 24437 }, 24438 .Array, .Vector => ty = ty.childType(mod), 24439 24440 .ErrorUnion => ty = ty.errorUnionPayload(mod), 24441 24442 .Struct, .Union => { 24443 const resolved_ty = try sema.resolveTypeFields(ty); 24444 const needs_comptime = try sema.typeRequiresComptime(resolved_ty); 24445 return !needs_comptime; 24446 }, 24447 }; 24448 } 24449 24450 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void); 24451 24452 fn explainWhyTypeIsComptime( 24453 sema: *Sema, 24454 msg: *Module.ErrorMsg, 24455 src_loc: Module.SrcLoc, 24456 ty: Type, 24457 ) CompileError!void { 24458 var type_set = TypeSet{}; 24459 defer type_set.deinit(sema.gpa); 24460 24461 try sema.resolveTypeFully(ty); 24462 return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set); 24463 } 24464 24465 fn explainWhyTypeIsComptimeInner( 24466 sema: *Sema, 24467 msg: *Module.ErrorMsg, 24468 src_loc: Module.SrcLoc, 24469 ty: Type, 24470 type_set: *TypeSet, 24471 ) CompileError!void { 24472 const mod = sema.mod; 24473 switch (ty.zigTypeTag(mod)) { 24474 .Bool, 24475 .Int, 24476 .Float, 24477 .ErrorSet, 24478 .Enum, 24479 .Frame, 24480 .AnyFrame, 24481 .Void, 24482 => return, 24483 24484 .Fn => { 24485 try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{ 24486 ty.fmt(sema.mod), 24487 }); 24488 }, 24489 24490 .Type => { 24491 try mod.errNoteNonLazy(src_loc, msg, "types are not available at runtime", .{}); 24492 }, 24493 24494 .ComptimeFloat, 24495 .ComptimeInt, 24496 .EnumLiteral, 24497 .NoReturn, 24498 .Undefined, 24499 .Null, 24500 => return, 24501 24502 .Opaque => { 24503 try mod.errNoteNonLazy(src_loc, msg, "opaque type '{}' has undefined size", .{ty.fmt(sema.mod)}); 24504 }, 24505 24506 .Array, .Vector => { 24507 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set); 24508 }, 24509 .Pointer => { 24510 const elem_ty = ty.elemType2(mod); 24511 if (elem_ty.zigTypeTag(mod) == .Fn) { 24512 const fn_info = mod.typeToFunc(elem_ty).?; 24513 if (fn_info.is_generic) { 24514 try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{}); 24515 } 24516 switch (fn_info.cc) { 24517 .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}), 24518 else => {}, 24519 } 24520 if (fn_info.return_type.toType().comptimeOnly(mod)) { 24521 try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{}); 24522 } 24523 return; 24524 } 24525 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(mod), type_set); 24526 }, 24527 24528 .Optional => { 24529 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(mod), type_set); 24530 }, 24531 .ErrorUnion => { 24532 try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(mod), type_set); 24533 }, 24534 24535 .Struct => { 24536 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 24537 24538 if (mod.typeToStruct(ty)) |struct_obj| { 24539 for (struct_obj.fields.values(), 0..) |field, i| { 24540 const field_src_loc = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 24541 .index = i, 24542 .range = .type, 24543 }); 24544 24545 if (try sema.typeRequiresComptime(field.ty)) { 24546 try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{}); 24547 try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set); 24548 } 24549 } 24550 } 24551 // TODO tuples 24552 }, 24553 24554 .Union => { 24555 if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; 24556 24557 if (mod.typeToUnion(ty)) |union_obj| { 24558 for (union_obj.fields.values(), 0..) |field, i| { 24559 const field_src_loc = mod.fieldSrcLoc(union_obj.owner_decl, .{ 24560 .index = i, 24561 .range = .type, 24562 }); 24563 24564 if (try sema.typeRequiresComptime(field.ty)) { 24565 try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{}); 24566 try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set); 24567 } 24568 } 24569 } 24570 }, 24571 } 24572 } 24573 24574 const ExternPosition = enum { 24575 ret_ty, 24576 param_ty, 24577 union_field, 24578 struct_field, 24579 element, 24580 other, 24581 }; 24582 24583 /// Returns true if `ty` is allowed in extern types. 24584 /// Does *NOT* require `ty` to be resolved in any way. 24585 /// Calls `resolveTypeLayout` for packed containers. 24586 fn validateExternType( 24587 sema: *Sema, 24588 ty: Type, 24589 position: ExternPosition, 24590 ) !bool { 24591 const mod = sema.mod; 24592 switch (ty.zigTypeTag(mod)) { 24593 .Type, 24594 .ComptimeFloat, 24595 .ComptimeInt, 24596 .EnumLiteral, 24597 .Undefined, 24598 .Null, 24599 .ErrorUnion, 24600 .ErrorSet, 24601 .Frame, 24602 => return false, 24603 .Void => return position == .union_field or position == .ret_ty, 24604 .NoReturn => return position == .ret_ty, 24605 .Opaque, 24606 .Bool, 24607 .Float, 24608 .AnyFrame, 24609 => return true, 24610 .Pointer => return !(ty.isSlice(mod) or try sema.typeRequiresComptime(ty)), 24611 .Int => switch (ty.intInfo(mod).bits) { 24612 8, 16, 32, 64, 128 => return true, 24613 else => return false, 24614 }, 24615 .Fn => { 24616 if (position != .other) return false; 24617 const target = sema.mod.getTarget(); 24618 // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI. 24619 // The goal is to experiment with more integrated CPU/GPU code. 24620 if (ty.fnCallingConvention(mod) == .Kernel and (target.cpu.arch == .nvptx or target.cpu.arch == .nvptx64)) { 24621 return true; 24622 } 24623 return !target_util.fnCallConvAllowsZigTypes(target, ty.fnCallingConvention(mod)); 24624 }, 24625 .Enum => { 24626 return sema.validateExternType(ty.intTagType(mod), position); 24627 }, 24628 .Struct, .Union => switch (ty.containerLayout(mod)) { 24629 .Extern => return true, 24630 .Packed => { 24631 const bit_size = try ty.bitSizeAdvanced(mod, sema); 24632 switch (bit_size) { 24633 8, 16, 32, 64, 128 => return true, 24634 else => return false, 24635 } 24636 }, 24637 .Auto => return false, 24638 }, 24639 .Array => { 24640 if (position == .ret_ty or position == .param_ty) return false; 24641 return sema.validateExternType(ty.elemType2(mod), .element); 24642 }, 24643 .Vector => return sema.validateExternType(ty.elemType2(mod), .element), 24644 .Optional => return ty.isPtrLikeOptional(mod), 24645 } 24646 } 24647 24648 fn explainWhyTypeIsNotExtern( 24649 sema: *Sema, 24650 msg: *Module.ErrorMsg, 24651 src_loc: Module.SrcLoc, 24652 ty: Type, 24653 position: ExternPosition, 24654 ) CompileError!void { 24655 const mod = sema.mod; 24656 switch (ty.zigTypeTag(mod)) { 24657 .Opaque, 24658 .Bool, 24659 .Float, 24660 .AnyFrame, 24661 => return, 24662 24663 .Type, 24664 .ComptimeFloat, 24665 .ComptimeInt, 24666 .EnumLiteral, 24667 .Undefined, 24668 .Null, 24669 .ErrorUnion, 24670 .ErrorSet, 24671 .Frame, 24672 => return, 24673 24674 .Pointer => { 24675 if (ty.isSlice(mod)) { 24676 try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); 24677 } else { 24678 const pointee_ty = ty.childType(mod); 24679 try mod.errNoteNonLazy(src_loc, msg, "pointer to comptime-only type '{}'", .{pointee_ty.fmt(sema.mod)}); 24680 try sema.explainWhyTypeIsComptime(msg, src_loc, pointee_ty); 24681 } 24682 }, 24683 .Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}), 24684 .NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), 24685 .Int => if (!std.math.isPowerOfTwo(ty.intInfo(mod).bits)) { 24686 try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{}); 24687 } else { 24688 try mod.errNoteNonLazy(src_loc, msg, "only integers with 8, 16, 32, 64 and 128 bits are extern compatible", .{}); 24689 }, 24690 .Fn => { 24691 if (position != .other) { 24692 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 24693 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 24694 return; 24695 } 24696 switch (ty.fnCallingConvention(mod)) { 24697 .Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}), 24698 .Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}), 24699 .Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}), 24700 else => return, 24701 } 24702 }, 24703 .Enum => { 24704 const tag_ty = ty.intTagType(mod); 24705 try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)}); 24706 try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position); 24707 }, 24708 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}), 24709 .Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}), 24710 .Array => { 24711 if (position == .ret_ty) { 24712 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); 24713 } else if (position == .param_ty) { 24714 return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); 24715 } 24716 try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element); 24717 }, 24718 .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(mod), .element), 24719 .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}), 24720 } 24721 } 24722 24723 /// Returns true if `ty` is allowed in packed types. 24724 /// Does *NOT* require `ty` to be resolved in any way. 24725 fn validatePackedType(ty: Type, mod: *Module) bool { 24726 switch (ty.zigTypeTag(mod)) { 24727 .Type, 24728 .ComptimeFloat, 24729 .ComptimeInt, 24730 .EnumLiteral, 24731 .Undefined, 24732 .Null, 24733 .ErrorUnion, 24734 .ErrorSet, 24735 .Frame, 24736 .NoReturn, 24737 .Opaque, 24738 .AnyFrame, 24739 .Fn, 24740 .Array, 24741 => return false, 24742 .Optional => return ty.isPtrLikeOptional(mod), 24743 .Void, 24744 .Bool, 24745 .Float, 24746 .Int, 24747 .Vector, 24748 .Enum, 24749 => return true, 24750 .Pointer => return !ty.isSlice(mod), 24751 .Struct, .Union => return ty.containerLayout(mod) == .Packed, 24752 } 24753 } 24754 24755 fn explainWhyTypeIsNotPacked( 24756 sema: *Sema, 24757 msg: *Module.ErrorMsg, 24758 src_loc: Module.SrcLoc, 24759 ty: Type, 24760 ) CompileError!void { 24761 const mod = sema.mod; 24762 switch (ty.zigTypeTag(mod)) { 24763 .Void, 24764 .Bool, 24765 .Float, 24766 .Int, 24767 .Vector, 24768 .Enum, 24769 => return, 24770 .Type, 24771 .ComptimeFloat, 24772 .ComptimeInt, 24773 .EnumLiteral, 24774 .Undefined, 24775 .Null, 24776 .Frame, 24777 .NoReturn, 24778 .Opaque, 24779 .ErrorUnion, 24780 .ErrorSet, 24781 .AnyFrame, 24782 .Optional, 24783 .Array, 24784 => try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}), 24785 .Pointer => try mod.errNoteNonLazy(src_loc, msg, "slices have no guaranteed in-memory representation", .{}), 24786 .Fn => { 24787 try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}); 24788 try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{}); 24789 }, 24790 .Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}), 24791 .Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}), 24792 } 24793 } 24794 24795 fn prepareSimplePanic(sema: *Sema, block: *Block) !void { 24796 const mod = sema.mod; 24797 24798 if (mod.panic_func_index == .none) { 24799 const decl_index = (try sema.getBuiltinDecl(block, "panic")); 24800 // decl_index may be an alias; we must find the decl that actually 24801 // owns the function. 24802 try sema.ensureDeclAnalyzed(decl_index); 24803 const tv = try mod.declPtr(decl_index).typedValue(); 24804 assert(tv.ty.zigTypeTag(mod) == .Fn); 24805 assert(try sema.fnHasRuntimeBits(tv.ty)); 24806 const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap().?; 24807 try mod.ensureFuncBodyAnalysisQueued(func_index); 24808 mod.panic_func_index = func_index.toOptional(); 24809 } 24810 24811 if (mod.null_stack_trace == .none) { 24812 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); 24813 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); 24814 const target = mod.getTarget(); 24815 const ptr_stack_trace_ty = try mod.ptrType(.{ 24816 .child = stack_trace_ty.toIntern(), 24817 .flags = .{ 24818 .address_space = target_util.defaultAddressSpace(target, .global_constant), 24819 }, 24820 }); 24821 const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern()); 24822 mod.null_stack_trace = try mod.intern(.{ .opt = .{ 24823 .ty = opt_ptr_stack_trace_ty.toIntern(), 24824 .val = .none, 24825 } }); 24826 } 24827 } 24828 24829 /// Backends depend on panic decls being available when lowering safety-checked 24830 /// instructions. This function ensures the panic function will be available to 24831 /// be called during that time. 24832 fn preparePanicId(sema: *Sema, block: *Block, panic_id: Module.PanicId) !Module.Decl.Index { 24833 const mod = sema.mod; 24834 const gpa = sema.gpa; 24835 if (mod.panic_messages[@intFromEnum(panic_id)].unwrap()) |x| return x; 24836 24837 try sema.prepareSimplePanic(block); 24838 24839 const panic_messages_ty = try sema.getBuiltinType("panic_messages"); 24840 const msg_decl_index = (try sema.namespaceLookup( 24841 block, 24842 sema.src, 24843 panic_messages_ty.getNamespaceIndex(mod).unwrap().?, 24844 try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id)), 24845 )).?; 24846 try sema.ensureDeclAnalyzed(msg_decl_index); 24847 mod.panic_messages[@intFromEnum(panic_id)] = msg_decl_index.toOptional(); 24848 return msg_decl_index; 24849 } 24850 24851 fn addSafetyCheck( 24852 sema: *Sema, 24853 parent_block: *Block, 24854 ok: Air.Inst.Ref, 24855 panic_id: Module.PanicId, 24856 ) !void { 24857 const gpa = sema.gpa; 24858 assert(!parent_block.is_comptime); 24859 24860 var fail_block: Block = .{ 24861 .parent = parent_block, 24862 .sema = sema, 24863 .src_decl = parent_block.src_decl, 24864 .namespace = parent_block.namespace, 24865 .wip_capture_scope = parent_block.wip_capture_scope, 24866 .instructions = .{}, 24867 .inlining = parent_block.inlining, 24868 .is_comptime = false, 24869 }; 24870 24871 defer fail_block.instructions.deinit(gpa); 24872 24873 try sema.safetyPanic(&fail_block, panic_id); 24874 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 24875 } 24876 24877 fn addSafetyCheckExtra( 24878 sema: *Sema, 24879 parent_block: *Block, 24880 ok: Air.Inst.Ref, 24881 fail_block: *Block, 24882 ) !void { 24883 const gpa = sema.gpa; 24884 24885 try parent_block.instructions.ensureUnusedCapacity(gpa, 1); 24886 24887 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + 24888 1 + // The main block only needs space for the cond_br. 24889 @typeInfo(Air.CondBr).Struct.fields.len + 24890 1 + // The ok branch of the cond_br only needs space for the br. 24891 fail_block.instructions.items.len); 24892 24893 try sema.air_instructions.ensureUnusedCapacity(gpa, 3); 24894 const block_inst = @as(Air.Inst.Index, @intCast(sema.air_instructions.len)); 24895 const cond_br_inst = block_inst + 1; 24896 const br_inst = cond_br_inst + 1; 24897 sema.air_instructions.appendAssumeCapacity(.{ 24898 .tag = .block, 24899 .data = .{ .ty_pl = .{ 24900 .ty = .void_type, 24901 .payload = sema.addExtraAssumeCapacity(Air.Block{ 24902 .body_len = 1, 24903 }), 24904 } }, 24905 }); 24906 sema.air_extra.appendAssumeCapacity(cond_br_inst); 24907 24908 sema.air_instructions.appendAssumeCapacity(.{ 24909 .tag = .cond_br, 24910 .data = .{ .pl_op = .{ 24911 .operand = ok, 24912 .payload = sema.addExtraAssumeCapacity(Air.CondBr{ 24913 .then_body_len = 1, 24914 .else_body_len = @as(u32, @intCast(fail_block.instructions.items.len)), 24915 }), 24916 } }, 24917 }); 24918 sema.air_extra.appendAssumeCapacity(br_inst); 24919 sema.air_extra.appendSliceAssumeCapacity(fail_block.instructions.items); 24920 24921 sema.air_instructions.appendAssumeCapacity(.{ 24922 .tag = .br, 24923 .data = .{ .br = .{ 24924 .block_inst = block_inst, 24925 .operand = .void_value, 24926 } }, 24927 }); 24928 24929 parent_block.instructions.appendAssumeCapacity(block_inst); 24930 } 24931 24932 fn panicWithMsg(sema: *Sema, block: *Block, msg_inst: Air.Inst.Ref) !void { 24933 const mod = sema.mod; 24934 24935 if (!mod.backendSupportsFeature(.panic_fn)) { 24936 _ = try block.addNoOp(.trap); 24937 return; 24938 } 24939 24940 try sema.prepareSimplePanic(block); 24941 24942 const panic_func = mod.funcPtrUnwrap(mod.panic_func_index).?; 24943 const panic_fn = try sema.analyzeDeclVal(block, .unneeded, panic_func.owner_decl); 24944 const null_stack_trace = try sema.addConstant(mod.null_stack_trace.toValue()); 24945 24946 const opt_usize_ty = try mod.optionalType(.usize_type); 24947 const null_ret_addr = try sema.addConstant((try mod.intern(.{ .opt = .{ 24948 .ty = opt_usize_ty.toIntern(), 24949 .val = .none, 24950 } })).toValue()); 24951 try sema.callBuiltin(block, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr }); 24952 } 24953 24954 fn panicUnwrapError( 24955 sema: *Sema, 24956 parent_block: *Block, 24957 operand: Air.Inst.Ref, 24958 unwrap_err_tag: Air.Inst.Tag, 24959 is_non_err_tag: Air.Inst.Tag, 24960 ) !void { 24961 assert(!parent_block.is_comptime); 24962 const ok = try parent_block.addUnOp(is_non_err_tag, operand); 24963 if (!sema.mod.comp.formatted_panics) { 24964 return sema.addSafetyCheck(parent_block, ok, .unwrap_error); 24965 } 24966 const gpa = sema.gpa; 24967 24968 var fail_block: Block = .{ 24969 .parent = parent_block, 24970 .sema = sema, 24971 .src_decl = parent_block.src_decl, 24972 .namespace = parent_block.namespace, 24973 .wip_capture_scope = parent_block.wip_capture_scope, 24974 .instructions = .{}, 24975 .inlining = parent_block.inlining, 24976 .is_comptime = false, 24977 }; 24978 24979 defer fail_block.instructions.deinit(gpa); 24980 24981 { 24982 if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) { 24983 _ = try fail_block.addNoOp(.trap); 24984 } else { 24985 const panic_fn = try sema.getBuiltin("panicUnwrapError"); 24986 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); 24987 const err_return_trace = try sema.getErrorReturnTrace(&fail_block); 24988 const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; 24989 try sema.callBuiltin(&fail_block, panic_fn, .auto, &args); 24990 } 24991 } 24992 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 24993 } 24994 24995 fn panicIndexOutOfBounds( 24996 sema: *Sema, 24997 parent_block: *Block, 24998 index: Air.Inst.Ref, 24999 len: Air.Inst.Ref, 25000 cmp_op: Air.Inst.Tag, 25001 ) !void { 25002 assert(!parent_block.is_comptime); 25003 const ok = try parent_block.addBinOp(cmp_op, index, len); 25004 if (!sema.mod.comp.formatted_panics) { 25005 return sema.addSafetyCheck(parent_block, ok, .index_out_of_bounds); 25006 } 25007 try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len }); 25008 } 25009 25010 fn panicInactiveUnionField( 25011 sema: *Sema, 25012 parent_block: *Block, 25013 active_tag: Air.Inst.Ref, 25014 wanted_tag: Air.Inst.Ref, 25015 ) !void { 25016 assert(!parent_block.is_comptime); 25017 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); 25018 if (!sema.mod.comp.formatted_panics) { 25019 return sema.addSafetyCheck(parent_block, ok, .inactive_union_field); 25020 } 25021 try sema.safetyCheckFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag }); 25022 } 25023 25024 fn panicSentinelMismatch( 25025 sema: *Sema, 25026 parent_block: *Block, 25027 maybe_sentinel: ?Value, 25028 sentinel_ty: Type, 25029 ptr: Air.Inst.Ref, 25030 sentinel_index: Air.Inst.Ref, 25031 ) !void { 25032 assert(!parent_block.is_comptime); 25033 const mod = sema.mod; 25034 const expected_sentinel_val = maybe_sentinel orelse return; 25035 const expected_sentinel = try sema.addConstant(expected_sentinel_val); 25036 25037 const ptr_ty = sema.typeOf(ptr); 25038 const actual_sentinel = if (ptr_ty.isSlice(mod)) 25039 try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) 25040 else blk: { 25041 const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null); 25042 const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty); 25043 break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); 25044 }; 25045 25046 const ok = if (sentinel_ty.zigTypeTag(mod) == .Vector) ok: { 25047 const eql = 25048 try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); 25049 break :ok try parent_block.addInst(.{ 25050 .tag = .reduce, 25051 .data = .{ .reduce = .{ 25052 .operand = eql, 25053 .operation = .And, 25054 } }, 25055 }); 25056 } else if (sentinel_ty.isSelfComparable(mod, true)) 25057 try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel) 25058 else { 25059 const panic_fn = try sema.getBuiltin("checkNonScalarSentinel"); 25060 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel }; 25061 try sema.callBuiltin(parent_block, panic_fn, .auto, &args); 25062 return; 25063 }; 25064 25065 if (!sema.mod.comp.formatted_panics) { 25066 return sema.addSafetyCheck(parent_block, ok, .sentinel_mismatch); 25067 } 25068 try sema.safetyCheckFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel }); 25069 } 25070 25071 fn safetyCheckFormatted( 25072 sema: *Sema, 25073 parent_block: *Block, 25074 ok: Air.Inst.Ref, 25075 func: []const u8, 25076 args: []const Air.Inst.Ref, 25077 ) CompileError!void { 25078 assert(sema.mod.comp.formatted_panics); 25079 const gpa = sema.gpa; 25080 25081 var fail_block: Block = .{ 25082 .parent = parent_block, 25083 .sema = sema, 25084 .src_decl = parent_block.src_decl, 25085 .namespace = parent_block.namespace, 25086 .wip_capture_scope = parent_block.wip_capture_scope, 25087 .instructions = .{}, 25088 .inlining = parent_block.inlining, 25089 .is_comptime = false, 25090 }; 25091 25092 defer fail_block.instructions.deinit(gpa); 25093 25094 if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) { 25095 _ = try fail_block.addNoOp(.trap); 25096 } else { 25097 const panic_fn = try sema.getBuiltin(func); 25098 try sema.callBuiltin(&fail_block, panic_fn, .auto, args); 25099 } 25100 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); 25101 } 25102 25103 fn safetyPanic(sema: *Sema, block: *Block, panic_id: Module.PanicId) CompileError!void { 25104 const msg_decl_index = try sema.preparePanicId(block, panic_id); 25105 const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index); 25106 try sema.panicWithMsg(block, msg_inst); 25107 } 25108 25109 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { 25110 sema.branch_count += 1; 25111 if (sema.branch_count > sema.branch_quota) { 25112 const msg = try sema.errMsg( 25113 block, 25114 src, 25115 "evaluation exceeded {d} backwards branches", 25116 .{sema.branch_quota}, 25117 ); 25118 try sema.errNote( 25119 block, 25120 src, 25121 msg, 25122 "use @setEvalBranchQuota() to raise the branch limit from {d}", 25123 .{sema.branch_quota}, 25124 ); 25125 return sema.failWithOwnedErrorMsg(msg); 25126 } 25127 } 25128 25129 fn fieldVal( 25130 sema: *Sema, 25131 block: *Block, 25132 src: LazySrcLoc, 25133 object: Air.Inst.Ref, 25134 field_name: InternPool.NullTerminatedString, 25135 field_name_src: LazySrcLoc, 25136 ) CompileError!Air.Inst.Ref { 25137 // When editing this function, note that there is corresponding logic to be edited 25138 // in `fieldPtr`. This function takes a value and returns a value. 25139 25140 const mod = sema.mod; 25141 const ip = &mod.intern_pool; 25142 const object_src = src; // TODO better source location 25143 const object_ty = sema.typeOf(object); 25144 25145 // Zig allows dereferencing a single pointer during field lookup. Note that 25146 // we don't actually need to generate the dereference some field lookups, like the 25147 // length of arrays and other comptime operations. 25148 const is_pointer_to = object_ty.isSinglePointer(mod); 25149 25150 const inner_ty = if (is_pointer_to) 25151 object_ty.childType(mod) 25152 else 25153 object_ty; 25154 25155 switch (inner_ty.zigTypeTag(mod)) { 25156 .Array => { 25157 if (ip.stringEqlSlice(field_name, "len")) { 25158 return sema.addConstant( 25159 try mod.intValue(Type.usize, inner_ty.arrayLen(mod)), 25160 ); 25161 } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) { 25162 const ptr_info = object_ty.ptrInfo(mod); 25163 const result_ty = try mod.ptrType(.{ 25164 .child = ptr_info.child.toType().childType(mod).toIntern(), 25165 .sentinel = ptr_info.sentinel, 25166 .flags = .{ 25167 .size = .Many, 25168 .alignment = ptr_info.flags.alignment, 25169 .is_const = ptr_info.flags.is_const, 25170 .is_volatile = ptr_info.flags.is_volatile, 25171 .is_allowzero = ptr_info.flags.is_allowzero, 25172 .address_space = ptr_info.flags.address_space, 25173 .vector_index = ptr_info.flags.vector_index, 25174 }, 25175 .packed_offset = ptr_info.packed_offset, 25176 }); 25177 return sema.coerce(block, result_ty, object, src); 25178 } else { 25179 return sema.fail( 25180 block, 25181 field_name_src, 25182 "no member named '{}' in '{}'", 25183 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 25184 ); 25185 } 25186 }, 25187 .Pointer => { 25188 const ptr_info = inner_ty.ptrInfo(mod); 25189 if (ptr_info.flags.size == .Slice) { 25190 if (ip.stringEqlSlice(field_name, "ptr")) { 25191 const slice = if (is_pointer_to) 25192 try sema.analyzeLoad(block, src, object, object_src) 25193 else 25194 object; 25195 return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); 25196 } else if (ip.stringEqlSlice(field_name, "len")) { 25197 const slice = if (is_pointer_to) 25198 try sema.analyzeLoad(block, src, object, object_src) 25199 else 25200 object; 25201 return sema.analyzeSliceLen(block, src, slice); 25202 } else { 25203 return sema.fail( 25204 block, 25205 field_name_src, 25206 "no member named '{}' in '{}'", 25207 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 25208 ); 25209 } 25210 } 25211 }, 25212 .Type => { 25213 const dereffed_type = if (is_pointer_to) 25214 try sema.analyzeLoad(block, src, object, object_src) 25215 else 25216 object; 25217 25218 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?; 25219 const child_type = val.toType(); 25220 25221 switch (try child_type.zigTypeTagOrPoison(mod)) { 25222 .ErrorSet => { 25223 switch (ip.indexToKey(child_type.toIntern())) { 25224 .error_set_type => |error_set_type| blk: { 25225 if (error_set_type.nameIndex(ip, field_name) != null) break :blk; 25226 const msg = msg: { 25227 const msg = try sema.errMsg(block, src, "no error named '{}' in '{}'", .{ 25228 field_name.fmt(ip), child_type.fmt(mod), 25229 }); 25230 errdefer msg.destroy(sema.gpa); 25231 try sema.addDeclaredHereNote(msg, child_type); 25232 break :msg msg; 25233 }; 25234 return sema.failWithOwnedErrorMsg(msg); 25235 }, 25236 .inferred_error_set_type => { 25237 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 25238 }, 25239 .simple_type => |t| { 25240 assert(t == .anyerror); 25241 _ = try mod.getErrorValue(field_name); 25242 }, 25243 else => unreachable, 25244 } 25245 25246 const error_set_type = if (!child_type.isAnyError(mod)) 25247 child_type 25248 else 25249 try mod.singleErrorSetType(field_name); 25250 return sema.addConstant((try mod.intern(.{ .err = .{ 25251 .ty = error_set_type.toIntern(), 25252 .name = field_name, 25253 } })).toValue()); 25254 }, 25255 .Union => { 25256 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25257 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 25258 return inst; 25259 } 25260 } 25261 const union_ty = try sema.resolveTypeFields(child_type); 25262 if (union_ty.unionTagType(mod)) |enum_ty| { 25263 if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| { 25264 const field_index = @as(u32, @intCast(field_index_usize)); 25265 return sema.addConstant( 25266 try mod.enumValueFieldIndex(enum_ty, field_index), 25267 ); 25268 } 25269 } 25270 return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name); 25271 }, 25272 .Enum => { 25273 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25274 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 25275 return inst; 25276 } 25277 } 25278 const field_index_usize = child_type.enumFieldIndex(field_name, mod) orelse 25279 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 25280 const field_index = @as(u32, @intCast(field_index_usize)); 25281 const enum_val = try mod.enumValueFieldIndex(child_type, field_index); 25282 return sema.addConstant(enum_val); 25283 }, 25284 .Struct, .Opaque => { 25285 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25286 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { 25287 return inst; 25288 } 25289 } 25290 return sema.failWithBadMemberAccess(block, child_type, src, field_name); 25291 }, 25292 else => { 25293 const msg = msg: { 25294 const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(mod)}); 25295 errdefer msg.destroy(sema.gpa); 25296 if (child_type.isSlice(mod)) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{}); 25297 if (child_type.zigTypeTag(mod) == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{}); 25298 break :msg msg; 25299 }; 25300 return sema.failWithOwnedErrorMsg(msg); 25301 }, 25302 } 25303 }, 25304 .Struct => if (is_pointer_to) { 25305 // Avoid loading the entire struct by fetching a pointer and loading that 25306 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 25307 return sema.analyzeLoad(block, src, field_ptr, object_src); 25308 } else { 25309 return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty); 25310 }, 25311 .Union => if (is_pointer_to) { 25312 // Avoid loading the entire union by fetching a pointer and loading that 25313 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); 25314 return sema.analyzeLoad(block, src, field_ptr, object_src); 25315 } else { 25316 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty); 25317 }, 25318 else => {}, 25319 } 25320 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 25321 } 25322 25323 fn fieldPtr( 25324 sema: *Sema, 25325 block: *Block, 25326 src: LazySrcLoc, 25327 object_ptr: Air.Inst.Ref, 25328 field_name: InternPool.NullTerminatedString, 25329 field_name_src: LazySrcLoc, 25330 initializing: bool, 25331 ) CompileError!Air.Inst.Ref { 25332 // When editing this function, note that there is corresponding logic to be edited 25333 // in `fieldVal`. This function takes a pointer and returns a pointer. 25334 25335 const mod = sema.mod; 25336 const ip = &mod.intern_pool; 25337 const object_ptr_src = src; // TODO better source location 25338 const object_ptr_ty = sema.typeOf(object_ptr); 25339 const object_ty = switch (object_ptr_ty.zigTypeTag(mod)) { 25340 .Pointer => object_ptr_ty.childType(mod), 25341 else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(mod)}), 25342 }; 25343 25344 // Zig allows dereferencing a single pointer during field lookup. Note that 25345 // we don't actually need to generate the dereference some field lookups, like the 25346 // length of arrays and other comptime operations. 25347 const is_pointer_to = object_ty.isSinglePointer(mod); 25348 25349 const inner_ty = if (is_pointer_to) 25350 object_ty.childType(mod) 25351 else 25352 object_ty; 25353 25354 switch (inner_ty.zigTypeTag(mod)) { 25355 .Array => { 25356 if (ip.stringEqlSlice(field_name, "len")) { 25357 var anon_decl = try block.startAnonDecl(); 25358 defer anon_decl.deinit(); 25359 return sema.analyzeDeclRef(try anon_decl.finish( 25360 Type.usize, 25361 try mod.intValue(Type.usize, inner_ty.arrayLen(mod)), 25362 .none, // default alignment 25363 )); 25364 } else { 25365 return sema.fail( 25366 block, 25367 field_name_src, 25368 "no member named '{}' in '{}'", 25369 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 25370 ); 25371 } 25372 }, 25373 .Pointer => if (inner_ty.isSlice(mod)) { 25374 const inner_ptr = if (is_pointer_to) 25375 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 25376 else 25377 object_ptr; 25378 25379 const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty; 25380 25381 if (ip.stringEqlSlice(field_name, "ptr")) { 25382 const slice_ptr_ty = inner_ty.slicePtrFieldType(mod); 25383 25384 const result_ty = try mod.ptrType(.{ 25385 .child = slice_ptr_ty.toIntern(), 25386 .flags = .{ 25387 .is_const = !attr_ptr_ty.ptrIsMutable(mod), 25388 .is_volatile = attr_ptr_ty.isVolatilePtr(mod), 25389 .address_space = attr_ptr_ty.ptrAddressSpace(mod), 25390 }, 25391 }); 25392 25393 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 25394 return sema.addConstant((try mod.intern(.{ .ptr = .{ 25395 .ty = result_ty.toIntern(), 25396 .addr = .{ .field = .{ 25397 .base = val.toIntern(), 25398 .index = Value.slice_ptr_index, 25399 } }, 25400 } })).toValue()); 25401 } 25402 try sema.requireRuntimeBlock(block, src, null); 25403 25404 return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); 25405 } else if (ip.stringEqlSlice(field_name, "len")) { 25406 const result_ty = try mod.ptrType(.{ 25407 .child = .usize_type, 25408 .flags = .{ 25409 .is_const = !attr_ptr_ty.ptrIsMutable(mod), 25410 .is_volatile = attr_ptr_ty.isVolatilePtr(mod), 25411 .address_space = attr_ptr_ty.ptrAddressSpace(mod), 25412 }, 25413 }); 25414 25415 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { 25416 return sema.addConstant((try mod.intern(.{ .ptr = .{ 25417 .ty = result_ty.toIntern(), 25418 .addr = .{ .field = .{ 25419 .base = val.toIntern(), 25420 .index = Value.slice_len_index, 25421 } }, 25422 } })).toValue()); 25423 } 25424 try sema.requireRuntimeBlock(block, src, null); 25425 25426 return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); 25427 } else { 25428 return sema.fail( 25429 block, 25430 field_name_src, 25431 "no member named '{}' in '{}'", 25432 .{ field_name.fmt(ip), object_ty.fmt(mod) }, 25433 ); 25434 } 25435 }, 25436 .Type => { 25437 _ = try sema.resolveConstValue(block, .unneeded, object_ptr, ""); 25438 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); 25439 const inner = if (is_pointer_to) 25440 try sema.analyzeLoad(block, src, result, object_ptr_src) 25441 else 25442 result; 25443 25444 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?; 25445 const child_type = val.toType(); 25446 25447 switch (child_type.zigTypeTag(mod)) { 25448 .ErrorSet => { 25449 switch (ip.indexToKey(child_type.toIntern())) { 25450 .error_set_type => |error_set_type| blk: { 25451 if (error_set_type.nameIndex(ip, field_name) != null) { 25452 break :blk; 25453 } 25454 return sema.fail(block, src, "no error named '{}' in '{}'", .{ 25455 field_name.fmt(ip), child_type.fmt(mod), 25456 }); 25457 }, 25458 .inferred_error_set_type => { 25459 return sema.fail(block, src, "TODO handle inferred error sets here", .{}); 25460 }, 25461 .simple_type => |t| { 25462 assert(t == .anyerror); 25463 _ = try mod.getErrorValue(field_name); 25464 }, 25465 else => unreachable, 25466 } 25467 25468 var anon_decl = try block.startAnonDecl(); 25469 defer anon_decl.deinit(); 25470 const error_set_type = if (!child_type.isAnyError(mod)) 25471 child_type 25472 else 25473 try mod.singleErrorSetType(field_name); 25474 return sema.analyzeDeclRef(try anon_decl.finish( 25475 error_set_type, 25476 (try mod.intern(.{ .err = .{ 25477 .ty = error_set_type.toIntern(), 25478 .name = field_name, 25479 } })).toValue(), 25480 .none, // default alignment 25481 )); 25482 }, 25483 .Union => { 25484 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25485 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 25486 return inst; 25487 } 25488 } 25489 const union_ty = try sema.resolveTypeFields(child_type); 25490 if (union_ty.unionTagType(mod)) |enum_ty| { 25491 if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| { 25492 const field_index_u32 = @as(u32, @intCast(field_index)); 25493 var anon_decl = try block.startAnonDecl(); 25494 defer anon_decl.deinit(); 25495 return sema.analyzeDeclRef(try anon_decl.finish( 25496 enum_ty, 25497 try mod.enumValueFieldIndex(enum_ty, field_index_u32), 25498 .none, // default alignment 25499 )); 25500 } 25501 } 25502 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 25503 }, 25504 .Enum => { 25505 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25506 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 25507 return inst; 25508 } 25509 } 25510 const field_index = child_type.enumFieldIndex(field_name, mod) orelse { 25511 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 25512 }; 25513 const field_index_u32 = @as(u32, @intCast(field_index)); 25514 var anon_decl = try block.startAnonDecl(); 25515 defer anon_decl.deinit(); 25516 return sema.analyzeDeclRef(try anon_decl.finish( 25517 child_type, 25518 try mod.enumValueFieldIndex(child_type, field_index_u32), 25519 .none, // default alignment 25520 )); 25521 }, 25522 .Struct, .Opaque => { 25523 if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { 25524 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { 25525 return inst; 25526 } 25527 } 25528 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); 25529 }, 25530 else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(mod)}), 25531 } 25532 }, 25533 .Struct => { 25534 const inner_ptr = if (is_pointer_to) 25535 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 25536 else 25537 object_ptr; 25538 return sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 25539 }, 25540 .Union => { 25541 const inner_ptr = if (is_pointer_to) 25542 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) 25543 else 25544 object_ptr; 25545 return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); 25546 }, 25547 else => {}, 25548 } 25549 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); 25550 } 25551 25552 const ResolvedFieldCallee = union(enum) { 25553 /// The LHS of the call was an actual field with this value. 25554 direct: Air.Inst.Ref, 25555 /// This is a method call, with the function and first argument given. 25556 method: struct { 25557 func_inst: Air.Inst.Ref, 25558 arg0_inst: Air.Inst.Ref, 25559 }, 25560 }; 25561 25562 fn fieldCallBind( 25563 sema: *Sema, 25564 block: *Block, 25565 src: LazySrcLoc, 25566 raw_ptr: Air.Inst.Ref, 25567 field_name: InternPool.NullTerminatedString, 25568 field_name_src: LazySrcLoc, 25569 ) CompileError!ResolvedFieldCallee { 25570 // When editing this function, note that there is corresponding logic to be edited 25571 // in `fieldVal`. This function takes a pointer and returns a pointer. 25572 25573 const mod = sema.mod; 25574 const ip = &mod.intern_pool; 25575 const raw_ptr_src = src; // TODO better source location 25576 const raw_ptr_ty = sema.typeOf(raw_ptr); 25577 const inner_ty = if (raw_ptr_ty.zigTypeTag(mod) == .Pointer and (raw_ptr_ty.ptrSize(mod) == .One or raw_ptr_ty.ptrSize(mod) == .C)) 25578 raw_ptr_ty.childType(mod) 25579 else 25580 return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(mod)}); 25581 25582 // Optionally dereference a second pointer to get the concrete type. 25583 const is_double_ptr = inner_ty.zigTypeTag(mod) == .Pointer and inner_ty.ptrSize(mod) == .One; 25584 const concrete_ty = if (is_double_ptr) inner_ty.childType(mod) else inner_ty; 25585 const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; 25586 const object_ptr = if (is_double_ptr) 25587 try sema.analyzeLoad(block, src, raw_ptr, src) 25588 else 25589 raw_ptr; 25590 25591 find_field: { 25592 switch (concrete_ty.zigTypeTag(mod)) { 25593 .Struct => { 25594 const struct_ty = try sema.resolveTypeFields(concrete_ty); 25595 if (mod.typeToStruct(struct_ty)) |struct_obj| { 25596 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse 25597 break :find_field; 25598 const field_index = @as(u32, @intCast(field_index_usize)); 25599 const field = struct_obj.fields.values()[field_index]; 25600 25601 return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr); 25602 } else if (struct_ty.isTuple(mod)) { 25603 if (ip.stringEqlSlice(field_name, "len")) { 25604 return .{ .direct = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod)) }; 25605 } 25606 if (field_name.toUnsigned(ip)) |field_index| { 25607 if (field_index >= struct_ty.structFieldCount(mod)) break :find_field; 25608 return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(field_index, mod), field_index, object_ptr); 25609 } 25610 } else { 25611 const max = struct_ty.structFieldCount(mod); 25612 for (0..max) |i_usize| { 25613 const i = @as(u32, @intCast(i_usize)); 25614 if (field_name == struct_ty.structFieldName(i, mod)) { 25615 return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(i, mod), i, object_ptr); 25616 } 25617 } 25618 } 25619 }, 25620 .Union => { 25621 const union_ty = try sema.resolveTypeFields(concrete_ty); 25622 const fields = union_ty.unionFields(mod); 25623 const field_index_usize = fields.getIndex(field_name) orelse break :find_field; 25624 const field_index = @as(u32, @intCast(field_index_usize)); 25625 const field = fields.values()[field_index]; 25626 25627 return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr); 25628 }, 25629 .Type => { 25630 const namespace = try sema.analyzeLoad(block, src, object_ptr, src); 25631 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) }; 25632 }, 25633 else => {}, 25634 } 25635 } 25636 25637 // If we get here, we need to look for a decl in the struct type instead. 25638 const found_decl = switch (concrete_ty.zigTypeTag(mod)) { 25639 .Struct, .Opaque, .Union, .Enum => found_decl: { 25640 if (concrete_ty.getNamespaceIndex(mod).unwrap()) |namespace| { 25641 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| { 25642 try sema.addReferencedBy(block, src, decl_idx); 25643 const decl_val = try sema.analyzeDeclVal(block, src, decl_idx); 25644 const decl_type = sema.typeOf(decl_val); 25645 if (mod.typeToFunc(decl_type)) |func_type| f: { 25646 if (func_type.param_types.len == 0) break :f; 25647 25648 const first_param_type = func_type.param_types[0].toType(); 25649 // zig fmt: off 25650 if (first_param_type.isGenericPoison() or ( 25651 first_param_type.zigTypeTag(mod) == .Pointer and 25652 (first_param_type.ptrSize(mod) == .One or 25653 first_param_type.ptrSize(mod) == .C) and 25654 first_param_type.childType(mod).eql(concrete_ty, mod))) 25655 { 25656 // zig fmt: on 25657 // Note that if the param type is generic poison, we know that it must 25658 // specifically be `anytype` since it's the first parameter, meaning we 25659 // can safely assume it can be a pointer. 25660 // TODO: bound fn calls on rvalues should probably 25661 // generate a by-value argument somehow. 25662 return .{ .method = .{ 25663 .func_inst = decl_val, 25664 .arg0_inst = object_ptr, 25665 } }; 25666 } else if (first_param_type.eql(concrete_ty, mod)) { 25667 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 25668 return .{ .method = .{ 25669 .func_inst = decl_val, 25670 .arg0_inst = deref, 25671 } }; 25672 } else if (first_param_type.zigTypeTag(mod) == .Optional) { 25673 const child = first_param_type.optionalChild(mod); 25674 if (child.eql(concrete_ty, mod)) { 25675 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 25676 return .{ .method = .{ 25677 .func_inst = decl_val, 25678 .arg0_inst = deref, 25679 } }; 25680 } else if (child.zigTypeTag(mod) == .Pointer and 25681 child.ptrSize(mod) == .One and 25682 child.childType(mod).eql(concrete_ty, mod)) 25683 { 25684 return .{ .method = .{ 25685 .func_inst = decl_val, 25686 .arg0_inst = object_ptr, 25687 } }; 25688 } 25689 } else if (first_param_type.zigTypeTag(mod) == .ErrorUnion and 25690 first_param_type.errorUnionPayload(mod).eql(concrete_ty, mod)) 25691 { 25692 const deref = try sema.analyzeLoad(block, src, object_ptr, src); 25693 return .{ .method = .{ 25694 .func_inst = decl_val, 25695 .arg0_inst = deref, 25696 } }; 25697 } 25698 } 25699 break :found_decl decl_idx; 25700 } 25701 } 25702 break :found_decl null; 25703 }, 25704 else => null, 25705 }; 25706 25707 const msg = msg: { 25708 const msg = try sema.errMsg(block, src, "no field or member function named '{}' in '{}'", .{ 25709 field_name.fmt(ip), 25710 concrete_ty.fmt(mod), 25711 }); 25712 errdefer msg.destroy(sema.gpa); 25713 try sema.addDeclaredHereNote(msg, concrete_ty); 25714 if (found_decl) |decl_idx| { 25715 const decl = mod.declPtr(decl_idx); 25716 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{}' is not a member function", .{field_name.fmt(ip)}); 25717 } 25718 break :msg msg; 25719 }; 25720 return sema.failWithOwnedErrorMsg(msg); 25721 } 25722 25723 fn finishFieldCallBind( 25724 sema: *Sema, 25725 block: *Block, 25726 src: LazySrcLoc, 25727 ptr_ty: Type, 25728 field_ty: Type, 25729 field_index: u32, 25730 object_ptr: Air.Inst.Ref, 25731 ) CompileError!ResolvedFieldCallee { 25732 const mod = sema.mod; 25733 const ptr_field_ty = try mod.ptrType(.{ 25734 .child = field_ty.toIntern(), 25735 .flags = .{ 25736 .is_const = !ptr_ty.ptrIsMutable(mod), 25737 .address_space = ptr_ty.ptrAddressSpace(mod), 25738 }, 25739 }); 25740 25741 const container_ty = ptr_ty.childType(mod); 25742 if (container_ty.zigTypeTag(mod) == .Struct) { 25743 if (try container_ty.structFieldValueComptime(mod, field_index)) |default_val| { 25744 return .{ .direct = try sema.addConstant(default_val) }; 25745 } 25746 } 25747 25748 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| { 25749 const pointer = try sema.addConstant((try mod.intern(.{ .ptr = .{ 25750 .ty = ptr_field_ty.toIntern(), 25751 .addr = .{ .field = .{ 25752 .base = struct_ptr_val.toIntern(), 25753 .index = field_index, 25754 } }, 25755 } })).toValue()); 25756 return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) }; 25757 } 25758 25759 try sema.requireRuntimeBlock(block, src, null); 25760 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); 25761 return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) }; 25762 } 25763 25764 fn namespaceLookup( 25765 sema: *Sema, 25766 block: *Block, 25767 src: LazySrcLoc, 25768 namespace: Namespace.Index, 25769 decl_name: InternPool.NullTerminatedString, 25770 ) CompileError!?Decl.Index { 25771 const mod = sema.mod; 25772 const gpa = sema.gpa; 25773 if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { 25774 const decl = mod.declPtr(decl_index); 25775 if (!decl.is_pub and decl.getFileScope(mod) != block.getFileScope(mod)) { 25776 const msg = msg: { 25777 const msg = try sema.errMsg(block, src, "'{}' is not marked 'pub'", .{ 25778 decl_name.fmt(&mod.intern_pool), 25779 }); 25780 errdefer msg.destroy(gpa); 25781 try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "declared here", .{}); 25782 break :msg msg; 25783 }; 25784 return sema.failWithOwnedErrorMsg(msg); 25785 } 25786 return decl_index; 25787 } 25788 return null; 25789 } 25790 25791 fn namespaceLookupRef( 25792 sema: *Sema, 25793 block: *Block, 25794 src: LazySrcLoc, 25795 namespace: Namespace.Index, 25796 decl_name: InternPool.NullTerminatedString, 25797 ) CompileError!?Air.Inst.Ref { 25798 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; 25799 try sema.addReferencedBy(block, src, decl); 25800 return try sema.analyzeDeclRef(decl); 25801 } 25802 25803 fn namespaceLookupVal( 25804 sema: *Sema, 25805 block: *Block, 25806 src: LazySrcLoc, 25807 namespace: Namespace.Index, 25808 decl_name: InternPool.NullTerminatedString, 25809 ) CompileError!?Air.Inst.Ref { 25810 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; 25811 return try sema.analyzeDeclVal(block, src, decl); 25812 } 25813 25814 fn structFieldPtr( 25815 sema: *Sema, 25816 block: *Block, 25817 src: LazySrcLoc, 25818 struct_ptr: Air.Inst.Ref, 25819 field_name: InternPool.NullTerminatedString, 25820 field_name_src: LazySrcLoc, 25821 unresolved_struct_ty: Type, 25822 initializing: bool, 25823 ) CompileError!Air.Inst.Ref { 25824 const mod = sema.mod; 25825 assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); 25826 25827 const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); 25828 try sema.resolveStructLayout(struct_ty); 25829 25830 if (struct_ty.isTuple(mod)) { 25831 if (mod.intern_pool.stringEqlSlice(field_name, "len")) { 25832 const len_inst = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount(mod)); 25833 return sema.analyzeRef(block, src, len_inst); 25834 } 25835 const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); 25836 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); 25837 } else if (struct_ty.isAnonStruct(mod)) { 25838 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); 25839 return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); 25840 } 25841 25842 const struct_obj = mod.typeToStruct(struct_ty).?; 25843 25844 const field_index_big = struct_obj.fields.getIndex(field_name) orelse 25845 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); 25846 const field_index = @as(u32, @intCast(field_index_big)); 25847 25848 return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty, initializing); 25849 } 25850 25851 fn structFieldPtrByIndex( 25852 sema: *Sema, 25853 block: *Block, 25854 src: LazySrcLoc, 25855 struct_ptr: Air.Inst.Ref, 25856 field_index: u32, 25857 field_src: LazySrcLoc, 25858 struct_ty: Type, 25859 initializing: bool, 25860 ) CompileError!Air.Inst.Ref { 25861 const mod = sema.mod; 25862 if (struct_ty.isAnonStruct(mod)) { 25863 return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing); 25864 } 25865 25866 const struct_obj = mod.typeToStruct(struct_ty).?; 25867 const field = struct_obj.fields.values()[field_index]; 25868 const struct_ptr_ty = sema.typeOf(struct_ptr); 25869 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); 25870 25871 var ptr_ty_data: InternPool.Key.PtrType = .{ 25872 .child = field.ty.toIntern(), 25873 .flags = .{ 25874 .is_const = struct_ptr_ty_info.flags.is_const, 25875 .is_volatile = struct_ptr_ty_info.flags.is_volatile, 25876 .address_space = struct_ptr_ty_info.flags.address_space, 25877 }, 25878 }; 25879 25880 const target = mod.getTarget(); 25881 25882 const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse 25883 try sema.typeAbiAlignment(struct_ptr_ty_info.child.toType()); 25884 25885 if (struct_obj.layout == .Packed) { 25886 comptime assert(Type.packed_struct_layout_version == 2); 25887 25888 var running_bits: u16 = 0; 25889 for (struct_obj.fields.values(), 0..) |f, i| { 25890 if (!(try sema.typeHasRuntimeBits(f.ty))) continue; 25891 25892 if (i == field_index) { 25893 ptr_ty_data.packed_offset.bit_offset = running_bits; 25894 } 25895 running_bits += @as(u16, @intCast(f.ty.bitSize(mod))); 25896 } 25897 ptr_ty_data.packed_offset.host_size = (running_bits + 7) / 8; 25898 25899 // If this is a packed struct embedded in another one, we need to offset 25900 // the bits against each other. 25901 if (struct_ptr_ty_info.packed_offset.host_size != 0) { 25902 ptr_ty_data.packed_offset.host_size = struct_ptr_ty_info.packed_offset.host_size; 25903 ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset; 25904 } 25905 25906 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align); 25907 25908 // If the field happens to be byte-aligned, simplify the pointer type. 25909 // The pointee type bit size must match its ABI byte size so that loads and stores 25910 // do not interfere with the surrounding packed bits. 25911 // We do not attempt this with big-endian targets yet because of nested 25912 // structs and floats. I need to double-check the desired behavior for big endian 25913 // targets before adding the necessary complications to this code. This will not 25914 // cause miscompilations; it only means the field pointer uses bit masking when it 25915 // might not be strictly necessary. 25916 if (parent_align != 0 and ptr_ty_data.packed_offset.bit_offset % 8 == 0 and 25917 target.cpu.arch.endian() == .Little) 25918 { 25919 const elem_size_bytes = ptr_ty_data.child.toType().abiSize(mod); 25920 const elem_size_bits = ptr_ty_data.child.toType().bitSize(mod); 25921 if (elem_size_bytes * 8 == elem_size_bits) { 25922 const byte_offset = ptr_ty_data.packed_offset.bit_offset / 8; 25923 const new_align = @as(Alignment, @enumFromInt(@ctz(byte_offset | parent_align))); 25924 assert(new_align != .none); 25925 ptr_ty_data.flags.alignment = new_align; 25926 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 }; 25927 } 25928 } 25929 } else if (struct_obj.layout == .Extern) { 25930 // For extern structs, field aligment might be bigger than type's natural alignment. Eg, in 25931 // `extern struct { x: u32, y: u16 }` the second field is aligned as u32. 25932 const field_offset = struct_ty.structFieldOffset(field_index, mod); 25933 ptr_ty_data.flags.alignment = Alignment.fromByteUnits( 25934 if (parent_align == 0) 0 else std.math.gcd(field_offset, parent_align), 25935 ); 25936 } else { 25937 // Our alignment is capped at the field alignment 25938 const field_align = try sema.structFieldAlignment(field, struct_obj.layout); 25939 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(@min(field_align, parent_align)); 25940 } 25941 25942 const ptr_field_ty = try mod.ptrType(ptr_ty_data); 25943 25944 if (field.is_comptime) { 25945 const val = try mod.intern(.{ .ptr = .{ 25946 .ty = ptr_field_ty.toIntern(), 25947 .addr = .{ .comptime_field = field.default_val }, 25948 } }); 25949 return sema.addConstant(val.toValue()); 25950 } 25951 25952 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { 25953 const val = try mod.intern(.{ .ptr = .{ 25954 .ty = ptr_field_ty.toIntern(), 25955 .addr = .{ .field = .{ 25956 .base = try struct_ptr_val.intern(struct_ptr_ty, mod), 25957 .index = field_index, 25958 } }, 25959 } }); 25960 return sema.addConstant(val.toValue()); 25961 } 25962 25963 try sema.requireRuntimeBlock(block, src, null); 25964 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); 25965 } 25966 25967 fn structFieldVal( 25968 sema: *Sema, 25969 block: *Block, 25970 src: LazySrcLoc, 25971 struct_byval: Air.Inst.Ref, 25972 field_name: InternPool.NullTerminatedString, 25973 field_name_src: LazySrcLoc, 25974 unresolved_struct_ty: Type, 25975 ) CompileError!Air.Inst.Ref { 25976 const mod = sema.mod; 25977 assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); 25978 25979 const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); 25980 switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) { 25981 .struct_type => |struct_type| { 25982 const struct_obj = mod.structPtrUnwrap(struct_type.index).?; 25983 if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); 25984 25985 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse 25986 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); 25987 const field_index = @as(u32, @intCast(field_index_usize)); 25988 const field = struct_obj.fields.values()[field_index]; 25989 25990 if (field.is_comptime) { 25991 return sema.addConstant(field.default_val.toValue()); 25992 } 25993 25994 if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| { 25995 if (struct_val.isUndef(mod)) return sema.addConstUndef(field.ty); 25996 if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| { 25997 return sema.addConstant(opv); 25998 } 25999 return sema.addConstant(try struct_val.fieldValue(mod, field_index)); 26000 } 26001 26002 try sema.requireRuntimeBlock(block, src, null); 26003 return block.addStructFieldVal(struct_byval, field_index, field.ty); 26004 }, 26005 .anon_struct_type => |anon_struct| { 26006 if (anon_struct.names.len == 0) { 26007 return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); 26008 } else { 26009 const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); 26010 return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty); 26011 } 26012 }, 26013 else => unreachable, 26014 } 26015 } 26016 26017 fn tupleFieldVal( 26018 sema: *Sema, 26019 block: *Block, 26020 src: LazySrcLoc, 26021 tuple_byval: Air.Inst.Ref, 26022 field_name: InternPool.NullTerminatedString, 26023 field_name_src: LazySrcLoc, 26024 tuple_ty: Type, 26025 ) CompileError!Air.Inst.Ref { 26026 const mod = sema.mod; 26027 if (mod.intern_pool.stringEqlSlice(field_name, "len")) { 26028 return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount(mod)); 26029 } 26030 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src); 26031 return sema.tupleFieldValByIndex(block, src, tuple_byval, field_index, tuple_ty); 26032 } 26033 26034 /// Asserts that `field_name` is not "len". 26035 fn tupleFieldIndex( 26036 sema: *Sema, 26037 block: *Block, 26038 tuple_ty: Type, 26039 field_name: InternPool.NullTerminatedString, 26040 field_name_src: LazySrcLoc, 26041 ) CompileError!u32 { 26042 const mod = sema.mod; 26043 assert(!mod.intern_pool.stringEqlSlice(field_name, "len")); 26044 if (field_name.toUnsigned(&mod.intern_pool)) |field_index| { 26045 if (field_index < tuple_ty.structFieldCount(mod)) return field_index; 26046 return sema.fail(block, field_name_src, "index '{}' out of bounds of tuple '{}'", .{ 26047 field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), 26048 }); 26049 } 26050 26051 return sema.fail(block, field_name_src, "no field named '{}' in tuple '{}'", .{ 26052 field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), 26053 }); 26054 } 26055 26056 fn tupleFieldValByIndex( 26057 sema: *Sema, 26058 block: *Block, 26059 src: LazySrcLoc, 26060 tuple_byval: Air.Inst.Ref, 26061 field_index: u32, 26062 tuple_ty: Type, 26063 ) CompileError!Air.Inst.Ref { 26064 const mod = sema.mod; 26065 const field_ty = tuple_ty.structFieldType(field_index, mod); 26066 26067 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| { 26068 return sema.addConstant(default_value); 26069 } 26070 26071 if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| { 26072 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| { 26073 return sema.addConstant(opv); 26074 } 26075 return switch (mod.intern_pool.indexToKey(tuple_val.toIntern())) { 26076 .undef => sema.addConstUndef(field_ty), 26077 .aggregate => |aggregate| sema.addConstant(switch (aggregate.storage) { 26078 .bytes => |bytes| try mod.intValue(Type.u8, bytes[0]), 26079 .elems => |elems| elems[field_index].toValue(), 26080 .repeated_elem => |elem| elem.toValue(), 26081 }), 26082 else => unreachable, 26083 }; 26084 } 26085 26086 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| { 26087 return sema.addConstant(default_val); 26088 } 26089 26090 try sema.requireRuntimeBlock(block, src, null); 26091 return block.addStructFieldVal(tuple_byval, field_index, field_ty); 26092 } 26093 26094 fn unionFieldPtr( 26095 sema: *Sema, 26096 block: *Block, 26097 src: LazySrcLoc, 26098 union_ptr: Air.Inst.Ref, 26099 field_name: InternPool.NullTerminatedString, 26100 field_name_src: LazySrcLoc, 26101 unresolved_union_ty: Type, 26102 initializing: bool, 26103 ) CompileError!Air.Inst.Ref { 26104 const mod = sema.mod; 26105 const ip = &mod.intern_pool; 26106 26107 assert(unresolved_union_ty.zigTypeTag(mod) == .Union); 26108 26109 const union_ptr_ty = sema.typeOf(union_ptr); 26110 const union_ptr_info = union_ptr_ty.ptrInfo(mod); 26111 const union_ty = try sema.resolveTypeFields(unresolved_union_ty); 26112 const union_obj = mod.typeToUnion(union_ty).?; 26113 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 26114 const field = union_obj.fields.values()[field_index]; 26115 const ptr_field_ty = try mod.ptrType(.{ 26116 .child = field.ty.toIntern(), 26117 .flags = .{ 26118 .is_const = union_ptr_info.flags.is_const, 26119 .is_volatile = union_ptr_info.flags.is_volatile, 26120 .address_space = union_ptr_info.flags.address_space, 26121 .alignment = if (union_obj.layout == .Auto) blk: { 26122 const union_align = union_ptr_info.flags.alignment.toByteUnitsOptional() orelse try sema.typeAbiAlignment(union_ty); 26123 const field_align = try sema.unionFieldAlignment(field); 26124 break :blk InternPool.Alignment.fromByteUnits(@min(union_align, field_align)); 26125 } else union_ptr_info.flags.alignment, 26126 }, 26127 .packed_offset = union_ptr_info.packed_offset, 26128 }); 26129 const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?)); 26130 26131 if (initializing and field.ty.zigTypeTag(mod) == .NoReturn) { 26132 const msg = msg: { 26133 const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{}); 26134 errdefer msg.destroy(sema.gpa); 26135 26136 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 26137 field_name.fmt(ip), 26138 }); 26139 try sema.addDeclaredHereNote(msg, union_ty); 26140 break :msg msg; 26141 }; 26142 return sema.failWithOwnedErrorMsg(msg); 26143 } 26144 26145 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { 26146 switch (union_obj.layout) { 26147 .Auto => if (!initializing) { 26148 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse 26149 break :ct; 26150 if (union_val.isUndef(mod)) { 26151 return sema.failWithUseOfUndef(block, src); 26152 } 26153 const un = ip.indexToKey(union_val.toIntern()).un; 26154 const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); 26155 const tag_matches = un.tag == field_tag.toIntern(); 26156 if (!tag_matches) { 26157 const msg = msg: { 26158 const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?; 26159 const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); 26160 const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ 26161 field_name.fmt(ip), 26162 active_field_name.fmt(ip), 26163 }); 26164 errdefer msg.destroy(sema.gpa); 26165 try sema.addDeclaredHereNote(msg, union_ty); 26166 break :msg msg; 26167 }; 26168 return sema.failWithOwnedErrorMsg(msg); 26169 } 26170 }, 26171 .Packed, .Extern => {}, 26172 } 26173 return sema.addConstant((try mod.intern(.{ .ptr = .{ 26174 .ty = ptr_field_ty.toIntern(), 26175 .addr = .{ .field = .{ 26176 .base = union_ptr_val.toIntern(), 26177 .index = field_index, 26178 } }, 26179 } })).toValue()); 26180 } 26181 26182 try sema.requireRuntimeBlock(block, src, null); 26183 if (!initializing and union_obj.layout == .Auto and block.wantSafety() and 26184 union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) 26185 { 26186 const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); 26187 const wanted_tag = try sema.addConstant(wanted_tag_val); 26188 // TODO would it be better if get_union_tag supported pointers to unions? 26189 const union_val = try block.addTyOp(.load, union_ty, union_ptr); 26190 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val); 26191 try sema.panicInactiveUnionField(block, active_tag, wanted_tag); 26192 } 26193 if (field.ty.zigTypeTag(mod) == .NoReturn) { 26194 _ = try block.addNoOp(.unreach); 26195 return Air.Inst.Ref.unreachable_value; 26196 } 26197 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); 26198 } 26199 26200 fn unionFieldVal( 26201 sema: *Sema, 26202 block: *Block, 26203 src: LazySrcLoc, 26204 union_byval: Air.Inst.Ref, 26205 field_name: InternPool.NullTerminatedString, 26206 field_name_src: LazySrcLoc, 26207 unresolved_union_ty: Type, 26208 ) CompileError!Air.Inst.Ref { 26209 const mod = sema.mod; 26210 const ip = &mod.intern_pool; 26211 assert(unresolved_union_ty.zigTypeTag(mod) == .Union); 26212 26213 const union_ty = try sema.resolveTypeFields(unresolved_union_ty); 26214 const union_obj = mod.typeToUnion(union_ty).?; 26215 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); 26216 const field = union_obj.fields.values()[field_index]; 26217 const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?)); 26218 26219 if (try sema.resolveMaybeUndefVal(union_byval)) |union_val| { 26220 if (union_val.isUndef(mod)) return sema.addConstUndef(field.ty); 26221 26222 const un = ip.indexToKey(union_val.toIntern()).un; 26223 const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); 26224 const tag_matches = un.tag == field_tag.toIntern(); 26225 switch (union_obj.layout) { 26226 .Auto => { 26227 if (tag_matches) { 26228 return sema.addConstant(un.val.toValue()); 26229 } else { 26230 const msg = msg: { 26231 const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?; 26232 const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); 26233 const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ 26234 field_name.fmt(ip), active_field_name.fmt(ip), 26235 }); 26236 errdefer msg.destroy(sema.gpa); 26237 try sema.addDeclaredHereNote(msg, union_ty); 26238 break :msg msg; 26239 }; 26240 return sema.failWithOwnedErrorMsg(msg); 26241 } 26242 }, 26243 .Packed, .Extern => { 26244 if (tag_matches) { 26245 return sema.addConstant(un.val.toValue()); 26246 } else { 26247 const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod); 26248 if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field.ty, 0)) |new_val| { 26249 return sema.addConstant(new_val); 26250 } 26251 } 26252 }, 26253 } 26254 } 26255 26256 try sema.requireRuntimeBlock(block, src, null); 26257 if (union_obj.layout == .Auto and block.wantSafety() and 26258 union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) 26259 { 26260 const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); 26261 const wanted_tag = try sema.addConstant(wanted_tag_val); 26262 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval); 26263 try sema.panicInactiveUnionField(block, active_tag, wanted_tag); 26264 } 26265 if (field.ty.zigTypeTag(mod) == .NoReturn) { 26266 _ = try block.addNoOp(.unreach); 26267 return Air.Inst.Ref.unreachable_value; 26268 } 26269 return block.addStructFieldVal(union_byval, field_index, field.ty); 26270 } 26271 26272 fn elemPtr( 26273 sema: *Sema, 26274 block: *Block, 26275 src: LazySrcLoc, 26276 indexable_ptr: Air.Inst.Ref, 26277 elem_index: Air.Inst.Ref, 26278 elem_index_src: LazySrcLoc, 26279 init: bool, 26280 oob_safety: bool, 26281 ) CompileError!Air.Inst.Ref { 26282 const mod = sema.mod; 26283 const indexable_ptr_src = src; // TODO better source location 26284 const indexable_ptr_ty = sema.typeOf(indexable_ptr); 26285 26286 const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(mod)) { 26287 .Pointer => indexable_ptr_ty.childType(mod), 26288 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(mod)}), 26289 }; 26290 try checkIndexable(sema, block, src, indexable_ty); 26291 26292 switch (indexable_ty.zigTypeTag(mod)) { 26293 .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), 26294 .Struct => { 26295 // Tuple field access. 26296 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); 26297 const index = @as(u32, @intCast(index_val.toUnsignedInt(mod))); 26298 return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); 26299 }, 26300 else => { 26301 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); 26302 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); 26303 }, 26304 } 26305 } 26306 26307 /// Asserts that the type of indexable is pointer. 26308 fn elemPtrOneLayerOnly( 26309 sema: *Sema, 26310 block: *Block, 26311 src: LazySrcLoc, 26312 indexable: Air.Inst.Ref, 26313 elem_index: Air.Inst.Ref, 26314 elem_index_src: LazySrcLoc, 26315 init: bool, 26316 oob_safety: bool, 26317 ) CompileError!Air.Inst.Ref { 26318 const indexable_src = src; // TODO better source location 26319 const indexable_ty = sema.typeOf(indexable); 26320 const mod = sema.mod; 26321 26322 try checkIndexable(sema, block, src, indexable_ty); 26323 26324 switch (indexable_ty.ptrSize(mod)) { 26325 .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 26326 .Many, .C => { 26327 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 26328 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 26329 const runtime_src = rs: { 26330 const ptr_val = maybe_ptr_val orelse break :rs indexable_src; 26331 const index_val = maybe_index_val orelse break :rs elem_index_src; 26332 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26333 const result_ty = try sema.elemPtrType(indexable_ty, index); 26334 const elem_ptr = try ptr_val.elemPtr(result_ty, index, mod); 26335 return sema.addConstant(elem_ptr); 26336 }; 26337 const result_ty = try sema.elemPtrType(indexable_ty, null); 26338 26339 try sema.requireRuntimeBlock(block, src, runtime_src); 26340 return block.addPtrElemPtr(indexable, elem_index, result_ty); 26341 }, 26342 .One => { 26343 const child_ty = indexable_ty.childType(mod); 26344 switch (child_ty.zigTypeTag(mod)) { 26345 .Array, .Vector => { 26346 return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety); 26347 }, 26348 .Struct => { 26349 assert(child_ty.isTuple(mod)); 26350 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); 26351 const index = @as(u32, @intCast(index_val.toUnsignedInt(mod))); 26352 return sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); 26353 }, 26354 else => unreachable, // Guaranteed by checkIndexable 26355 } 26356 }, 26357 } 26358 } 26359 26360 fn elemVal( 26361 sema: *Sema, 26362 block: *Block, 26363 src: LazySrcLoc, 26364 indexable: Air.Inst.Ref, 26365 elem_index_uncasted: Air.Inst.Ref, 26366 elem_index_src: LazySrcLoc, 26367 oob_safety: bool, 26368 ) CompileError!Air.Inst.Ref { 26369 const indexable_src = src; // TODO better source location 26370 const indexable_ty = sema.typeOf(indexable); 26371 const mod = sema.mod; 26372 26373 try checkIndexable(sema, block, src, indexable_ty); 26374 26375 // TODO in case of a vector of pointers, we need to detect whether the element 26376 // index is a scalar or vector instead of unconditionally casting to usize. 26377 const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src); 26378 26379 switch (indexable_ty.zigTypeTag(mod)) { 26380 .Pointer => switch (indexable_ty.ptrSize(mod)) { 26381 .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 26382 .Many, .C => { 26383 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); 26384 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 26385 26386 const runtime_src = rs: { 26387 const indexable_val = maybe_indexable_val orelse break :rs indexable_src; 26388 const index_val = maybe_index_val orelse break :rs elem_index_src; 26389 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26390 const elem_ty = indexable_ty.elemType2(mod); 26391 const many_ptr_ty = try mod.manyConstPtrType(elem_ty); 26392 const many_ptr_val = try mod.getCoerced(indexable_val, many_ptr_ty); 26393 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty); 26394 const elem_ptr_val = try many_ptr_val.elemPtr(elem_ptr_ty, index, mod); 26395 if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { 26396 return sema.addConstant(try mod.getCoerced(elem_val, elem_ty)); 26397 } 26398 break :rs indexable_src; 26399 }; 26400 26401 try sema.requireRuntimeBlock(block, src, runtime_src); 26402 return block.addBinOp(.ptr_elem_val, indexable, elem_index); 26403 }, 26404 .One => { 26405 arr_sent: { 26406 const inner_ty = indexable_ty.childType(mod); 26407 if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent; 26408 const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent; 26409 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; 26410 const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod)); 26411 if (index != inner_ty.arrayLen(mod)) break :arr_sent; 26412 return sema.addConstant(sentinel); 26413 } 26414 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); 26415 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); 26416 }, 26417 }, 26418 .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), 26419 .Vector => { 26420 // TODO: If the index is a vector, the result should be a vector. 26421 return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety); 26422 }, 26423 .Struct => { 26424 // Tuple field access. 26425 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known"); 26426 const index = @as(u32, @intCast(index_val.toUnsignedInt(mod))); 26427 return sema.tupleField(block, indexable_src, indexable, elem_index_src, index); 26428 }, 26429 else => unreachable, 26430 } 26431 } 26432 26433 fn validateRuntimeElemAccess( 26434 sema: *Sema, 26435 block: *Block, 26436 elem_index_src: LazySrcLoc, 26437 elem_ty: Type, 26438 parent_ty: Type, 26439 parent_src: LazySrcLoc, 26440 ) CompileError!void { 26441 const mod = sema.mod; 26442 const valid_rt = try sema.validateRunTimeType(elem_ty, false); 26443 if (!valid_rt) { 26444 const msg = msg: { 26445 const msg = try sema.errMsg( 26446 block, 26447 elem_index_src, 26448 "values of type '{}' must be comptime-known, but index value is runtime-known", 26449 .{parent_ty.fmt(mod)}, 26450 ); 26451 errdefer msg.destroy(sema.gpa); 26452 26453 const src_decl = mod.declPtr(block.src_decl); 26454 try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty); 26455 26456 break :msg msg; 26457 }; 26458 return sema.failWithOwnedErrorMsg(msg); 26459 } 26460 } 26461 26462 fn tupleFieldPtr( 26463 sema: *Sema, 26464 block: *Block, 26465 tuple_ptr_src: LazySrcLoc, 26466 tuple_ptr: Air.Inst.Ref, 26467 field_index_src: LazySrcLoc, 26468 field_index: u32, 26469 init: bool, 26470 ) CompileError!Air.Inst.Ref { 26471 const mod = sema.mod; 26472 const tuple_ptr_ty = sema.typeOf(tuple_ptr); 26473 const tuple_ty = tuple_ptr_ty.childType(mod); 26474 _ = try sema.resolveTypeFields(tuple_ty); 26475 const field_count = tuple_ty.structFieldCount(mod); 26476 26477 if (field_count == 0) { 26478 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); 26479 } 26480 26481 if (field_index >= field_count) { 26482 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 26483 field_index, field_count, 26484 }); 26485 } 26486 26487 const field_ty = tuple_ty.structFieldType(field_index, mod); 26488 const ptr_field_ty = try mod.ptrType(.{ 26489 .child = field_ty.toIntern(), 26490 .flags = .{ 26491 .is_const = !tuple_ptr_ty.ptrIsMutable(mod), 26492 .is_volatile = tuple_ptr_ty.isVolatilePtr(mod), 26493 .address_space = tuple_ptr_ty.ptrAddressSpace(mod), 26494 }, 26495 }); 26496 26497 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_val| { 26498 return sema.addConstant((try mod.intern(.{ .ptr = .{ 26499 .ty = ptr_field_ty.toIntern(), 26500 .addr = .{ .comptime_field = default_val.toIntern() }, 26501 } })).toValue()); 26502 } 26503 26504 if (try sema.resolveMaybeUndefVal(tuple_ptr)) |tuple_ptr_val| { 26505 return sema.addConstant((try mod.intern(.{ .ptr = .{ 26506 .ty = ptr_field_ty.toIntern(), 26507 .addr = .{ .field = .{ 26508 .base = tuple_ptr_val.toIntern(), 26509 .index = field_index, 26510 } }, 26511 } })).toValue()); 26512 } 26513 26514 if (!init) { 26515 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src); 26516 } 26517 26518 try sema.requireRuntimeBlock(block, tuple_ptr_src, null); 26519 return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); 26520 } 26521 26522 fn tupleField( 26523 sema: *Sema, 26524 block: *Block, 26525 tuple_src: LazySrcLoc, 26526 tuple: Air.Inst.Ref, 26527 field_index_src: LazySrcLoc, 26528 field_index: u32, 26529 ) CompileError!Air.Inst.Ref { 26530 const mod = sema.mod; 26531 const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple)); 26532 const field_count = tuple_ty.structFieldCount(mod); 26533 26534 if (field_count == 0) { 26535 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); 26536 } 26537 26538 if (field_index >= field_count) { 26539 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ 26540 field_index, field_count, 26541 }); 26542 } 26543 26544 const field_ty = tuple_ty.structFieldType(field_index, mod); 26545 26546 if (try tuple_ty.structFieldValueComptime(mod, field_index)) |default_value| { 26547 return sema.addConstant(default_value); // comptime field 26548 } 26549 26550 if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| { 26551 if (tuple_val.isUndef(mod)) return sema.addConstUndef(field_ty); 26552 return sema.addConstant(try tuple_val.fieldValue(mod, field_index)); 26553 } 26554 26555 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); 26556 26557 try sema.requireRuntimeBlock(block, tuple_src, null); 26558 return block.addStructFieldVal(tuple, field_index, field_ty); 26559 } 26560 26561 fn elemValArray( 26562 sema: *Sema, 26563 block: *Block, 26564 src: LazySrcLoc, 26565 array_src: LazySrcLoc, 26566 array: Air.Inst.Ref, 26567 elem_index_src: LazySrcLoc, 26568 elem_index: Air.Inst.Ref, 26569 oob_safety: bool, 26570 ) CompileError!Air.Inst.Ref { 26571 const mod = sema.mod; 26572 const array_ty = sema.typeOf(array); 26573 const array_sent = array_ty.sentinel(mod); 26574 const array_len = array_ty.arrayLen(mod); 26575 const array_len_s = array_len + @intFromBool(array_sent != null); 26576 const elem_ty = array_ty.childType(mod); 26577 26578 if (array_len_s == 0) { 26579 return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); 26580 } 26581 26582 const maybe_undef_array_val = try sema.resolveMaybeUndefVal(array); 26583 // index must be defined since it can access out of bounds 26584 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 26585 26586 if (maybe_index_val) |index_val| { 26587 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26588 if (array_sent) |s| { 26589 if (index == array_len) { 26590 return sema.addConstant(s); 26591 } 26592 } 26593 if (index >= array_len_s) { 26594 const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else ""; 26595 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 26596 } 26597 } 26598 if (maybe_undef_array_val) |array_val| { 26599 if (array_val.isUndef(mod)) { 26600 return sema.addConstUndef(elem_ty); 26601 } 26602 if (maybe_index_val) |index_val| { 26603 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26604 const elem_val = try array_val.elemValue(mod, index); 26605 return sema.addConstant(elem_val); 26606 } 26607 } 26608 26609 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src); 26610 26611 const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src; 26612 try sema.requireRuntimeBlock(block, src, runtime_src); 26613 if (oob_safety and block.wantSafety()) { 26614 // Runtime check is only needed if unable to comptime check 26615 if (maybe_index_val == null) { 26616 const len_inst = try sema.addIntUnsigned(Type.usize, array_len); 26617 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt; 26618 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); 26619 } 26620 } 26621 return block.addBinOp(.array_elem_val, array, elem_index); 26622 } 26623 26624 fn elemPtrArray( 26625 sema: *Sema, 26626 block: *Block, 26627 src: LazySrcLoc, 26628 array_ptr_src: LazySrcLoc, 26629 array_ptr: Air.Inst.Ref, 26630 elem_index_src: LazySrcLoc, 26631 elem_index: Air.Inst.Ref, 26632 init: bool, 26633 oob_safety: bool, 26634 ) CompileError!Air.Inst.Ref { 26635 const mod = sema.mod; 26636 const array_ptr_ty = sema.typeOf(array_ptr); 26637 const array_ty = array_ptr_ty.childType(mod); 26638 const array_sent = array_ty.sentinel(mod) != null; 26639 const array_len = array_ty.arrayLen(mod); 26640 const array_len_s = array_len + @intFromBool(array_sent); 26641 26642 if (array_len_s == 0) { 26643 return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{}); 26644 } 26645 26646 const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(array_ptr); 26647 // The index must not be undefined since it can be out of bounds. 26648 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 26649 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod)); 26650 if (index >= array_len_s) { 26651 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; 26652 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); 26653 } 26654 break :o index; 26655 } else null; 26656 26657 const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset); 26658 26659 if (maybe_undef_array_ptr_val) |array_ptr_val| { 26660 if (array_ptr_val.isUndef(mod)) { 26661 return sema.addConstUndef(elem_ptr_ty); 26662 } 26663 if (offset) |index| { 26664 const elem_ptr = try array_ptr_val.elemPtr(elem_ptr_ty, index, mod); 26665 return sema.addConstant(elem_ptr); 26666 } 26667 } 26668 26669 if (!init) { 26670 try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(mod), array_ty, array_ptr_src); 26671 } 26672 26673 const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; 26674 try sema.requireRuntimeBlock(block, src, runtime_src); 26675 26676 // Runtime check is only needed if unable to comptime check. 26677 if (oob_safety and block.wantSafety() and offset == null) { 26678 const len_inst = try sema.addIntUnsigned(Type.usize, array_len); 26679 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; 26680 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); 26681 } 26682 26683 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); 26684 } 26685 26686 fn elemValSlice( 26687 sema: *Sema, 26688 block: *Block, 26689 src: LazySrcLoc, 26690 slice_src: LazySrcLoc, 26691 slice: Air.Inst.Ref, 26692 elem_index_src: LazySrcLoc, 26693 elem_index: Air.Inst.Ref, 26694 oob_safety: bool, 26695 ) CompileError!Air.Inst.Ref { 26696 const mod = sema.mod; 26697 const slice_ty = sema.typeOf(slice); 26698 const slice_sent = slice_ty.sentinel(mod) != null; 26699 const elem_ty = slice_ty.elemType2(mod); 26700 var runtime_src = slice_src; 26701 26702 // slice must be defined since it can dereferenced as null 26703 const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); 26704 // index must be defined since it can index out of bounds 26705 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); 26706 26707 if (maybe_slice_val) |slice_val| { 26708 runtime_src = elem_index_src; 26709 const slice_len = slice_val.sliceLen(mod); 26710 const slice_len_s = slice_len + @intFromBool(slice_sent); 26711 if (slice_len_s == 0) { 26712 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 26713 } 26714 if (maybe_index_val) |index_val| { 26715 const index = @as(usize, @intCast(index_val.toUnsignedInt(mod))); 26716 if (index >= slice_len_s) { 26717 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 26718 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 26719 } 26720 const elem_ptr_ty = try sema.elemPtrType(slice_ty, index); 26721 const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod); 26722 if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| { 26723 return sema.addConstant(elem_val); 26724 } 26725 runtime_src = slice_src; 26726 } 26727 } 26728 26729 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src); 26730 26731 try sema.requireRuntimeBlock(block, src, runtime_src); 26732 if (oob_safety and block.wantSafety()) { 26733 const len_inst = if (maybe_slice_val) |slice_val| 26734 try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)) 26735 else 26736 try block.addTyOp(.slice_len, Type.usize, slice); 26737 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 26738 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); 26739 } 26740 try sema.queueFullTypeResolution(sema.typeOf(slice)); 26741 return block.addBinOp(.slice_elem_val, slice, elem_index); 26742 } 26743 26744 fn elemPtrSlice( 26745 sema: *Sema, 26746 block: *Block, 26747 src: LazySrcLoc, 26748 slice_src: LazySrcLoc, 26749 slice: Air.Inst.Ref, 26750 elem_index_src: LazySrcLoc, 26751 elem_index: Air.Inst.Ref, 26752 oob_safety: bool, 26753 ) CompileError!Air.Inst.Ref { 26754 const mod = sema.mod; 26755 const slice_ty = sema.typeOf(slice); 26756 const slice_sent = slice_ty.sentinel(mod) != null; 26757 26758 const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(slice); 26759 // The index must not be undefined since it can be out of bounds. 26760 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { 26761 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(mod)); 26762 break :o index; 26763 } else null; 26764 26765 const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset); 26766 26767 if (maybe_undef_slice_val) |slice_val| { 26768 if (slice_val.isUndef(mod)) { 26769 return sema.addConstUndef(elem_ptr_ty); 26770 } 26771 const slice_len = slice_val.sliceLen(mod); 26772 const slice_len_s = slice_len + @intFromBool(slice_sent); 26773 if (slice_len_s == 0) { 26774 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); 26775 } 26776 if (offset) |index| { 26777 if (index >= slice_len_s) { 26778 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; 26779 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); 26780 } 26781 const elem_ptr_val = try slice_val.elemPtr(elem_ptr_ty, index, mod); 26782 return sema.addConstant(elem_ptr_val); 26783 } 26784 } 26785 26786 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src); 26787 26788 const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src; 26789 try sema.requireRuntimeBlock(block, src, runtime_src); 26790 if (oob_safety and block.wantSafety()) { 26791 const len_inst = len: { 26792 if (maybe_undef_slice_val) |slice_val| 26793 if (!slice_val.isUndef(mod)) 26794 break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)); 26795 break :len try block.addTyOp(.slice_len, Type.usize, slice); 26796 }; 26797 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; 26798 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); 26799 } 26800 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); 26801 } 26802 26803 fn coerce( 26804 sema: *Sema, 26805 block: *Block, 26806 dest_ty_unresolved: Type, 26807 inst: Air.Inst.Ref, 26808 inst_src: LazySrcLoc, 26809 ) CompileError!Air.Inst.Ref { 26810 return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) { 26811 error.NotCoercible => unreachable, 26812 else => |e| return e, 26813 }; 26814 } 26815 26816 const CoersionError = CompileError || error{ 26817 /// When coerce is called recursively, this error should be returned instead of using `fail` 26818 /// to ensure correct types in compile errors. 26819 NotCoercible, 26820 }; 26821 26822 const CoerceOpts = struct { 26823 /// Should coerceExtra emit error messages. 26824 report_err: bool = true, 26825 /// Ignored if `report_err == false`. 26826 is_ret: bool = false, 26827 /// Should coercion to comptime_int ermit an error message. 26828 no_cast_to_comptime_int: bool = false, 26829 26830 param_src: struct { 26831 func_inst: Air.Inst.Ref = .none, 26832 param_i: u32 = undefined, 26833 26834 fn get(info: @This(), sema: *Sema) !?Module.SrcLoc { 26835 if (info.func_inst == .none) return null; 26836 const mod = sema.mod; 26837 const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null; 26838 const param_src = Module.paramSrc(0, mod, fn_decl, info.param_i); 26839 if (param_src == .node_offset_param) { 26840 return Module.SrcLoc{ 26841 .file_scope = fn_decl.getFileScope(mod), 26842 .parent_decl_node = fn_decl.src_node, 26843 .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param), 26844 }; 26845 } 26846 return param_src.toSrcLoc(fn_decl, mod); 26847 } 26848 } = .{}, 26849 }; 26850 26851 fn coerceExtra( 26852 sema: *Sema, 26853 block: *Block, 26854 dest_ty_unresolved: Type, 26855 inst: Air.Inst.Ref, 26856 inst_src: LazySrcLoc, 26857 opts: CoerceOpts, 26858 ) CoersionError!Air.Inst.Ref { 26859 if (dest_ty_unresolved.isGenericPoison()) return inst; 26860 const mod = sema.mod; 26861 const dest_ty_src = inst_src; // TODO better source location 26862 const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); 26863 const inst_ty = try sema.resolveTypeFields(sema.typeOf(inst)); 26864 const target = mod.getTarget(); 26865 // If the types are the same, we can return the operand. 26866 if (dest_ty.eql(inst_ty, mod)) 26867 return inst; 26868 26869 const maybe_inst_val = try sema.resolveMaybeUndefVal(inst); 26870 26871 var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); 26872 if (in_memory_result == .ok) { 26873 if (maybe_inst_val) |val| { 26874 return sema.coerceInMemory(val, dest_ty); 26875 } 26876 try sema.requireRuntimeBlock(block, inst_src, null); 26877 return block.addBitCast(dest_ty, inst); 26878 } 26879 26880 const is_undef = inst_ty.zigTypeTag(mod) == .Undefined; 26881 26882 switch (dest_ty.zigTypeTag(mod)) { 26883 .Optional => optional: { 26884 // undefined sets the optional bit also to undefined. 26885 if (is_undef) { 26886 return sema.addConstUndef(dest_ty); 26887 } 26888 26889 // null to ?T 26890 if (inst_ty.zigTypeTag(mod) == .Null) { 26891 return sema.addConstant((try mod.intern(.{ .opt = .{ 26892 .ty = dest_ty.toIntern(), 26893 .val = .none, 26894 } })).toValue()); 26895 } 26896 26897 // cast from ?*T and ?[*]T to ?*anyopaque 26898 // but don't do it if the source type is a double pointer 26899 if (dest_ty.isPtrLikeOptional(mod) and 26900 dest_ty.elemType2(mod).toIntern() == .anyopaque_type and 26901 inst_ty.isPtrAtRuntime(mod)) 26902 anyopaque_check: { 26903 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; 26904 const elem_ty = inst_ty.elemType2(mod); 26905 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) { 26906 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 26907 .actual = inst_ty, 26908 .wanted = dest_ty, 26909 } }; 26910 break :optional; 26911 } 26912 // Let the logic below handle wrapping the optional now that 26913 // it has been checked to correctly coerce. 26914 if (!inst_ty.isPtrLikeOptional(mod)) break :anyopaque_check; 26915 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 26916 } 26917 26918 // T to ?T 26919 const child_type = dest_ty.optionalChild(mod); 26920 const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 26921 error.NotCoercible => { 26922 if (in_memory_result == .no_match) { 26923 // Try to give more useful notes 26924 in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src); 26925 } 26926 break :optional; 26927 }, 26928 else => |e| return e, 26929 }; 26930 return try sema.wrapOptional(block, dest_ty, intermediate, inst_src); 26931 }, 26932 .Pointer => pointer: { 26933 const dest_info = dest_ty.ptrInfo(mod); 26934 26935 // Function body to function pointer. 26936 if (inst_ty.zigTypeTag(mod) == .Fn) { 26937 const fn_val = try sema.resolveConstValue(block, .unneeded, inst, ""); 26938 const fn_decl = fn_val.pointerDecl(mod).?; 26939 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl); 26940 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); 26941 } 26942 26943 // *T to *[1]T 26944 single_item: { 26945 if (dest_info.flags.size != .One) break :single_item; 26946 if (!inst_ty.isSinglePointer(mod)) break :single_item; 26947 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 26948 const ptr_elem_ty = inst_ty.childType(mod); 26949 const array_ty = dest_info.child.toType(); 26950 if (array_ty.zigTypeTag(mod) != .Array) break :single_item; 26951 const array_elem_ty = array_ty.childType(mod); 26952 if (array_ty.arrayLen(mod) != 1) break :single_item; 26953 const dest_is_mut = !dest_info.flags.is_const; 26954 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { 26955 .ok => {}, 26956 else => break :single_item, 26957 } 26958 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 26959 } 26960 26961 // Coercions where the source is a single pointer to an array. 26962 src_array_ptr: { 26963 if (!inst_ty.isSinglePointer(mod)) break :src_array_ptr; 26964 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 26965 const array_ty = inst_ty.childType(mod); 26966 if (array_ty.zigTypeTag(mod) != .Array) break :src_array_ptr; 26967 const array_elem_type = array_ty.childType(mod); 26968 const dest_is_mut = !dest_info.flags.is_const; 26969 26970 const dst_elem_type = dest_info.child.toType(); 26971 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src); 26972 switch (elem_res) { 26973 .ok => {}, 26974 else => { 26975 in_memory_result = .{ .ptr_child = .{ 26976 .child = try elem_res.dupe(sema.arena), 26977 .actual = array_elem_type, 26978 .wanted = dst_elem_type, 26979 } }; 26980 break :src_array_ptr; 26981 }, 26982 } 26983 26984 if (dest_info.sentinel != .none) { 26985 if (array_ty.sentinel(mod)) |inst_sent| { 26986 if (dest_info.sentinel != (try mod.getCoerced(inst_sent, dst_elem_type)).toIntern()) { 26987 in_memory_result = .{ .ptr_sentinel = .{ 26988 .actual = inst_sent, 26989 .wanted = dest_info.sentinel.toValue(), 26990 .ty = dst_elem_type, 26991 } }; 26992 break :src_array_ptr; 26993 } 26994 } else { 26995 in_memory_result = .{ .ptr_sentinel = .{ 26996 .actual = Value.@"unreachable", 26997 .wanted = dest_info.sentinel.toValue(), 26998 .ty = dst_elem_type, 26999 } }; 27000 break :src_array_ptr; 27001 } 27002 } 27003 27004 switch (dest_info.flags.size) { 27005 .Slice => { 27006 // *[N]T to []T 27007 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src); 27008 }, 27009 .C => { 27010 // *[N]T to [*c]T 27011 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27012 }, 27013 .Many => { 27014 // *[N]T to [*]T 27015 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27016 }, 27017 .One => {}, 27018 } 27019 } 27020 27021 // coercion from C pointer 27022 if (inst_ty.isCPtr(mod)) src_c_ptr: { 27023 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr; 27024 // In this case we must add a safety check because the C pointer 27025 // could be null. 27026 const src_elem_ty = inst_ty.childType(mod); 27027 const dest_is_mut = !dest_info.flags.is_const; 27028 const dst_elem_type = dest_info.child.toType(); 27029 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) { 27030 .ok => {}, 27031 else => break :src_c_ptr, 27032 } 27033 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27034 } 27035 27036 // cast from *T and [*]T to *anyopaque 27037 // but don't do it if the source type is a double pointer 27038 if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(mod) == .Pointer) to_anyopaque: { 27039 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; 27040 const elem_ty = inst_ty.elemType2(mod); 27041 if (elem_ty.zigTypeTag(mod) == .Pointer or elem_ty.isPtrLikeOptional(mod)) { 27042 in_memory_result = .{ .double_ptr_to_anyopaque = .{ 27043 .actual = inst_ty, 27044 .wanted = dest_ty, 27045 } }; 27046 break :pointer; 27047 } 27048 if (dest_ty.isSlice(mod)) break :to_anyopaque; 27049 if (inst_ty.isSlice(mod)) { 27050 in_memory_result = .{ .slice_to_anyopaque = .{ 27051 .actual = inst_ty, 27052 .wanted = dest_ty, 27053 } }; 27054 break :pointer; 27055 } 27056 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27057 } 27058 27059 switch (dest_info.flags.size) { 27060 // coercion to C pointer 27061 .C => switch (inst_ty.zigTypeTag(mod)) { 27062 .Null => { 27063 return sema.addConstant(try mod.getCoerced(Value.null, dest_ty)); 27064 }, 27065 .ComptimeInt => { 27066 const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 27067 error.NotCoercible => break :pointer, 27068 else => |e| return e, 27069 }; 27070 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 27071 }, 27072 .Int => { 27073 const ptr_size_ty = switch (inst_ty.intInfo(mod).signedness) { 27074 .signed => Type.isize, 27075 .unsigned => Type.usize, 27076 }; 27077 const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { 27078 error.NotCoercible => { 27079 // Try to give more useful notes 27080 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src); 27081 break :pointer; 27082 }, 27083 else => |e| return e, 27084 }; 27085 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); 27086 }, 27087 .Pointer => p: { 27088 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 27089 const inst_info = inst_ty.ptrInfo(mod); 27090 switch (try sema.coerceInMemoryAllowed( 27091 block, 27092 dest_info.child.toType(), 27093 inst_info.child.toType(), 27094 !dest_info.flags.is_const, 27095 target, 27096 dest_ty_src, 27097 inst_src, 27098 )) { 27099 .ok => {}, 27100 else => break :p, 27101 } 27102 if (inst_info.flags.size == .Slice) { 27103 assert(dest_info.sentinel == .none); 27104 if (inst_info.sentinel == .none or 27105 inst_info.sentinel != (try mod.intValue(inst_info.child.toType(), 0)).toIntern()) 27106 break :p; 27107 27108 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 27109 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 27110 } 27111 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); 27112 }, 27113 else => {}, 27114 }, 27115 .One => switch (dest_info.child.toType().zigTypeTag(mod)) { 27116 .Union => { 27117 // pointer to anonymous struct to pointer to union 27118 if (inst_ty.isSinglePointer(mod) and 27119 inst_ty.childType(mod).isAnonStruct(mod) and 27120 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 27121 { 27122 return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src); 27123 } 27124 }, 27125 .Struct => { 27126 // pointer to anonymous struct to pointer to struct 27127 if (inst_ty.isSinglePointer(mod) and 27128 inst_ty.childType(mod).isAnonStruct(mod) and 27129 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 27130 { 27131 return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) { 27132 error.NotCoercible => break :pointer, 27133 else => |e| return e, 27134 }; 27135 } 27136 }, 27137 .Array => { 27138 // pointer to tuple to pointer to array 27139 if (inst_ty.isSinglePointer(mod) and 27140 inst_ty.childType(mod).isTuple(mod) and 27141 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) 27142 { 27143 return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src); 27144 } 27145 }, 27146 else => {}, 27147 }, 27148 .Slice => to_slice: { 27149 if (inst_ty.zigTypeTag(mod) == .Array) { 27150 return sema.fail( 27151 block, 27152 inst_src, 27153 "array literal requires address-of operator (&) to coerce to slice type '{}'", 27154 .{dest_ty.fmt(mod)}, 27155 ); 27156 } 27157 27158 if (!inst_ty.isSinglePointer(mod)) break :to_slice; 27159 const inst_child_ty = inst_ty.childType(mod); 27160 if (!inst_child_ty.isTuple(mod)) break :to_slice; 27161 27162 // empty tuple to zero-length slice 27163 // note that this allows coercing to a mutable slice. 27164 if (inst_child_ty.structFieldCount(mod) == 0) { 27165 // Optional slice is represented with a null pointer so 27166 // we use a dummy pointer value with the required alignment. 27167 return sema.addConstant((try mod.intern(.{ .ptr = .{ 27168 .ty = dest_ty.toIntern(), 27169 .addr = .{ .int = (if (dest_info.flags.alignment != .none) 27170 try mod.intValue(Type.usize, dest_info.flags.alignment.toByteUnitsOptional().?) 27171 else 27172 try mod.getCoerced(try dest_info.child.toType().lazyAbiAlignment(mod), Type.usize)).toIntern() }, 27173 .len = (try mod.intValue(Type.usize, 0)).toIntern(), 27174 } })).toValue()); 27175 } 27176 27177 // pointer to tuple to slice 27178 if (!dest_info.flags.is_const) { 27179 const err_msg = err_msg: { 27180 const err_msg = try sema.errMsg(block, inst_src, "cannot cast pointer to tuple to '{}'", .{dest_ty.fmt(mod)}); 27181 errdefer err_msg.deinit(sema.gpa); 27182 try sema.errNote(block, dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{}); 27183 break :err_msg err_msg; 27184 }; 27185 return sema.failWithOwnedErrorMsg(err_msg); 27186 } 27187 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); 27188 }, 27189 .Many => p: { 27190 if (!inst_ty.isSlice(mod)) break :p; 27191 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; 27192 const inst_info = inst_ty.ptrInfo(mod); 27193 27194 switch (try sema.coerceInMemoryAllowed( 27195 block, 27196 dest_info.child.toType(), 27197 inst_info.child.toType(), 27198 !dest_info.flags.is_const, 27199 target, 27200 dest_ty_src, 27201 inst_src, 27202 )) { 27203 .ok => {}, 27204 else => break :p, 27205 } 27206 27207 if (dest_info.sentinel == .none or inst_info.sentinel == .none or 27208 dest_info.sentinel != 27209 try mod.intern_pool.getCoerced(sema.gpa, inst_info.sentinel, dest_info.child)) 27210 break :p; 27211 27212 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); 27213 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); 27214 }, 27215 } 27216 }, 27217 .Int, .ComptimeInt => switch (inst_ty.zigTypeTag(mod)) { 27218 .Float, .ComptimeFloat => float: { 27219 if (is_undef) { 27220 return sema.addConstUndef(dest_ty); 27221 } 27222 const val = (try sema.resolveMaybeUndefVal(inst)) orelse { 27223 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { 27224 if (!opts.report_err) return error.NotCoercible; 27225 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known"); 27226 } 27227 break :float; 27228 }; 27229 27230 if (val.floatHasFraction(mod)) { 27231 return sema.fail( 27232 block, 27233 inst_src, 27234 "fractional component prevents float value '{}' from coercion to type '{}'", 27235 .{ val.fmtValue(inst_ty, mod), dest_ty.fmt(mod) }, 27236 ); 27237 } 27238 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty); 27239 return try sema.addConstant(result_val); 27240 }, 27241 .Int, .ComptimeInt => { 27242 if (is_undef) { 27243 return sema.addConstUndef(dest_ty); 27244 } 27245 if (try sema.resolveMaybeUndefVal(inst)) |val| { 27246 // comptime-known integer to other number 27247 if (!(try sema.intFitsInType(val, dest_ty, null))) { 27248 if (!opts.report_err) return error.NotCoercible; 27249 return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) }); 27250 } 27251 return try sema.addConstant(try mod.getCoerced(val, dest_ty)); 27252 } 27253 if (dest_ty.zigTypeTag(mod) == .ComptimeInt) { 27254 if (!opts.report_err) return error.NotCoercible; 27255 if (opts.no_cast_to_comptime_int) return inst; 27256 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known"); 27257 } 27258 27259 // integer widening 27260 const dst_info = dest_ty.intInfo(mod); 27261 const src_info = inst_ty.intInfo(mod); 27262 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or 27263 // small enough unsigned ints can get casted to large enough signed ints 27264 (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) 27265 { 27266 try sema.requireRuntimeBlock(block, inst_src, null); 27267 return block.addTyOp(.intcast, dest_ty, inst); 27268 } 27269 }, 27270 .Undefined => { 27271 return sema.addConstUndef(dest_ty); 27272 }, 27273 else => {}, 27274 }, 27275 .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) { 27276 .ComptimeFloat => { 27277 const val = try sema.resolveConstValue(block, .unneeded, inst, ""); 27278 const result_val = try val.floatCast(dest_ty, mod); 27279 return try sema.addConstant(result_val); 27280 }, 27281 .Float => { 27282 if (is_undef) { 27283 return sema.addConstUndef(dest_ty); 27284 } 27285 if (try sema.resolveMaybeUndefVal(inst)) |val| { 27286 const result_val = try val.floatCast(dest_ty, mod); 27287 if (!val.eql(try result_val.floatCast(inst_ty, mod), inst_ty, mod)) { 27288 return sema.fail( 27289 block, 27290 inst_src, 27291 "type '{}' cannot represent float value '{}'", 27292 .{ dest_ty.fmt(mod), val.fmtValue(inst_ty, mod) }, 27293 ); 27294 } 27295 return try sema.addConstant(result_val); 27296 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { 27297 if (!opts.report_err) return error.NotCoercible; 27298 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known"); 27299 } 27300 27301 // float widening 27302 const src_bits = inst_ty.floatBits(target); 27303 const dst_bits = dest_ty.floatBits(target); 27304 if (dst_bits >= src_bits) { 27305 try sema.requireRuntimeBlock(block, inst_src, null); 27306 return block.addTyOp(.fpext, dest_ty, inst); 27307 } 27308 }, 27309 .Int, .ComptimeInt => int: { 27310 if (is_undef) { 27311 return sema.addConstUndef(dest_ty); 27312 } 27313 const val = (try sema.resolveMaybeUndefVal(inst)) orelse { 27314 if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) { 27315 if (!opts.report_err) return error.NotCoercible; 27316 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known"); 27317 } 27318 break :int; 27319 }; 27320 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, mod, sema); 27321 // TODO implement this compile error 27322 //const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty); 27323 //if (!int_again_val.eql(val, inst_ty, mod)) { 27324 // return sema.fail( 27325 // block, 27326 // inst_src, 27327 // "type '{}' cannot represent integer value '{}'", 27328 // .{ dest_ty.fmt(mod), val }, 27329 // ); 27330 //} 27331 return try sema.addConstant(result_val); 27332 }, 27333 .Undefined => { 27334 return sema.addConstUndef(dest_ty); 27335 }, 27336 else => {}, 27337 }, 27338 .Enum => switch (inst_ty.zigTypeTag(mod)) { 27339 .EnumLiteral => { 27340 // enum literal to enum 27341 const val = try sema.resolveConstValue(block, .unneeded, inst, ""); 27342 const string = mod.intern_pool.indexToKey(val.toIntern()).enum_literal; 27343 const field_index = dest_ty.enumFieldIndex(string, mod) orelse { 27344 const msg = msg: { 27345 const msg = try sema.errMsg( 27346 block, 27347 inst_src, 27348 "no field named '{}' in enum '{}'", 27349 .{ string.fmt(&mod.intern_pool), dest_ty.fmt(mod) }, 27350 ); 27351 errdefer msg.destroy(sema.gpa); 27352 try sema.addDeclaredHereNote(msg, dest_ty); 27353 break :msg msg; 27354 }; 27355 return sema.failWithOwnedErrorMsg(msg); 27356 }; 27357 return sema.addConstant( 27358 try mod.enumValueFieldIndex(dest_ty, @as(u32, @intCast(field_index))), 27359 ); 27360 }, 27361 .Union => blk: { 27362 // union to its own tag type 27363 const union_tag_ty = inst_ty.unionTagType(mod) orelse break :blk; 27364 if (union_tag_ty.eql(dest_ty, mod)) { 27365 return sema.unionToTag(block, dest_ty, inst, inst_src); 27366 } 27367 }, 27368 .Undefined => { 27369 return sema.addConstUndef(dest_ty); 27370 }, 27371 else => {}, 27372 }, 27373 .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) { 27374 .ErrorUnion => eu: { 27375 if (maybe_inst_val) |inst_val| { 27376 switch (inst_val.toIntern()) { 27377 .undef => return sema.addConstUndef(dest_ty), 27378 else => switch (mod.intern_pool.indexToKey(inst_val.toIntern())) { 27379 .error_union => |error_union| switch (error_union.val) { 27380 .err_name => |err_name| { 27381 const error_set_ty = inst_ty.errorUnionSet(mod); 27382 const error_set_val = try sema.addConstant((try mod.intern(.{ .err = .{ 27383 .ty = error_set_ty.toIntern(), 27384 .name = err_name, 27385 } })).toValue()); 27386 return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src); 27387 }, 27388 .payload => |payload| { 27389 const payload_val = try sema.addConstant( 27390 payload.toValue(), 27391 ); 27392 return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) { 27393 error.NotCoercible => break :eu, 27394 else => |e| return e, 27395 }; 27396 }, 27397 }, 27398 else => unreachable, 27399 }, 27400 } 27401 } 27402 }, 27403 .ErrorSet => { 27404 // E to E!T 27405 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src); 27406 }, 27407 .Undefined => { 27408 return sema.addConstUndef(dest_ty); 27409 }, 27410 else => eu: { 27411 // T to E!T 27412 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) { 27413 error.NotCoercible => break :eu, 27414 else => |e| return e, 27415 }; 27416 }, 27417 }, 27418 .Union => switch (inst_ty.zigTypeTag(mod)) { 27419 .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), 27420 .Struct => { 27421 if (inst_ty.isAnonStruct(mod)) { 27422 return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src); 27423 } 27424 }, 27425 .Undefined => { 27426 return sema.addConstUndef(dest_ty); 27427 }, 27428 else => {}, 27429 }, 27430 .Array => switch (inst_ty.zigTypeTag(mod)) { 27431 .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 27432 .Struct => { 27433 if (inst == .empty_struct) { 27434 return sema.arrayInitEmpty(block, inst_src, dest_ty); 27435 } 27436 if (inst_ty.isTuple(mod)) { 27437 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 27438 } 27439 }, 27440 .Undefined => { 27441 return sema.addConstUndef(dest_ty); 27442 }, 27443 else => {}, 27444 }, 27445 .Vector => switch (inst_ty.zigTypeTag(mod)) { 27446 .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), 27447 .Struct => { 27448 if (inst_ty.isTuple(mod)) { 27449 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); 27450 } 27451 }, 27452 .Undefined => { 27453 return sema.addConstUndef(dest_ty); 27454 }, 27455 else => {}, 27456 }, 27457 .Struct => blk: { 27458 if (inst == .empty_struct) { 27459 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src); 27460 } 27461 if (inst_ty.isTupleOrAnonStruct(mod)) { 27462 return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) { 27463 error.NotCoercible => break :blk, 27464 else => |e| return e, 27465 }; 27466 } 27467 }, 27468 else => {}, 27469 } 27470 27471 // undefined to anything. We do this after the big switch above so that 27472 // special logic has a chance to run first, such as `*[N]T` to `[]T` which 27473 // should initialize the length field of the slice. 27474 if (is_undef) { 27475 return sema.addConstUndef(dest_ty); 27476 } 27477 27478 if (!opts.report_err) return error.NotCoercible; 27479 27480 if (opts.is_ret and dest_ty.zigTypeTag(mod) == .NoReturn) { 27481 const msg = msg: { 27482 const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{}); 27483 errdefer msg.destroy(sema.gpa); 27484 27485 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 27486 const src_decl = mod.declPtr(sema.func.?.owner_decl); 27487 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{}); 27488 break :msg msg; 27489 }; 27490 return sema.failWithOwnedErrorMsg(msg); 27491 } 27492 27493 const msg = msg: { 27494 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(mod), inst_ty.fmt(mod) }); 27495 errdefer msg.destroy(sema.gpa); 27496 27497 // E!T to T 27498 if (inst_ty.zigTypeTag(mod) == .ErrorUnion and 27499 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) 27500 { 27501 try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{}); 27502 try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); 27503 } 27504 27505 // ?T to T 27506 if (inst_ty.zigTypeTag(mod) == .Optional and 27507 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(mod), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) 27508 { 27509 try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{}); 27510 try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); 27511 } 27512 27513 try in_memory_result.report(sema, block, inst_src, msg); 27514 27515 // Add notes about function return type 27516 if (opts.is_ret and mod.test_functions.get(sema.func.?.owner_decl) == null) { 27517 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; 27518 const src_decl = mod.declPtr(sema.func.?.owner_decl); 27519 if (inst_ty.isError(mod) and !dest_ty.isError(mod)) { 27520 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{}); 27521 } else { 27522 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{}); 27523 } 27524 } 27525 27526 if (try opts.param_src.get(sema)) |param_src| { 27527 try mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{}); 27528 } 27529 27530 // TODO maybe add "cannot store an error in type '{}'" note 27531 27532 break :msg msg; 27533 }; 27534 return sema.failWithOwnedErrorMsg(msg); 27535 } 27536 27537 fn coerceInMemory( 27538 sema: *Sema, 27539 val: Value, 27540 dst_ty: Type, 27541 ) CompileError!Air.Inst.Ref { 27542 return sema.addConstant(try sema.mod.getCoerced(val, dst_ty)); 27543 } 27544 27545 const InMemoryCoercionResult = union(enum) { 27546 ok, 27547 no_match: Pair, 27548 int_not_coercible: Int, 27549 error_union_payload: PairAndChild, 27550 array_len: IntPair, 27551 array_sentinel: Sentinel, 27552 array_elem: PairAndChild, 27553 vector_len: IntPair, 27554 vector_elem: PairAndChild, 27555 optional_shape: Pair, 27556 optional_child: PairAndChild, 27557 from_anyerror, 27558 missing_error: []const InternPool.NullTerminatedString, 27559 /// true if wanted is var args 27560 fn_var_args: bool, 27561 /// true if wanted is generic 27562 fn_generic: bool, 27563 fn_param_count: IntPair, 27564 fn_param_noalias: IntPair, 27565 fn_param_comptime: ComptimeParam, 27566 fn_param: Param, 27567 fn_cc: CC, 27568 fn_return_type: PairAndChild, 27569 ptr_child: PairAndChild, 27570 ptr_addrspace: AddressSpace, 27571 ptr_sentinel: Sentinel, 27572 ptr_size: Size, 27573 ptr_qualifiers: Qualifiers, 27574 ptr_allowzero: Pair, 27575 ptr_bit_range: BitRange, 27576 ptr_alignment: IntPair, 27577 double_ptr_to_anyopaque: Pair, 27578 slice_to_anyopaque: Pair, 27579 27580 const Pair = struct { 27581 actual: Type, 27582 wanted: Type, 27583 }; 27584 27585 const PairAndChild = struct { 27586 child: *InMemoryCoercionResult, 27587 actual: Type, 27588 wanted: Type, 27589 }; 27590 27591 const Param = struct { 27592 child: *InMemoryCoercionResult, 27593 actual: Type, 27594 wanted: Type, 27595 index: u64, 27596 }; 27597 27598 const ComptimeParam = struct { 27599 index: u64, 27600 wanted: bool, 27601 }; 27602 27603 const Sentinel = struct { 27604 // unreachable_value indicates no sentinel 27605 actual: Value, 27606 wanted: Value, 27607 ty: Type, 27608 }; 27609 27610 const Int = struct { 27611 actual_signedness: std.builtin.Signedness, 27612 wanted_signedness: std.builtin.Signedness, 27613 actual_bits: u16, 27614 wanted_bits: u16, 27615 }; 27616 27617 const IntPair = struct { 27618 actual: u64, 27619 wanted: u64, 27620 }; 27621 27622 const Size = struct { 27623 actual: std.builtin.Type.Pointer.Size, 27624 wanted: std.builtin.Type.Pointer.Size, 27625 }; 27626 27627 const Qualifiers = struct { 27628 actual_const: bool, 27629 wanted_const: bool, 27630 actual_volatile: bool, 27631 wanted_volatile: bool, 27632 }; 27633 27634 const AddressSpace = struct { 27635 actual: std.builtin.AddressSpace, 27636 wanted: std.builtin.AddressSpace, 27637 }; 27638 27639 const CC = struct { 27640 actual: std.builtin.CallingConvention, 27641 wanted: std.builtin.CallingConvention, 27642 }; 27643 27644 const BitRange = struct { 27645 actual_host: u16, 27646 wanted_host: u16, 27647 actual_offset: u16, 27648 wanted_offset: u16, 27649 }; 27650 27651 fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult { 27652 const res = try arena.create(InMemoryCoercionResult); 27653 res.* = child.*; 27654 return res; 27655 } 27656 27657 fn report(res: *const InMemoryCoercionResult, sema: *Sema, block: *Block, src: LazySrcLoc, msg: *Module.ErrorMsg) !void { 27658 const mod = sema.mod; 27659 var cur = res; 27660 while (true) switch (cur.*) { 27661 .ok => unreachable, 27662 .no_match => |types| { 27663 try sema.addDeclaredHereNote(msg, types.wanted); 27664 try sema.addDeclaredHereNote(msg, types.actual); 27665 break; 27666 }, 27667 .int_not_coercible => |int| { 27668 try sema.errNote(block, src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{ 27669 @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits, 27670 }); 27671 break; 27672 }, 27673 .error_union_payload => |pair| { 27674 try sema.errNote(block, src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{ 27675 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27676 }); 27677 cur = pair.child; 27678 }, 27679 .array_len => |lens| { 27680 try sema.errNote(block, src, msg, "array of length {d} cannot cast into an array of length {d}", .{ 27681 lens.actual, lens.wanted, 27682 }); 27683 break; 27684 }, 27685 .array_sentinel => |sentinel| { 27686 if (sentinel.actual.toIntern() != .unreachable_value) { 27687 try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{ 27688 sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod), 27689 }); 27690 } else { 27691 try sema.errNote(block, src, msg, "destination array requires '{}' sentinel", .{ 27692 sentinel.wanted.fmtValue(sentinel.ty, mod), 27693 }); 27694 } 27695 break; 27696 }, 27697 .array_elem => |pair| { 27698 try sema.errNote(block, src, msg, "array element type '{}' cannot cast into array element type '{}'", .{ 27699 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27700 }); 27701 cur = pair.child; 27702 }, 27703 .vector_len => |lens| { 27704 try sema.errNote(block, src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{ 27705 lens.actual, lens.wanted, 27706 }); 27707 break; 27708 }, 27709 .vector_elem => |pair| { 27710 try sema.errNote(block, src, msg, "vector element type '{}' cannot cast into vector element type '{}'", .{ 27711 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27712 }); 27713 cur = pair.child; 27714 }, 27715 .optional_shape => |pair| { 27716 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ 27717 pair.actual.optionalChild(mod).fmt(mod), pair.wanted.optionalChild(mod).fmt(mod), 27718 }); 27719 break; 27720 }, 27721 .optional_child => |pair| { 27722 try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{ 27723 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27724 }); 27725 cur = pair.child; 27726 }, 27727 .from_anyerror => { 27728 try sema.errNote(block, src, msg, "global error set cannot cast into a smaller set", .{}); 27729 break; 27730 }, 27731 .missing_error => |missing_errors| { 27732 for (missing_errors) |err| { 27733 try sema.errNote(block, src, msg, "'error.{}' not a member of destination error set", .{err.fmt(&mod.intern_pool)}); 27734 } 27735 break; 27736 }, 27737 .fn_var_args => |wanted_var_args| { 27738 if (wanted_var_args) { 27739 try sema.errNote(block, src, msg, "non-variadic function cannot cast into a variadic function", .{}); 27740 } else { 27741 try sema.errNote(block, src, msg, "variadic function cannot cast into a non-variadic function", .{}); 27742 } 27743 break; 27744 }, 27745 .fn_generic => |wanted_generic| { 27746 if (wanted_generic) { 27747 try sema.errNote(block, src, msg, "non-generic function cannot cast into a generic function", .{}); 27748 } else { 27749 try sema.errNote(block, src, msg, "generic function cannot cast into a non-generic function", .{}); 27750 } 27751 break; 27752 }, 27753 .fn_param_count => |lens| { 27754 try sema.errNote(block, src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{ 27755 lens.actual, lens.wanted, 27756 }); 27757 break; 27758 }, 27759 .fn_param_noalias => |param| { 27760 var index: u6 = 0; 27761 var actual_noalias = false; 27762 while (true) : (index += 1) { 27763 const actual = @as(u1, @truncate(param.actual >> index)); 27764 const wanted = @as(u1, @truncate(param.wanted >> index)); 27765 if (actual != wanted) { 27766 actual_noalias = actual == 1; 27767 break; 27768 } 27769 } 27770 if (!actual_noalias) { 27771 try sema.errNote(block, src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index}); 27772 } else { 27773 try sema.errNote(block, src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index}); 27774 } 27775 break; 27776 }, 27777 .fn_param_comptime => |param| { 27778 if (param.wanted) { 27779 try sema.errNote(block, src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index}); 27780 } else { 27781 try sema.errNote(block, src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index}); 27782 } 27783 break; 27784 }, 27785 .fn_param => |param| { 27786 try sema.errNote(block, src, msg, "parameter {d} '{}' cannot cast into '{}'", .{ 27787 param.index, param.actual.fmt(mod), param.wanted.fmt(mod), 27788 }); 27789 cur = param.child; 27790 }, 27791 .fn_cc => |cc| { 27792 try sema.errNote(block, src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) }); 27793 break; 27794 }, 27795 .fn_return_type => |pair| { 27796 try sema.errNote(block, src, msg, "return type '{}' cannot cast into return type '{}'", .{ 27797 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27798 }); 27799 cur = pair.child; 27800 }, 27801 .ptr_child => |pair| { 27802 try sema.errNote(block, src, msg, "pointer type child '{}' cannot cast into pointer type child '{}'", .{ 27803 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27804 }); 27805 cur = pair.child; 27806 }, 27807 .ptr_addrspace => |@"addrspace"| { 27808 try sema.errNote(block, src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) }); 27809 break; 27810 }, 27811 .ptr_sentinel => |sentinel| { 27812 if (sentinel.actual.toIntern() != .unreachable_value) { 27813 try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{ 27814 sentinel.actual.fmtValue(sentinel.ty, mod), sentinel.wanted.fmtValue(sentinel.ty, mod), 27815 }); 27816 } else { 27817 try sema.errNote(block, src, msg, "destination pointer requires '{}' sentinel", .{ 27818 sentinel.wanted.fmtValue(sentinel.ty, mod), 27819 }); 27820 } 27821 break; 27822 }, 27823 .ptr_size => |size| { 27824 try sema.errNote(block, src, msg, "a {s} pointer cannot cast into a {s} pointer", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) }); 27825 break; 27826 }, 27827 .ptr_qualifiers => |qualifiers| { 27828 const ok_const = !qualifiers.actual_const or qualifiers.wanted_const; 27829 const ok_volatile = !qualifiers.actual_volatile or qualifiers.wanted_volatile; 27830 if (!ok_const) { 27831 try sema.errNote(block, src, msg, "cast discards const qualifier", .{}); 27832 } else if (!ok_volatile) { 27833 try sema.errNote(block, src, msg, "cast discards volatile qualifier", .{}); 27834 } 27835 break; 27836 }, 27837 .ptr_allowzero => |pair| { 27838 const wanted_allow_zero = pair.wanted.ptrAllowsZero(mod); 27839 const actual_allow_zero = pair.actual.ptrAllowsZero(mod); 27840 if (actual_allow_zero and !wanted_allow_zero) { 27841 try sema.errNote(block, src, msg, "'{}' could have null values which are illegal in type '{}'", .{ 27842 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27843 }); 27844 } else { 27845 try sema.errNote(block, src, msg, "mutable '{}' allows illegal null values stored to type '{}'", .{ 27846 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27847 }); 27848 } 27849 break; 27850 }, 27851 .ptr_bit_range => |bit_range| { 27852 if (bit_range.actual_host != bit_range.wanted_host) { 27853 try sema.errNote(block, src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{ 27854 bit_range.actual_host, bit_range.wanted_host, 27855 }); 27856 } 27857 if (bit_range.actual_offset != bit_range.wanted_offset) { 27858 try sema.errNote(block, src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{ 27859 bit_range.actual_offset, bit_range.wanted_offset, 27860 }); 27861 } 27862 break; 27863 }, 27864 .ptr_alignment => |pair| { 27865 try sema.errNote(block, src, msg, "pointer alignment '{}' cannot cast into pointer alignment '{}'", .{ 27866 pair.actual, pair.wanted, 27867 }); 27868 break; 27869 }, 27870 .double_ptr_to_anyopaque => |pair| { 27871 try sema.errNote(block, src, msg, "cannot implicitly cast double pointer '{}' to anyopaque pointer '{}'", .{ 27872 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27873 }); 27874 break; 27875 }, 27876 .slice_to_anyopaque => |pair| { 27877 try sema.errNote(block, src, msg, "cannot implicitly cast slice '{}' to anyopaque pointer '{}'", .{ 27878 pair.actual.fmt(mod), pair.wanted.fmt(mod), 27879 }); 27880 try sema.errNote(block, src, msg, "consider using '.ptr'", .{}); 27881 break; 27882 }, 27883 }; 27884 } 27885 }; 27886 27887 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 { 27888 return switch (size) { 27889 .One => "single", 27890 .Many => "many", 27891 .C => "C", 27892 .Slice => unreachable, 27893 }; 27894 } 27895 27896 /// If pointers have the same representation in runtime memory, a bitcast AIR instruction 27897 /// may be used for the coercion. 27898 /// * `const` attribute can be gained 27899 /// * `volatile` attribute can be gained 27900 /// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut 27901 /// * alignment can be decreased 27902 /// * bit offset attributes must match exactly 27903 /// * `*`/`[*]` must match exactly, but `[*c]` matches either one 27904 /// * sentinel-terminated pointers can coerce into `[*]` 27905 fn coerceInMemoryAllowed( 27906 sema: *Sema, 27907 block: *Block, 27908 dest_ty: Type, 27909 src_ty: Type, 27910 dest_is_mut: bool, 27911 target: std.Target, 27912 dest_src: LazySrcLoc, 27913 src_src: LazySrcLoc, 27914 ) CompileError!InMemoryCoercionResult { 27915 const mod = sema.mod; 27916 27917 if (dest_ty.eql(src_ty, mod)) 27918 return .ok; 27919 27920 const dest_tag = dest_ty.zigTypeTag(mod); 27921 const src_tag = src_ty.zigTypeTag(mod); 27922 27923 // Differently-named integers with the same number of bits. 27924 if (dest_tag == .Int and src_tag == .Int) { 27925 const dest_info = dest_ty.intInfo(mod); 27926 const src_info = src_ty.intInfo(mod); 27927 27928 if (dest_info.signedness == src_info.signedness and 27929 dest_info.bits == src_info.bits) 27930 { 27931 return .ok; 27932 } 27933 27934 if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or 27935 // small enough unsigned ints can get casted to large enough signed ints 27936 (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or 27937 (dest_info.signedness == .unsigned and src_info.signedness == .signed)) 27938 { 27939 return InMemoryCoercionResult{ .int_not_coercible = .{ 27940 .actual_signedness = src_info.signedness, 27941 .wanted_signedness = dest_info.signedness, 27942 .actual_bits = src_info.bits, 27943 .wanted_bits = dest_info.bits, 27944 } }; 27945 } 27946 } 27947 27948 // Differently-named floats with the same number of bits. 27949 if (dest_tag == .Float and src_tag == .Float) { 27950 const dest_bits = dest_ty.floatBits(target); 27951 const src_bits = src_ty.floatBits(target); 27952 if (dest_bits == src_bits) { 27953 return .ok; 27954 } 27955 } 27956 27957 // Pointers / Pointer-like Optionals 27958 const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty); 27959 const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty); 27960 if (maybe_dest_ptr_ty) |dest_ptr_ty| { 27961 if (maybe_src_ptr_ty) |src_ptr_ty| { 27962 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src); 27963 } 27964 } 27965 27966 // Slices 27967 if (dest_ty.isSlice(mod) and src_ty.isSlice(mod)) { 27968 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); 27969 } 27970 27971 // Functions 27972 if (dest_tag == .Fn and src_tag == .Fn) { 27973 return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, target, dest_src, src_src); 27974 } 27975 27976 // Error Unions 27977 if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) { 27978 const dest_payload = dest_ty.errorUnionPayload(mod); 27979 const src_payload = src_ty.errorUnionPayload(mod); 27980 const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src); 27981 if (child != .ok) { 27982 return InMemoryCoercionResult{ .error_union_payload = .{ 27983 .child = try child.dupe(sema.arena), 27984 .actual = src_payload, 27985 .wanted = dest_payload, 27986 } }; 27987 } 27988 return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(mod), src_ty.errorUnionSet(mod), dest_is_mut, target, dest_src, src_src); 27989 } 27990 27991 // Error Sets 27992 if (dest_tag == .ErrorSet and src_tag == .ErrorSet) { 27993 return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src); 27994 } 27995 27996 // Arrays 27997 if (dest_tag == .Array and src_tag == .Array) { 27998 const dest_info = dest_ty.arrayInfo(mod); 27999 const src_info = src_ty.arrayInfo(mod); 28000 if (dest_info.len != src_info.len) { 28001 return InMemoryCoercionResult{ .array_len = .{ 28002 .actual = src_info.len, 28003 .wanted = dest_info.len, 28004 } }; 28005 } 28006 28007 const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src); 28008 if (child != .ok) { 28009 return InMemoryCoercionResult{ .array_elem = .{ 28010 .child = try child.dupe(sema.arena), 28011 .actual = src_info.elem_type, 28012 .wanted = dest_info.elem_type, 28013 } }; 28014 } 28015 const ok_sent = dest_info.sentinel == null or 28016 (src_info.sentinel != null and 28017 dest_info.sentinel.?.eql( 28018 try mod.getCoerced(src_info.sentinel.?, dest_info.elem_type), 28019 dest_info.elem_type, 28020 mod, 28021 )); 28022 if (!ok_sent) { 28023 return InMemoryCoercionResult{ .array_sentinel = .{ 28024 .actual = src_info.sentinel orelse Value.@"unreachable", 28025 .wanted = dest_info.sentinel orelse Value.@"unreachable", 28026 .ty = dest_info.elem_type, 28027 } }; 28028 } 28029 return .ok; 28030 } 28031 28032 // Vectors 28033 if (dest_tag == .Vector and src_tag == .Vector) { 28034 const dest_len = dest_ty.vectorLen(mod); 28035 const src_len = src_ty.vectorLen(mod); 28036 if (dest_len != src_len) { 28037 return InMemoryCoercionResult{ .vector_len = .{ 28038 .actual = src_len, 28039 .wanted = dest_len, 28040 } }; 28041 } 28042 28043 const dest_elem_ty = dest_ty.scalarType(mod); 28044 const src_elem_ty = src_ty.scalarType(mod); 28045 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src); 28046 if (child != .ok) { 28047 return InMemoryCoercionResult{ .vector_elem = .{ 28048 .child = try child.dupe(sema.arena), 28049 .actual = src_elem_ty, 28050 .wanted = dest_elem_ty, 28051 } }; 28052 } 28053 28054 return .ok; 28055 } 28056 28057 // Optionals 28058 if (dest_tag == .Optional and src_tag == .Optional) { 28059 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) { 28060 return InMemoryCoercionResult{ .optional_shape = .{ 28061 .actual = src_ty, 28062 .wanted = dest_ty, 28063 } }; 28064 } 28065 const dest_child_type = dest_ty.optionalChild(mod); 28066 const src_child_type = src_ty.optionalChild(mod); 28067 28068 const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src); 28069 if (child != .ok) { 28070 return InMemoryCoercionResult{ .optional_child = .{ 28071 .child = try child.dupe(sema.arena), 28072 .actual = src_child_type, 28073 .wanted = dest_child_type, 28074 } }; 28075 } 28076 28077 return .ok; 28078 } 28079 28080 // Tuples (with in-memory-coercible fields) 28081 if (dest_ty.isTuple(mod) and src_ty.isTuple(mod)) tuple: { 28082 if (dest_ty.containerLayout(mod) != src_ty.containerLayout(mod)) break :tuple; 28083 if (dest_ty.structFieldCount(mod) != src_ty.structFieldCount(mod)) break :tuple; 28084 const field_count = dest_ty.structFieldCount(mod); 28085 for (0..field_count) |field_idx| { 28086 if (dest_ty.structFieldIsComptime(field_idx, mod) != src_ty.structFieldIsComptime(field_idx, mod)) break :tuple; 28087 if (dest_ty.structFieldAlign(field_idx, mod) != src_ty.structFieldAlign(field_idx, mod)) break :tuple; 28088 const dest_field_ty = dest_ty.structFieldType(field_idx, mod); 28089 const src_field_ty = src_ty.structFieldType(field_idx, mod); 28090 const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src); 28091 if (field != .ok) break :tuple; 28092 } 28093 return .ok; 28094 } 28095 28096 return InMemoryCoercionResult{ .no_match = .{ 28097 .actual = dest_ty, 28098 .wanted = src_ty, 28099 } }; 28100 } 28101 28102 fn coerceInMemoryAllowedErrorSets( 28103 sema: *Sema, 28104 block: *Block, 28105 dest_ty: Type, 28106 src_ty: Type, 28107 dest_src: LazySrcLoc, 28108 src_src: LazySrcLoc, 28109 ) !InMemoryCoercionResult { 28110 const mod = sema.mod; 28111 const gpa = sema.gpa; 28112 const ip = &mod.intern_pool; 28113 28114 // Coercion to `anyerror`. Note that this check can return false negatives 28115 // in case the error sets did not get resolved. 28116 if (dest_ty.isAnyError(mod)) { 28117 return .ok; 28118 } 28119 28120 if (mod.typeToInferredErrorSetIndex(dest_ty).unwrap()) |dst_ies_index| { 28121 const dst_ies = mod.inferredErrorSetPtr(dst_ies_index); 28122 // We will make an effort to return `ok` without resolving either error set, to 28123 // avoid unnecessary "unable to resolve error set" dependency loop errors. 28124 switch (src_ty.toIntern()) { 28125 .anyerror_type => {}, 28126 else => switch (ip.indexToKey(src_ty.toIntern())) { 28127 .inferred_error_set_type => |src_index| { 28128 // If both are inferred error sets of functions, and 28129 // the dest includes the source function, the coercion is OK. 28130 // This check is important because it works without forcing a full resolution 28131 // of inferred error sets. 28132 if (dst_ies.inferred_error_sets.contains(src_index)) { 28133 return .ok; 28134 } 28135 }, 28136 .error_set_type => |error_set_type| { 28137 for (error_set_type.names) |name| { 28138 if (!dst_ies.errors.contains(name)) break; 28139 } else return .ok; 28140 }, 28141 else => unreachable, 28142 }, 28143 } 28144 28145 if (dst_ies.func == sema.owner_func_index.unwrap()) { 28146 // We are trying to coerce an error set to the current function's 28147 // inferred error set. 28148 try dst_ies.addErrorSet(src_ty, ip, gpa); 28149 return .ok; 28150 } 28151 28152 try sema.resolveInferredErrorSet(block, dest_src, dst_ies_index); 28153 // isAnyError might have changed from a false negative to a true positive after resolution. 28154 if (dest_ty.isAnyError(mod)) { 28155 return .ok; 28156 } 28157 } 28158 28159 var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa); 28160 defer missing_error_buf.deinit(); 28161 28162 switch (src_ty.toIntern()) { 28163 .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) { 28164 .simple_type => unreachable, // filtered out above 28165 .error_set_type, .inferred_error_set_type => return .from_anyerror, 28166 else => unreachable, 28167 }, 28168 28169 else => switch (ip.indexToKey(src_ty.toIntern())) { 28170 .inferred_error_set_type => |src_index| { 28171 const src_data = mod.inferredErrorSetPtr(src_index); 28172 28173 try sema.resolveInferredErrorSet(block, src_src, src_index); 28174 // src anyerror status might have changed after the resolution. 28175 if (src_ty.isAnyError(mod)) { 28176 // dest_ty.isAnyError(mod) == true is already checked for at this point. 28177 return .from_anyerror; 28178 } 28179 28180 for (src_data.errors.keys()) |key| { 28181 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) { 28182 try missing_error_buf.append(key); 28183 } 28184 } 28185 28186 if (missing_error_buf.items.len != 0) { 28187 return InMemoryCoercionResult{ 28188 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 28189 }; 28190 } 28191 28192 return .ok; 28193 }, 28194 .error_set_type => |error_set_type| { 28195 for (error_set_type.names) |name| { 28196 if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) { 28197 try missing_error_buf.append(name); 28198 } 28199 } 28200 28201 if (missing_error_buf.items.len != 0) { 28202 return InMemoryCoercionResult{ 28203 .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items), 28204 }; 28205 } 28206 28207 return .ok; 28208 }, 28209 else => unreachable, 28210 }, 28211 } 28212 } 28213 28214 fn coerceInMemoryAllowedFns( 28215 sema: *Sema, 28216 block: *Block, 28217 dest_ty: Type, 28218 src_ty: Type, 28219 target: std.Target, 28220 dest_src: LazySrcLoc, 28221 src_src: LazySrcLoc, 28222 ) !InMemoryCoercionResult { 28223 const mod = sema.mod; 28224 28225 { 28226 const dest_info = mod.typeToFunc(dest_ty).?; 28227 const src_info = mod.typeToFunc(src_ty).?; 28228 28229 if (dest_info.is_var_args != src_info.is_var_args) { 28230 return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; 28231 } 28232 28233 if (dest_info.is_generic != src_info.is_generic) { 28234 return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; 28235 } 28236 28237 if (dest_info.cc != src_info.cc) { 28238 return InMemoryCoercionResult{ .fn_cc = .{ 28239 .actual = src_info.cc, 28240 .wanted = dest_info.cc, 28241 } }; 28242 } 28243 28244 switch (src_info.return_type) { 28245 .noreturn_type, .generic_poison_type => {}, 28246 else => { 28247 const dest_return_type = dest_info.return_type.toType(); 28248 const src_return_type = src_info.return_type.toType(); 28249 const rt = try sema.coerceInMemoryAllowed(block, dest_return_type, src_return_type, false, target, dest_src, src_src); 28250 if (rt != .ok) { 28251 return InMemoryCoercionResult{ .fn_return_type = .{ 28252 .child = try rt.dupe(sema.arena), 28253 .actual = src_return_type, 28254 .wanted = dest_return_type, 28255 } }; 28256 } 28257 }, 28258 } 28259 } 28260 28261 const params_len = params_len: { 28262 const dest_info = mod.typeToFunc(dest_ty).?; 28263 const src_info = mod.typeToFunc(src_ty).?; 28264 28265 if (dest_info.param_types.len != src_info.param_types.len) { 28266 return InMemoryCoercionResult{ .fn_param_count = .{ 28267 .actual = src_info.param_types.len, 28268 .wanted = dest_info.param_types.len, 28269 } }; 28270 } 28271 28272 if (dest_info.noalias_bits != src_info.noalias_bits) { 28273 return InMemoryCoercionResult{ .fn_param_noalias = .{ 28274 .actual = src_info.noalias_bits, 28275 .wanted = dest_info.noalias_bits, 28276 } }; 28277 } 28278 28279 break :params_len dest_info.param_types.len; 28280 }; 28281 28282 for (0..params_len) |param_i| { 28283 const dest_info = mod.typeToFunc(dest_ty).?; 28284 const src_info = mod.typeToFunc(src_ty).?; 28285 28286 const dest_param_ty = dest_info.param_types[param_i].toType(); 28287 const src_param_ty = src_info.param_types[param_i].toType(); 28288 28289 const param_i_small = @as(u5, @intCast(param_i)); 28290 if (dest_info.paramIsComptime(param_i_small) != src_info.paramIsComptime(param_i_small)) { 28291 return InMemoryCoercionResult{ .fn_param_comptime = .{ 28292 .index = param_i, 28293 .wanted = dest_info.paramIsComptime(param_i_small), 28294 } }; 28295 } 28296 28297 switch (src_param_ty.toIntern()) { 28298 .generic_poison_type => {}, 28299 else => { 28300 // Note: Cast direction is reversed here. 28301 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src); 28302 if (param != .ok) { 28303 return InMemoryCoercionResult{ .fn_param = .{ 28304 .child = try param.dupe(sema.arena), 28305 .actual = src_param_ty, 28306 .wanted = dest_param_ty, 28307 .index = param_i, 28308 } }; 28309 } 28310 }, 28311 } 28312 } 28313 28314 return .ok; 28315 } 28316 28317 fn coerceInMemoryAllowedPtrs( 28318 sema: *Sema, 28319 block: *Block, 28320 dest_ty: Type, 28321 src_ty: Type, 28322 dest_ptr_ty: Type, 28323 src_ptr_ty: Type, 28324 dest_is_mut: bool, 28325 target: std.Target, 28326 dest_src: LazySrcLoc, 28327 src_src: LazySrcLoc, 28328 ) !InMemoryCoercionResult { 28329 const mod = sema.mod; 28330 const dest_info = dest_ptr_ty.ptrInfo(mod); 28331 const src_info = src_ptr_ty.ptrInfo(mod); 28332 28333 const ok_ptr_size = src_info.flags.size == dest_info.flags.size or 28334 src_info.flags.size == .C or dest_info.flags.size == .C; 28335 if (!ok_ptr_size) { 28336 return InMemoryCoercionResult{ .ptr_size = .{ 28337 .actual = src_info.flags.size, 28338 .wanted = dest_info.flags.size, 28339 } }; 28340 } 28341 28342 const ok_cv_qualifiers = 28343 (!src_info.flags.is_const or dest_info.flags.is_const) and 28344 (!src_info.flags.is_volatile or dest_info.flags.is_volatile); 28345 28346 if (!ok_cv_qualifiers) { 28347 return InMemoryCoercionResult{ .ptr_qualifiers = .{ 28348 .actual_const = src_info.flags.is_const, 28349 .wanted_const = dest_info.flags.is_const, 28350 .actual_volatile = src_info.flags.is_volatile, 28351 .wanted_volatile = dest_info.flags.is_volatile, 28352 } }; 28353 } 28354 28355 if (dest_info.flags.address_space != src_info.flags.address_space) { 28356 return InMemoryCoercionResult{ .ptr_addrspace = .{ 28357 .actual = src_info.flags.address_space, 28358 .wanted = dest_info.flags.address_space, 28359 } }; 28360 } 28361 28362 const child = try sema.coerceInMemoryAllowed(block, dest_info.child.toType(), src_info.child.toType(), !dest_info.flags.is_const, target, dest_src, src_src); 28363 if (child != .ok) { 28364 return InMemoryCoercionResult{ .ptr_child = .{ 28365 .child = try child.dupe(sema.arena), 28366 .actual = src_info.child.toType(), 28367 .wanted = dest_info.child.toType(), 28368 } }; 28369 } 28370 28371 const dest_allow_zero = dest_ty.ptrAllowsZero(mod); 28372 const src_allow_zero = src_ty.ptrAllowsZero(mod); 28373 28374 const ok_allows_zero = (dest_allow_zero and 28375 (src_allow_zero or !dest_is_mut)) or 28376 (!dest_allow_zero and !src_allow_zero); 28377 if (!ok_allows_zero) { 28378 return InMemoryCoercionResult{ .ptr_allowzero = .{ 28379 .actual = src_ty, 28380 .wanted = dest_ty, 28381 } }; 28382 } 28383 28384 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or 28385 src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) 28386 { 28387 return InMemoryCoercionResult{ .ptr_bit_range = .{ 28388 .actual_host = src_info.packed_offset.host_size, 28389 .wanted_host = dest_info.packed_offset.host_size, 28390 .actual_offset = src_info.packed_offset.bit_offset, 28391 .wanted_offset = dest_info.packed_offset.bit_offset, 28392 } }; 28393 } 28394 28395 const ok_sent = dest_info.sentinel == .none or src_info.flags.size == .C or 28396 (src_info.sentinel != .none and 28397 dest_info.sentinel == try mod.intern_pool.getCoerced(sema.gpa, src_info.sentinel, dest_info.child)); 28398 if (!ok_sent) { 28399 return InMemoryCoercionResult{ .ptr_sentinel = .{ 28400 .actual = switch (src_info.sentinel) { 28401 .none => Value.@"unreachable", 28402 else => src_info.sentinel.toValue(), 28403 }, 28404 .wanted = switch (dest_info.sentinel) { 28405 .none => Value.@"unreachable", 28406 else => dest_info.sentinel.toValue(), 28407 }, 28408 .ty = dest_info.child.toType(), 28409 } }; 28410 } 28411 28412 // If both pointers have alignment 0, it means they both want ABI alignment. 28413 // In this case, if they share the same child type, no need to resolve 28414 // pointee type alignment. Otherwise both pointee types must have their alignment 28415 // resolved and we compare the alignment numerically. 28416 if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or 28417 dest_info.child != src_info.child) 28418 { 28419 const src_align = src_info.flags.alignment.toByteUnitsOptional() orelse 28420 src_info.child.toType().abiAlignment(mod); 28421 28422 const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse 28423 dest_info.child.toType().abiAlignment(mod); 28424 28425 if (dest_align > src_align) { 28426 return InMemoryCoercionResult{ .ptr_alignment = .{ 28427 .actual = src_align, 28428 .wanted = dest_align, 28429 } }; 28430 } 28431 } 28432 28433 return .ok; 28434 } 28435 28436 fn coerceVarArgParam( 28437 sema: *Sema, 28438 block: *Block, 28439 inst: Air.Inst.Ref, 28440 inst_src: LazySrcLoc, 28441 ) !Air.Inst.Ref { 28442 if (block.is_typeof) return inst; 28443 28444 const mod = sema.mod; 28445 const uncasted_ty = sema.typeOf(inst); 28446 const coerced = switch (uncasted_ty.zigTypeTag(mod)) { 28447 // TODO consider casting to c_int/f64 if they fit 28448 .ComptimeInt, .ComptimeFloat => return sema.fail( 28449 block, 28450 inst_src, 28451 "integer and float literals passed to variadic function must be casted to a fixed-size number type", 28452 .{}, 28453 ), 28454 .Fn => blk: { 28455 const fn_val = try sema.resolveConstValue(block, .unneeded, inst, ""); 28456 const fn_decl = fn_val.pointerDecl(mod).?; 28457 break :blk try sema.analyzeDeclRef(fn_decl); 28458 }, 28459 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), 28460 .Float => float: { 28461 const target = sema.mod.getTarget(); 28462 const double_bits = target.c_type_bit_size(.double); 28463 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget()); 28464 if (inst_bits >= double_bits) break :float inst; 28465 switch (double_bits) { 28466 32 => break :float try sema.coerce(block, Type.f32, inst, inst_src), 28467 64 => break :float try sema.coerce(block, Type.f64, inst, inst_src), 28468 else => unreachable, 28469 } 28470 }, 28471 else => inst, 28472 }; 28473 28474 const coerced_ty = sema.typeOf(coerced); 28475 if (!try sema.validateExternType(coerced_ty, .param_ty)) { 28476 const msg = msg: { 28477 const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)}); 28478 errdefer msg.destroy(sema.gpa); 28479 28480 const src_decl = sema.mod.declPtr(block.src_decl); 28481 try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty); 28482 28483 try sema.addDeclaredHereNote(msg, coerced_ty); 28484 break :msg msg; 28485 }; 28486 return sema.failWithOwnedErrorMsg(msg); 28487 } 28488 return coerced; 28489 } 28490 28491 // TODO migrate callsites to use storePtr2 instead. 28492 fn storePtr( 28493 sema: *Sema, 28494 block: *Block, 28495 src: LazySrcLoc, 28496 ptr: Air.Inst.Ref, 28497 uncasted_operand: Air.Inst.Ref, 28498 ) CompileError!void { 28499 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store; 28500 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag); 28501 } 28502 28503 fn storePtr2( 28504 sema: *Sema, 28505 block: *Block, 28506 src: LazySrcLoc, 28507 ptr: Air.Inst.Ref, 28508 ptr_src: LazySrcLoc, 28509 uncasted_operand: Air.Inst.Ref, 28510 operand_src: LazySrcLoc, 28511 air_tag: Air.Inst.Tag, 28512 ) CompileError!void { 28513 const mod = sema.mod; 28514 const ptr_ty = sema.typeOf(ptr); 28515 if (ptr_ty.isConstPtr(mod)) 28516 return sema.fail(block, ptr_src, "cannot assign to constant", .{}); 28517 28518 const elem_ty = ptr_ty.childType(mod); 28519 28520 // To generate better code for tuples, we detect a tuple operand here, and 28521 // analyze field loads and stores directly. This avoids an extra allocation + memcpy 28522 // which would occur if we used `coerce`. 28523 // However, we avoid this mechanism if the destination element type is a tuple, 28524 // because the regular store will be better for this case. 28525 // If the destination type is a struct we don't want this mechanism to trigger, because 28526 // this code does not handle tuple-to-struct coercion which requires dealing with missing 28527 // fields. 28528 const operand_ty = sema.typeOf(uncasted_operand); 28529 if (operand_ty.isTuple(mod) and elem_ty.zigTypeTag(mod) == .Array) { 28530 const field_count = operand_ty.structFieldCount(mod); 28531 var i: u32 = 0; 28532 while (i < field_count) : (i += 1) { 28533 const elem_src = operand_src; // TODO better source location 28534 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); 28535 const elem_index = try sema.addIntUnsigned(Type.usize, i); 28536 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true); 28537 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); 28538 } 28539 return; 28540 } 28541 28542 // TODO do the same thing for anon structs as for tuples above. 28543 // However, beware of the need to handle missing/extra fields. 28544 28545 const is_ret = air_tag == .ret_ptr; 28546 28547 // Detect if we are storing an array operand to a bitcasted vector pointer. 28548 // If so, we instead reach through the bitcasted pointer to the vector pointer, 28549 // bitcast the array operand to a vector, and then lower this as a store of 28550 // a vector value to a vector pointer. This generally results in better code, 28551 // as well as working around an LLVM bug: 28552 // https://github.com/ziglang/zig/issues/11154 28553 if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { 28554 const vector_ty = sema.typeOf(vector_ptr).childType(mod); 28555 const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 28556 error.NotCoercible => unreachable, 28557 else => |e| return e, 28558 }; 28559 try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store); 28560 return; 28561 } 28562 28563 const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { 28564 error.NotCoercible => unreachable, 28565 else => |e| return e, 28566 }; 28567 const maybe_operand_val = try sema.resolveMaybeUndefVal(operand); 28568 28569 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { 28570 const operand_val = maybe_operand_val orelse { 28571 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); 28572 break :rs operand_src; 28573 }; 28574 if (ptr_val.isComptimeMutablePtr(mod)) { 28575 try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); 28576 return; 28577 } else break :rs ptr_src; 28578 } else ptr_src; 28579 28580 // We do this after the possible comptime store above, for the case of field_ptr stores 28581 // to unions because we want the comptime tag to be set, even if the field type is void. 28582 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) 28583 return; 28584 28585 if (air_tag == .bitcast) { 28586 // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr` 28587 // to avoid calling `requireRuntimeBlock` for the dummy block. 28588 _ = try block.addBinOp(.store, ptr, operand); 28589 return; 28590 } 28591 28592 try sema.requireRuntimeBlock(block, src, runtime_src); 28593 try sema.queueFullTypeResolution(elem_ty); 28594 28595 if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) { 28596 const ptr_inst = Air.refToIndex(ptr).?; 28597 const air_tags = sema.air_instructions.items(.tag); 28598 if (air_tags[ptr_inst] == .ptr_elem_ptr) { 28599 const ty_pl = sema.air_instructions.items(.data)[ptr_inst].ty_pl; 28600 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 28601 _ = try block.addInst(.{ 28602 .tag = .vector_store_elem, 28603 .data = .{ .vector_store_elem = .{ 28604 .vector_ptr = bin_op.lhs, 28605 .payload = try block.sema.addExtra(Air.Bin{ 28606 .lhs = bin_op.rhs, 28607 .rhs = operand, 28608 }), 28609 } }, 28610 }); 28611 return; 28612 } 28613 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{ 28614 ptr_ty.fmt(sema.mod), 28615 }); 28616 } 28617 28618 if (is_ret) { 28619 _ = try block.addBinOp(.store, ptr, operand); 28620 } else { 28621 _ = try block.addBinOp(air_tag, ptr, operand); 28622 } 28623 } 28624 28625 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector 28626 /// pointer. Only if the final element type matches the vector element type, and the 28627 /// lengths match. 28628 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { 28629 const mod = sema.mod; 28630 const array_ty = sema.typeOf(ptr).childType(mod); 28631 if (array_ty.zigTypeTag(mod) != .Array) return null; 28632 var ptr_ref = ptr; 28633 var ptr_inst = Air.refToIndex(ptr_ref) orelse return null; 28634 const air_datas = sema.air_instructions.items(.data); 28635 const air_tags = sema.air_instructions.items(.tag); 28636 const vector_ty = while (air_tags[ptr_inst] == .bitcast) { 28637 ptr_ref = air_datas[ptr_inst].ty_op.operand; 28638 if (!sema.isKnownZigType(ptr_ref, .Pointer)) return null; 28639 const child_ty = sema.typeOf(ptr_ref).childType(mod); 28640 if (child_ty.zigTypeTag(mod) == .Vector) break child_ty; 28641 ptr_inst = Air.refToIndex(ptr_ref) orelse return null; 28642 } else return null; 28643 28644 // We have a pointer-to-array and a pointer-to-vector. If the elements and 28645 // lengths match, return the result. 28646 if (array_ty.childType(mod).eql(vector_ty.childType(mod), sema.mod) and 28647 array_ty.arrayLen(mod) == vector_ty.vectorLen(mod)) 28648 { 28649 return ptr_ref; 28650 } else { 28651 return null; 28652 } 28653 } 28654 28655 /// Call when you have Value objects rather than Air instructions, and you want to 28656 /// assert the store must be done at comptime. 28657 fn storePtrVal( 28658 sema: *Sema, 28659 block: *Block, 28660 src: LazySrcLoc, 28661 ptr_val: Value, 28662 operand_val: Value, 28663 operand_ty: Type, 28664 ) !void { 28665 const mod = sema.mod; 28666 var mut_kit = try sema.beginComptimePtrMutation(block, src, ptr_val, operand_ty); 28667 try sema.checkComptimeVarStore(block, src, mut_kit.mut_decl); 28668 28669 switch (mut_kit.pointee) { 28670 .direct => |val_ptr| { 28671 if (mut_kit.mut_decl.runtime_index == .comptime_field_ptr) { 28672 if (!operand_val.eql(val_ptr.*, operand_ty, mod)) { 28673 // TODO use failWithInvalidComptimeFieldStore 28674 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); 28675 } 28676 return; 28677 } 28678 val_ptr.* = (try operand_val.intern(operand_ty, mod)).toValue(); 28679 }, 28680 .reinterpret => |reinterpret| { 28681 const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod)); 28682 const buffer = try sema.gpa.alloc(u8, abi_size); 28683 defer sema.gpa.free(buffer); 28684 reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) { 28685 error.OutOfMemory => return error.OutOfMemory, 28686 error.ReinterpretDeclRef => unreachable, 28687 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 28688 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), 28689 }; 28690 operand_val.writeToMemory(operand_ty, mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) { 28691 error.OutOfMemory => return error.OutOfMemory, 28692 error.ReinterpretDeclRef => unreachable, 28693 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 28694 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), 28695 }; 28696 28697 reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue(); 28698 }, 28699 .bad_decl_ty, .bad_ptr_ty => { 28700 // TODO show the decl declaration site in a note and explain whether the decl 28701 // or the pointer is the problematic type 28702 return sema.fail( 28703 block, 28704 src, 28705 "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout", 28706 .{mut_kit.ty.fmt(mod)}, 28707 ); 28708 }, 28709 } 28710 } 28711 28712 const ComptimePtrMutationKit = struct { 28713 mut_decl: InternPool.Key.Ptr.Addr.MutDecl, 28714 pointee: union(enum) { 28715 /// The pointer type matches the actual comptime Value so a direct 28716 /// modification is possible. 28717 direct: *Value, 28718 /// The largest parent Value containing pointee and having a well-defined memory layout. 28719 /// This is used for bitcasting, if direct dereferencing failed. 28720 reinterpret: struct { 28721 val_ptr: *Value, 28722 byte_offset: usize, 28723 }, 28724 /// If the root decl could not be used as parent, this means `ty` is the type that 28725 /// caused that by not having a well-defined layout. 28726 /// This one means the Decl that owns the value trying to be modified does not 28727 /// have a well defined memory layout. 28728 bad_decl_ty, 28729 /// If the root decl could not be used as parent, this means `ty` is the type that 28730 /// caused that by not having a well-defined layout. 28731 /// This one means the pointer type that is being stored through does not 28732 /// have a well defined memory layout. 28733 bad_ptr_ty, 28734 }, 28735 ty: Type, 28736 }; 28737 28738 fn beginComptimePtrMutation( 28739 sema: *Sema, 28740 block: *Block, 28741 src: LazySrcLoc, 28742 ptr_val: Value, 28743 ptr_elem_ty: Type, 28744 ) CompileError!ComptimePtrMutationKit { 28745 const mod = sema.mod; 28746 const ptr = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr; 28747 switch (ptr.addr) { 28748 .decl, .int => unreachable, // isComptimeMutablePtr has been checked already 28749 .mut_decl => |mut_decl| { 28750 const decl = mod.declPtr(mut_decl.decl); 28751 return sema.beginComptimePtrMutationInner(block, src, decl.ty, &decl.val, ptr_elem_ty, mut_decl); 28752 }, 28753 .comptime_field => |comptime_field| { 28754 const duped = try sema.arena.create(Value); 28755 duped.* = comptime_field.toValue(); 28756 return sema.beginComptimePtrMutationInner(block, src, mod.intern_pool.typeOf(comptime_field).toType(), duped, ptr_elem_ty, .{ 28757 .decl = undefined, 28758 .runtime_index = .comptime_field_ptr, 28759 }); 28760 }, 28761 .eu_payload => |eu_ptr| { 28762 const eu_ty = mod.intern_pool.typeOf(eu_ptr).toType().childType(mod); 28763 var parent = try sema.beginComptimePtrMutation(block, src, eu_ptr.toValue(), eu_ty); 28764 switch (parent.pointee) { 28765 .direct => |val_ptr| { 28766 const payload_ty = parent.ty.errorUnionPayload(mod); 28767 if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) { 28768 return ComptimePtrMutationKit{ 28769 .mut_decl = parent.mut_decl, 28770 .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data }, 28771 .ty = payload_ty, 28772 }; 28773 } else { 28774 // An error union has been initialized to undefined at comptime and now we 28775 // are for the first time setting the payload. We must change the 28776 // representation of the error union from `undef` to `opt_payload`. 28777 28778 const payload = try sema.arena.create(Value.Payload.SubValue); 28779 payload.* = .{ 28780 .base = .{ .tag = .eu_payload }, 28781 .data = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(), 28782 }; 28783 28784 val_ptr.* = Value.initPayload(&payload.base); 28785 28786 return ComptimePtrMutationKit{ 28787 .mut_decl = parent.mut_decl, 28788 .pointee = .{ .direct = &payload.data }, 28789 .ty = payload_ty, 28790 }; 28791 } 28792 }, 28793 .bad_decl_ty, .bad_ptr_ty => return parent, 28794 // Even though the parent value type has well-defined memory layout, our 28795 // pointer type does not. 28796 .reinterpret => return ComptimePtrMutationKit{ 28797 .mut_decl = parent.mut_decl, 28798 .pointee = .bad_ptr_ty, 28799 .ty = eu_ty, 28800 }, 28801 } 28802 }, 28803 .opt_payload => |opt_ptr| { 28804 const opt_ty = mod.intern_pool.typeOf(opt_ptr).toType().childType(mod); 28805 var parent = try sema.beginComptimePtrMutation(block, src, opt_ptr.toValue(), opt_ty); 28806 switch (parent.pointee) { 28807 .direct => |val_ptr| { 28808 const payload_ty = parent.ty.optionalChild(mod); 28809 switch (val_ptr.ip_index) { 28810 .none => return ComptimePtrMutationKit{ 28811 .mut_decl = parent.mut_decl, 28812 .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data }, 28813 .ty = payload_ty, 28814 }, 28815 else => { 28816 const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) { 28817 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }), 28818 .opt => |opt| switch (opt.val) { 28819 .none => try mod.intern(.{ .undef = payload_ty.toIntern() }), 28820 else => |payload| payload, 28821 }, 28822 else => unreachable, 28823 }; 28824 28825 // An optional has been initialized to undefined at comptime and now we 28826 // are for the first time setting the payload. We must change the 28827 // representation of the optional from `undef` to `opt_payload`. 28828 28829 const payload = try sema.arena.create(Value.Payload.SubValue); 28830 payload.* = .{ 28831 .base = .{ .tag = .opt_payload }, 28832 .data = payload_val.toValue(), 28833 }; 28834 28835 val_ptr.* = Value.initPayload(&payload.base); 28836 28837 return ComptimePtrMutationKit{ 28838 .mut_decl = parent.mut_decl, 28839 .pointee = .{ .direct = &payload.data }, 28840 .ty = payload_ty, 28841 }; 28842 }, 28843 } 28844 }, 28845 .bad_decl_ty, .bad_ptr_ty => return parent, 28846 // Even though the parent value type has well-defined memory layout, our 28847 // pointer type does not. 28848 .reinterpret => return ComptimePtrMutationKit{ 28849 .mut_decl = parent.mut_decl, 28850 .pointee = .bad_ptr_ty, 28851 .ty = opt_ty, 28852 }, 28853 } 28854 }, 28855 .elem => |elem_ptr| { 28856 const base_elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod); 28857 var parent = try sema.beginComptimePtrMutation(block, src, elem_ptr.base.toValue(), base_elem_ty); 28858 28859 switch (parent.pointee) { 28860 .direct => |val_ptr| switch (parent.ty.zigTypeTag(mod)) { 28861 .Array, .Vector => { 28862 const check_len = parent.ty.arrayLenIncludingSentinel(mod); 28863 if (elem_ptr.index >= check_len) { 28864 // TODO have the parent include the decl so we can say "declared here" 28865 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{ 28866 elem_ptr.index, check_len, 28867 }); 28868 } 28869 const elem_ty = parent.ty.childType(mod); 28870 28871 // We might have a pointer to multiple elements of the array (e.g. a pointer 28872 // to a sub-array). In this case, we just have to reinterpret the relevant 28873 // bytes of the whole array rather than any single element. 28874 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty); 28875 if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) { 28876 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); 28877 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 28878 return .{ 28879 .mut_decl = parent.mut_decl, 28880 .pointee = .{ .reinterpret = .{ 28881 .val_ptr = val_ptr, 28882 .byte_offset = elem_abi_size * elem_idx, 28883 } }, 28884 .ty = parent.ty, 28885 }; 28886 } 28887 28888 switch (val_ptr.ip_index) { 28889 .none => switch (val_ptr.tag()) { 28890 .bytes => { 28891 // An array is memory-optimized to store a slice of bytes, but we are about 28892 // to modify an individual field and the representation has to change. 28893 // If we wanted to avoid this, there would need to be special detection 28894 // elsewhere to identify when writing a value to an array element that is stored 28895 // using the `bytes` tag, and handle it without making a call to this function. 28896 const arena = mod.tmp_hack_arena.allocator(); 28897 28898 const bytes = val_ptr.castTag(.bytes).?.data; 28899 const dest_len = parent.ty.arrayLenIncludingSentinel(mod); 28900 // bytes.len may be one greater than dest_len because of the case when 28901 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. 28902 assert(bytes.len >= dest_len); 28903 const elems = try arena.alloc(Value, @as(usize, @intCast(dest_len))); 28904 for (elems, 0..) |*elem, i| { 28905 elem.* = try mod.intValue(elem_ty, bytes[i]); 28906 } 28907 28908 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 28909 28910 return beginComptimePtrMutationInner( 28911 sema, 28912 block, 28913 src, 28914 elem_ty, 28915 &elems[@as(usize, @intCast(elem_ptr.index))], 28916 ptr_elem_ty, 28917 parent.mut_decl, 28918 ); 28919 }, 28920 .repeated => { 28921 // An array is memory-optimized to store only a single element value, and 28922 // that value is understood to be the same for the entire length of the array. 28923 // However, now we want to modify an individual field and so the 28924 // representation has to change. If we wanted to avoid this, there would 28925 // need to be special detection elsewhere to identify when writing a value to an 28926 // array element that is stored using the `repeated` tag, and handle it 28927 // without making a call to this function. 28928 const arena = mod.tmp_hack_arena.allocator(); 28929 28930 const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod); 28931 const array_len_including_sentinel = 28932 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); 28933 const elems = try arena.alloc(Value, array_len_including_sentinel); 28934 @memset(elems, repeated_val.toValue()); 28935 28936 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 28937 28938 return beginComptimePtrMutationInner( 28939 sema, 28940 block, 28941 src, 28942 elem_ty, 28943 &elems[@as(usize, @intCast(elem_ptr.index))], 28944 ptr_elem_ty, 28945 parent.mut_decl, 28946 ); 28947 }, 28948 28949 .aggregate => return beginComptimePtrMutationInner( 28950 sema, 28951 block, 28952 src, 28953 elem_ty, 28954 &val_ptr.castTag(.aggregate).?.data[@as(usize, @intCast(elem_ptr.index))], 28955 ptr_elem_ty, 28956 parent.mut_decl, 28957 ), 28958 28959 else => unreachable, 28960 }, 28961 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) { 28962 .undef => { 28963 // An array has been initialized to undefined at comptime and now we 28964 // are for the first time setting an element. We must change the representation 28965 // of the array from `undef` to `array`. 28966 const arena = mod.tmp_hack_arena.allocator(); 28967 28968 const array_len_including_sentinel = 28969 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); 28970 const elems = try arena.alloc(Value, array_len_including_sentinel); 28971 @memset(elems, (try mod.intern(.{ .undef = elem_ty.toIntern() })).toValue()); 28972 28973 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 28974 28975 return beginComptimePtrMutationInner( 28976 sema, 28977 block, 28978 src, 28979 elem_ty, 28980 &elems[@as(usize, @intCast(elem_ptr.index))], 28981 ptr_elem_ty, 28982 parent.mut_decl, 28983 ); 28984 }, 28985 else => unreachable, 28986 }, 28987 } 28988 }, 28989 else => { 28990 if (elem_ptr.index != 0) { 28991 // TODO include a "declared here" note for the decl 28992 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{ 28993 elem_ptr.index, 28994 }); 28995 } 28996 return beginComptimePtrMutationInner( 28997 sema, 28998 block, 28999 src, 29000 parent.ty, 29001 val_ptr, 29002 ptr_elem_ty, 29003 parent.mut_decl, 29004 ); 29005 }, 29006 }, 29007 .reinterpret => |reinterpret| { 29008 if (!base_elem_ty.hasWellDefinedLayout(mod)) { 29009 // Even though the parent value type has well-defined memory layout, our 29010 // pointer type does not. 29011 return ComptimePtrMutationKit{ 29012 .mut_decl = parent.mut_decl, 29013 .pointee = .bad_ptr_ty, 29014 .ty = base_elem_ty, 29015 }; 29016 } 29017 29018 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty); 29019 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); 29020 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 29021 return ComptimePtrMutationKit{ 29022 .mut_decl = parent.mut_decl, 29023 .pointee = .{ .reinterpret = .{ 29024 .val_ptr = reinterpret.val_ptr, 29025 .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_idx, 29026 } }, 29027 .ty = parent.ty, 29028 }; 29029 }, 29030 .bad_decl_ty, .bad_ptr_ty => return parent, 29031 } 29032 }, 29033 .field => |field_ptr| { 29034 const base_child_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod); 29035 const field_index = @as(u32, @intCast(field_ptr.index)); 29036 29037 var parent = try sema.beginComptimePtrMutation(block, src, field_ptr.base.toValue(), base_child_ty); 29038 switch (parent.pointee) { 29039 .direct => |val_ptr| switch (val_ptr.ip_index) { 29040 .empty_struct => { 29041 const duped = try sema.arena.create(Value); 29042 duped.* = val_ptr.*; 29043 return beginComptimePtrMutationInner( 29044 sema, 29045 block, 29046 src, 29047 parent.ty.structFieldType(field_index, mod), 29048 duped, 29049 ptr_elem_ty, 29050 parent.mut_decl, 29051 ); 29052 }, 29053 .none => switch (val_ptr.tag()) { 29054 .aggregate => return beginComptimePtrMutationInner( 29055 sema, 29056 block, 29057 src, 29058 parent.ty.structFieldType(field_index, mod), 29059 &val_ptr.castTag(.aggregate).?.data[field_index], 29060 ptr_elem_ty, 29061 parent.mut_decl, 29062 ), 29063 .repeated => { 29064 const arena = mod.tmp_hack_arena.allocator(); 29065 29066 const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod)); 29067 @memset(elems, val_ptr.castTag(.repeated).?.data); 29068 val_ptr.* = try Value.Tag.aggregate.create(arena, elems); 29069 29070 return beginComptimePtrMutationInner( 29071 sema, 29072 block, 29073 src, 29074 parent.ty.structFieldType(field_index, mod), 29075 &elems[field_index], 29076 ptr_elem_ty, 29077 parent.mut_decl, 29078 ); 29079 }, 29080 .@"union" => { 29081 // We need to set the active field of the union. 29082 const union_tag_ty = base_child_ty.unionTagTypeHypothetical(mod); 29083 29084 const payload = &val_ptr.castTag(.@"union").?.data; 29085 payload.tag = try mod.enumValueFieldIndex(union_tag_ty, field_index); 29086 29087 return beginComptimePtrMutationInner( 29088 sema, 29089 block, 29090 src, 29091 parent.ty.structFieldType(field_index, mod), 29092 &payload.val, 29093 ptr_elem_ty, 29094 parent.mut_decl, 29095 ); 29096 }, 29097 .slice => switch (field_index) { 29098 Value.slice_ptr_index => return beginComptimePtrMutationInner( 29099 sema, 29100 block, 29101 src, 29102 parent.ty.slicePtrFieldType(mod), 29103 &val_ptr.castTag(.slice).?.data.ptr, 29104 ptr_elem_ty, 29105 parent.mut_decl, 29106 ), 29107 29108 Value.slice_len_index => return beginComptimePtrMutationInner( 29109 sema, 29110 block, 29111 src, 29112 Type.usize, 29113 &val_ptr.castTag(.slice).?.data.len, 29114 ptr_elem_ty, 29115 parent.mut_decl, 29116 ), 29117 29118 else => unreachable, 29119 }, 29120 else => unreachable, 29121 }, 29122 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) { 29123 .undef => { 29124 // A struct or union has been initialized to undefined at comptime and now we 29125 // are for the first time setting a field. We must change the representation 29126 // of the struct/union from `undef` to `struct`/`union`. 29127 const arena = mod.tmp_hack_arena.allocator(); 29128 29129 switch (parent.ty.zigTypeTag(mod)) { 29130 .Struct => { 29131 const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod)); 29132 for (fields, 0..) |*field, i| field.* = (try mod.intern(.{ 29133 .undef = parent.ty.structFieldType(i, mod).toIntern(), 29134 })).toValue(); 29135 29136 val_ptr.* = try Value.Tag.aggregate.create(arena, fields); 29137 29138 return beginComptimePtrMutationInner( 29139 sema, 29140 block, 29141 src, 29142 parent.ty.structFieldType(field_index, mod), 29143 &fields[field_index], 29144 ptr_elem_ty, 29145 parent.mut_decl, 29146 ); 29147 }, 29148 .Union => { 29149 const payload = try arena.create(Value.Payload.Union); 29150 const tag_ty = parent.ty.unionTagTypeHypothetical(mod); 29151 const payload_ty = parent.ty.structFieldType(field_index, mod); 29152 payload.* = .{ .data = .{ 29153 .tag = try mod.enumValueFieldIndex(tag_ty, field_index), 29154 .val = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(), 29155 } }; 29156 29157 val_ptr.* = Value.initPayload(&payload.base); 29158 29159 return beginComptimePtrMutationInner( 29160 sema, 29161 block, 29162 src, 29163 payload_ty, 29164 &payload.data.val, 29165 ptr_elem_ty, 29166 parent.mut_decl, 29167 ); 29168 }, 29169 .Pointer => { 29170 assert(parent.ty.isSlice(mod)); 29171 const ptr_ty = parent.ty.slicePtrFieldType(mod); 29172 val_ptr.* = try Value.Tag.slice.create(arena, .{ 29173 .ptr = (try mod.intern(.{ .undef = ptr_ty.toIntern() })).toValue(), 29174 .len = (try mod.intern(.{ .undef = .usize_type })).toValue(), 29175 }); 29176 29177 switch (field_index) { 29178 Value.slice_ptr_index => return beginComptimePtrMutationInner( 29179 sema, 29180 block, 29181 src, 29182 ptr_ty, 29183 &val_ptr.castTag(.slice).?.data.ptr, 29184 ptr_elem_ty, 29185 parent.mut_decl, 29186 ), 29187 Value.slice_len_index => return beginComptimePtrMutationInner( 29188 sema, 29189 block, 29190 src, 29191 Type.usize, 29192 &val_ptr.castTag(.slice).?.data.len, 29193 ptr_elem_ty, 29194 parent.mut_decl, 29195 ), 29196 29197 else => unreachable, 29198 } 29199 }, 29200 else => unreachable, 29201 } 29202 }, 29203 else => unreachable, 29204 }, 29205 }, 29206 .reinterpret => |reinterpret| { 29207 const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod); 29208 const field_offset = try sema.usizeCast(block, src, field_offset_u64); 29209 return ComptimePtrMutationKit{ 29210 .mut_decl = parent.mut_decl, 29211 .pointee = .{ .reinterpret = .{ 29212 .val_ptr = reinterpret.val_ptr, 29213 .byte_offset = reinterpret.byte_offset + field_offset, 29214 } }, 29215 .ty = parent.ty, 29216 }; 29217 }, 29218 .bad_decl_ty, .bad_ptr_ty => return parent, 29219 } 29220 }, 29221 } 29222 } 29223 29224 fn beginComptimePtrMutationInner( 29225 sema: *Sema, 29226 block: *Block, 29227 src: LazySrcLoc, 29228 decl_ty: Type, 29229 decl_val: *Value, 29230 ptr_elem_ty: Type, 29231 mut_decl: InternPool.Key.Ptr.Addr.MutDecl, 29232 ) CompileError!ComptimePtrMutationKit { 29233 const mod = sema.mod; 29234 const target = mod.getTarget(); 29235 const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok; 29236 29237 decl_val.* = try decl_val.unintern(sema.arena, mod); 29238 29239 if (coerce_ok) { 29240 return ComptimePtrMutationKit{ 29241 .mut_decl = mut_decl, 29242 .pointee = .{ .direct = decl_val }, 29243 .ty = decl_ty, 29244 }; 29245 } 29246 29247 // Handle the case that the decl is an array and we're actually trying to point to an element. 29248 if (decl_ty.isArrayOrVector(mod)) { 29249 const decl_elem_ty = decl_ty.childType(mod); 29250 if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) { 29251 return ComptimePtrMutationKit{ 29252 .mut_decl = mut_decl, 29253 .pointee = .{ .direct = decl_val }, 29254 .ty = decl_ty, 29255 }; 29256 } 29257 } 29258 29259 if (!decl_ty.hasWellDefinedLayout(mod)) { 29260 return ComptimePtrMutationKit{ 29261 .mut_decl = mut_decl, 29262 .pointee = .bad_decl_ty, 29263 .ty = decl_ty, 29264 }; 29265 } 29266 if (!ptr_elem_ty.hasWellDefinedLayout(mod)) { 29267 return ComptimePtrMutationKit{ 29268 .mut_decl = mut_decl, 29269 .pointee = .bad_ptr_ty, 29270 .ty = ptr_elem_ty, 29271 }; 29272 } 29273 return ComptimePtrMutationKit{ 29274 .mut_decl = mut_decl, 29275 .pointee = .{ .reinterpret = .{ 29276 .val_ptr = decl_val, 29277 .byte_offset = 0, 29278 } }, 29279 .ty = decl_ty, 29280 }; 29281 } 29282 29283 const TypedValueAndOffset = struct { 29284 tv: TypedValue, 29285 byte_offset: usize, 29286 }; 29287 29288 const ComptimePtrLoadKit = struct { 29289 /// The Value and Type corresponding to the pointee of the provided pointer. 29290 /// If a direct dereference is not possible, this is null. 29291 pointee: ?TypedValue, 29292 /// The largest parent Value containing `pointee` and having a well-defined memory layout. 29293 /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null). 29294 parent: ?TypedValueAndOffset, 29295 /// Whether the `pointee` could be mutated by further 29296 /// semantic analysis and a copy must be performed. 29297 is_mutable: bool, 29298 /// If the root decl could not be used as `parent`, this is the type that 29299 /// caused that by not having a well-defined layout 29300 ty_without_well_defined_layout: ?Type, 29301 }; 29302 29303 const ComptimePtrLoadError = CompileError || error{ 29304 RuntimeLoad, 29305 }; 29306 29307 /// If `maybe_array_ty` is provided, it will be used to directly dereference an 29308 /// .elem_ptr of type T to a value of [N]T, if necessary. 29309 fn beginComptimePtrLoad( 29310 sema: *Sema, 29311 block: *Block, 29312 src: LazySrcLoc, 29313 ptr_val: Value, 29314 maybe_array_ty: ?Type, 29315 ) ComptimePtrLoadError!ComptimePtrLoadKit { 29316 const mod = sema.mod; 29317 const target = mod.getTarget(); 29318 29319 var deref: ComptimePtrLoadKit = switch (mod.intern_pool.indexToKey(ptr_val.toIntern())) { 29320 .ptr => |ptr| switch (ptr.addr) { 29321 .decl, .mut_decl => blk: { 29322 const decl_index = switch (ptr.addr) { 29323 .decl => |decl| decl, 29324 .mut_decl => |mut_decl| mut_decl.decl, 29325 else => unreachable, 29326 }; 29327 const is_mutable = ptr.addr == .mut_decl; 29328 const decl = mod.declPtr(decl_index); 29329 const decl_tv = try decl.typedValue(); 29330 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad; 29331 29332 const layout_defined = decl.ty.hasWellDefinedLayout(mod); 29333 break :blk ComptimePtrLoadKit{ 29334 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, 29335 .pointee = decl_tv, 29336 .is_mutable = is_mutable, 29337 .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, 29338 }; 29339 }, 29340 .int => return error.RuntimeLoad, 29341 .eu_payload, .opt_payload => |container_ptr| blk: { 29342 const container_ty = mod.intern_pool.typeOf(container_ptr).toType().childType(mod); 29343 const payload_ty = switch (ptr.addr) { 29344 .eu_payload => container_ty.errorUnionPayload(mod), 29345 .opt_payload => container_ty.optionalChild(mod), 29346 else => unreachable, 29347 }; 29348 var deref = try sema.beginComptimePtrLoad(block, src, container_ptr.toValue(), container_ty); 29349 29350 // eu_payload and opt_payload never have a well-defined layout 29351 if (deref.parent != null) { 29352 deref.parent = null; 29353 deref.ty_without_well_defined_layout = container_ty; 29354 } 29355 29356 if (deref.pointee) |*tv| { 29357 const coerce_in_mem_ok = 29358 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or 29359 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok; 29360 if (coerce_in_mem_ok) { 29361 const payload_val = switch (tv.val.ip_index) { 29362 .none => tv.val.cast(Value.Payload.SubValue).?.data, 29363 .null_value => return sema.fail(block, src, "attempt to use null value", .{}), 29364 else => switch (mod.intern_pool.indexToKey(tv.val.toIntern())) { 29365 .error_union => |error_union| switch (error_union.val) { 29366 .err_name => |err_name| return sema.fail( 29367 block, 29368 src, 29369 "attempt to unwrap error: {}", 29370 .{err_name.fmt(&mod.intern_pool)}, 29371 ), 29372 .payload => |payload| payload, 29373 }, 29374 .opt => |opt| switch (opt.val) { 29375 .none => return sema.fail(block, src, "attempt to use null value", .{}), 29376 else => |payload| payload, 29377 }, 29378 else => unreachable, 29379 }.toValue(), 29380 }; 29381 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; 29382 break :blk deref; 29383 } 29384 } 29385 deref.pointee = null; 29386 break :blk deref; 29387 }, 29388 .comptime_field => |comptime_field| blk: { 29389 const field_ty = mod.intern_pool.typeOf(comptime_field).toType(); 29390 break :blk ComptimePtrLoadKit{ 29391 .parent = null, 29392 .pointee = .{ .ty = field_ty, .val = comptime_field.toValue() }, 29393 .is_mutable = false, 29394 .ty_without_well_defined_layout = field_ty, 29395 }; 29396 }, 29397 .elem => |elem_ptr| blk: { 29398 const elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod); 29399 var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.base.toValue(), null); 29400 29401 // This code assumes that elem_ptrs have been "flattened" in order for direct dereference 29402 // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that 29403 // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" 29404 switch (mod.intern_pool.indexToKey(elem_ptr.base)) { 29405 .ptr => |base_ptr| switch (base_ptr.addr) { 29406 .elem => |base_elem| assert(!mod.intern_pool.typeOf(base_elem.base).toType().elemType2(mod).eql(elem_ty, mod)), 29407 else => {}, 29408 }, 29409 else => {}, 29410 } 29411 29412 if (elem_ptr.index != 0) { 29413 if (elem_ty.hasWellDefinedLayout(mod)) { 29414 if (deref.parent) |*parent| { 29415 // Update the byte offset (in-place) 29416 const elem_size = try sema.typeAbiSize(elem_ty); 29417 const offset = parent.byte_offset + elem_size * elem_ptr.index; 29418 parent.byte_offset = try sema.usizeCast(block, src, offset); 29419 } 29420 } else { 29421 deref.parent = null; 29422 deref.ty_without_well_defined_layout = elem_ty; 29423 } 29424 } 29425 29426 // If we're loading an elem that was derived from a different type 29427 // than the true type of the underlying decl, we cannot deref directly 29428 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: { 29429 const deref_elem_ty = deref.pointee.?.ty.childType(mod); 29430 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or 29431 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; 29432 } else false; 29433 if (!ty_matches) { 29434 deref.pointee = null; 29435 break :blk deref; 29436 } 29437 29438 var array_tv = deref.pointee.?; 29439 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod); 29440 if (maybe_array_ty) |load_ty| { 29441 // It's possible that we're loading a [N]T, in which case we'd like to slice 29442 // the pointee array directly from our parent array. 29443 if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) { 29444 const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod)); 29445 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index); 29446 deref.pointee = if (elem_ptr.index + len <= check_len) TypedValue{ 29447 .ty = try mod.arrayType(.{ 29448 .len = len, 29449 .child = elem_ty.toIntern(), 29450 }), 29451 .val = try array_tv.val.sliceArray(mod, sema.arena, elem_idx, elem_idx + len), 29452 } else null; 29453 break :blk deref; 29454 } 29455 } 29456 29457 if (elem_ptr.index >= check_len) { 29458 deref.pointee = null; 29459 break :blk deref; 29460 } 29461 if (elem_ptr.index == check_len - 1) { 29462 if (array_tv.ty.sentinel(mod)) |sent| { 29463 deref.pointee = TypedValue{ 29464 .ty = elem_ty, 29465 .val = sent, 29466 }; 29467 break :blk deref; 29468 } 29469 } 29470 deref.pointee = TypedValue{ 29471 .ty = elem_ty, 29472 .val = try array_tv.val.elemValue(mod, @as(usize, @intCast(elem_ptr.index))), 29473 }; 29474 break :blk deref; 29475 }, 29476 .field => |field_ptr| blk: { 29477 const field_index = @as(u32, @intCast(field_ptr.index)); 29478 const container_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod); 29479 var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.base.toValue(), container_ty); 29480 29481 if (container_ty.hasWellDefinedLayout(mod)) { 29482 const struct_obj = mod.typeToStruct(container_ty); 29483 if (struct_obj != null and struct_obj.?.layout == .Packed) { 29484 // packed structs are not byte addressable 29485 deref.parent = null; 29486 } else if (deref.parent) |*parent| { 29487 // Update the byte offset (in-place) 29488 try sema.resolveTypeLayout(container_ty); 29489 const field_offset = container_ty.structFieldOffset(field_index, mod); 29490 parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset); 29491 } 29492 } else { 29493 deref.parent = null; 29494 deref.ty_without_well_defined_layout = container_ty; 29495 } 29496 29497 const tv = deref.pointee orelse { 29498 deref.pointee = null; 29499 break :blk deref; 29500 }; 29501 const coerce_in_mem_ok = 29502 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or 29503 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok; 29504 if (!coerce_in_mem_ok) { 29505 deref.pointee = null; 29506 break :blk deref; 29507 } 29508 29509 if (container_ty.isSlice(mod)) { 29510 deref.pointee = switch (field_index) { 29511 Value.slice_ptr_index => TypedValue{ 29512 .ty = container_ty.slicePtrFieldType(mod), 29513 .val = tv.val.slicePtr(mod), 29514 }, 29515 Value.slice_len_index => TypedValue{ 29516 .ty = Type.usize, 29517 .val = mod.intern_pool.indexToKey(try tv.val.intern(tv.ty, mod)).ptr.len.toValue(), 29518 }, 29519 else => unreachable, 29520 }; 29521 } else { 29522 const field_ty = container_ty.structFieldType(field_index, mod); 29523 deref.pointee = TypedValue{ 29524 .ty = field_ty, 29525 .val = try tv.val.fieldValue(mod, field_index), 29526 }; 29527 } 29528 break :blk deref; 29529 }, 29530 }, 29531 .opt => |opt| switch (opt.val) { 29532 .none => return sema.fail(block, src, "attempt to use null value", .{}), 29533 else => |payload| try sema.beginComptimePtrLoad(block, src, payload.toValue(), null), 29534 }, 29535 else => unreachable, 29536 }; 29537 29538 if (deref.pointee) |tv| { 29539 if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) { 29540 deref.parent = .{ .tv = tv, .byte_offset = 0 }; 29541 } 29542 } 29543 return deref; 29544 } 29545 29546 fn bitCast( 29547 sema: *Sema, 29548 block: *Block, 29549 dest_ty_unresolved: Type, 29550 inst: Air.Inst.Ref, 29551 inst_src: LazySrcLoc, 29552 operand_src: ?LazySrcLoc, 29553 ) CompileError!Air.Inst.Ref { 29554 const mod = sema.mod; 29555 const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); 29556 try sema.resolveTypeLayout(dest_ty); 29557 29558 const old_ty = try sema.resolveTypeFields(sema.typeOf(inst)); 29559 try sema.resolveTypeLayout(old_ty); 29560 29561 const dest_bits = dest_ty.bitSize(mod); 29562 const old_bits = old_ty.bitSize(mod); 29563 29564 if (old_bits != dest_bits) { 29565 return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{ 29566 dest_ty.fmt(mod), 29567 dest_bits, 29568 old_ty.fmt(mod), 29569 old_bits, 29570 }); 29571 } 29572 29573 if (try sema.resolveMaybeUndefVal(inst)) |val| { 29574 if (try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0)) |result_val| { 29575 return sema.addConstant(result_val); 29576 } 29577 } 29578 try sema.requireRuntimeBlock(block, inst_src, operand_src); 29579 return block.addBitCast(dest_ty, inst); 29580 } 29581 29582 fn bitCastVal( 29583 sema: *Sema, 29584 block: *Block, 29585 src: LazySrcLoc, 29586 val: Value, 29587 old_ty: Type, 29588 new_ty: Type, 29589 buffer_offset: usize, 29590 ) !?Value { 29591 const mod = sema.mod; 29592 if (old_ty.eql(new_ty, mod)) return val; 29593 29594 // For types with well-defined memory layouts, we serialize them a byte buffer, 29595 // then deserialize to the new type. 29596 const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(mod)); 29597 const buffer = try sema.gpa.alloc(u8, abi_size); 29598 defer sema.gpa.free(buffer); 29599 val.writeToMemory(old_ty, mod, buffer) catch |err| switch (err) { 29600 error.OutOfMemory => return error.OutOfMemory, 29601 error.ReinterpretDeclRef => return null, 29602 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already 29603 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}), 29604 }; 29605 return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena); 29606 } 29607 29608 fn coerceArrayPtrToSlice( 29609 sema: *Sema, 29610 block: *Block, 29611 dest_ty: Type, 29612 inst: Air.Inst.Ref, 29613 inst_src: LazySrcLoc, 29614 ) CompileError!Air.Inst.Ref { 29615 const mod = sema.mod; 29616 if (try sema.resolveMaybeUndefVal(inst)) |val| { 29617 const ptr_array_ty = sema.typeOf(inst); 29618 const array_ty = ptr_array_ty.childType(mod); 29619 const slice_val = try mod.intern(.{ .ptr = .{ 29620 .ty = dest_ty.toIntern(), 29621 .addr = switch (mod.intern_pool.indexToKey(val.toIntern())) { 29622 .undef => .{ .int = try mod.intern(.{ .undef = .usize_type }) }, 29623 .ptr => |ptr| ptr.addr, 29624 else => unreachable, 29625 }, 29626 .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).toIntern(), 29627 } }); 29628 return sema.addConstant(slice_val.toValue()); 29629 } 29630 try sema.requireRuntimeBlock(block, inst_src, null); 29631 return block.addTyOp(.array_to_slice, dest_ty, inst); 29632 } 29633 29634 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool { 29635 const mod = sema.mod; 29636 const dest_info = dest_ty.ptrInfo(mod); 29637 const inst_info = inst_ty.ptrInfo(mod); 29638 const len0 = (inst_info.child.toType().zigTypeTag(mod) == .Array and (inst_info.child.toType().arrayLenIncludingSentinel(mod) == 0 or 29639 (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 29640 (inst_info.child.toType().isTuple(mod) and inst_info.child.toType().structFieldCount(mod) == 0); 29641 29642 const ok_cv_qualifiers = 29643 ((!inst_info.flags.is_const or dest_info.flags.is_const) or len0) and 29644 (!inst_info.flags.is_volatile or dest_info.flags.is_volatile); 29645 29646 if (!ok_cv_qualifiers) { 29647 in_memory_result.* = .{ .ptr_qualifiers = .{ 29648 .actual_const = inst_info.flags.is_const, 29649 .wanted_const = dest_info.flags.is_const, 29650 .actual_volatile = inst_info.flags.is_volatile, 29651 .wanted_volatile = dest_info.flags.is_volatile, 29652 } }; 29653 return false; 29654 } 29655 if (dest_info.flags.address_space != inst_info.flags.address_space) { 29656 in_memory_result.* = .{ .ptr_addrspace = .{ 29657 .actual = inst_info.flags.address_space, 29658 .wanted = dest_info.flags.address_space, 29659 } }; 29660 return false; 29661 } 29662 if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true; 29663 if (len0) return true; 29664 29665 const inst_align = inst_info.flags.alignment.toByteUnitsOptional() orelse 29666 inst_info.child.toType().abiAlignment(mod); 29667 29668 const dest_align = dest_info.flags.alignment.toByteUnitsOptional() orelse 29669 dest_info.child.toType().abiAlignment(mod); 29670 29671 if (dest_align > inst_align) { 29672 in_memory_result.* = .{ .ptr_alignment = .{ 29673 .actual = inst_align, 29674 .wanted = dest_align, 29675 } }; 29676 return false; 29677 } 29678 return true; 29679 } 29680 29681 fn coerceCompatiblePtrs( 29682 sema: *Sema, 29683 block: *Block, 29684 dest_ty: Type, 29685 inst: Air.Inst.Ref, 29686 inst_src: LazySrcLoc, 29687 ) !Air.Inst.Ref { 29688 const mod = sema.mod; 29689 const inst_ty = sema.typeOf(inst); 29690 if (try sema.resolveMaybeUndefVal(inst)) |val| { 29691 if (!val.isUndef(mod) and val.isNull(mod) and !dest_ty.isAllowzeroPtr(mod)) { 29692 return sema.fail(block, inst_src, "null pointer casted to type '{}'", .{dest_ty.fmt(sema.mod)}); 29693 } 29694 // The comptime Value representation is compatible with both types. 29695 return sema.addConstant( 29696 try mod.getCoerced((try val.intern(inst_ty, mod)).toValue(), dest_ty), 29697 ); 29698 } 29699 try sema.requireRuntimeBlock(block, inst_src, null); 29700 const inst_allows_zero = inst_ty.zigTypeTag(mod) != .Pointer or inst_ty.ptrAllowsZero(mod); 29701 if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(mod) and 29702 (try sema.typeHasRuntimeBits(dest_ty.elemType2(mod)) or dest_ty.elemType2(mod).zigTypeTag(mod) == .Fn)) 29703 { 29704 const actual_ptr = if (inst_ty.isSlice(mod)) 29705 try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) 29706 else 29707 inst; 29708 const ptr_int = try block.addUnOp(.int_from_ptr, actual_ptr); 29709 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); 29710 const ok = if (inst_ty.isSlice(mod)) ok: { 29711 const len = try sema.analyzeSliceLen(block, inst_src, inst); 29712 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); 29713 break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero); 29714 } else is_non_zero; 29715 try sema.addSafetyCheck(block, ok, .cast_to_null); 29716 } 29717 return sema.bitCast(block, dest_ty, inst, inst_src, null); 29718 } 29719 29720 fn coerceEnumToUnion( 29721 sema: *Sema, 29722 block: *Block, 29723 union_ty: Type, 29724 union_ty_src: LazySrcLoc, 29725 inst: Air.Inst.Ref, 29726 inst_src: LazySrcLoc, 29727 ) !Air.Inst.Ref { 29728 const mod = sema.mod; 29729 const ip = &mod.intern_pool; 29730 const inst_ty = sema.typeOf(inst); 29731 29732 const tag_ty = union_ty.unionTagType(mod) orelse { 29733 const msg = msg: { 29734 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 29735 union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), 29736 }); 29737 errdefer msg.destroy(sema.gpa); 29738 try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{}); 29739 try sema.addDeclaredHereNote(msg, union_ty); 29740 break :msg msg; 29741 }; 29742 return sema.failWithOwnedErrorMsg(msg); 29743 }; 29744 29745 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src); 29746 if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { 29747 const field_index = union_ty.unionTagFieldIndex(val, sema.mod) orelse { 29748 const msg = msg: { 29749 const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{ 29750 union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod), 29751 }); 29752 errdefer msg.destroy(sema.gpa); 29753 try sema.addDeclaredHereNote(msg, union_ty); 29754 break :msg msg; 29755 }; 29756 return sema.failWithOwnedErrorMsg(msg); 29757 }; 29758 29759 const union_obj = mod.typeToUnion(union_ty).?; 29760 const field = union_obj.fields.values()[field_index]; 29761 const field_ty = try sema.resolveTypeFields(field.ty); 29762 if (field_ty.zigTypeTag(mod) == .NoReturn) { 29763 const msg = msg: { 29764 const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{}); 29765 errdefer msg.destroy(sema.gpa); 29766 29767 const field_name = union_obj.fields.keys()[field_index]; 29768 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 29769 field_name.fmt(ip), 29770 }); 29771 try sema.addDeclaredHereNote(msg, union_ty); 29772 break :msg msg; 29773 }; 29774 return sema.failWithOwnedErrorMsg(msg); 29775 } 29776 const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { 29777 const msg = msg: { 29778 const field_name = union_obj.fields.keys()[field_index]; 29779 const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{ 29780 inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), 29781 field_ty.fmt(sema.mod), field_name.fmt(ip), 29782 }); 29783 errdefer msg.destroy(sema.gpa); 29784 29785 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ 29786 field_name.fmt(ip), 29787 }); 29788 try sema.addDeclaredHereNote(msg, union_ty); 29789 break :msg msg; 29790 }; 29791 return sema.failWithOwnedErrorMsg(msg); 29792 }; 29793 29794 return sema.addConstant(try mod.unionValue(union_ty, val, opv)); 29795 } 29796 29797 try sema.requireRuntimeBlock(block, inst_src, null); 29798 29799 if (tag_ty.isNonexhaustiveEnum(mod)) { 29800 const msg = msg: { 29801 const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{ 29802 union_ty.fmt(sema.mod), 29803 }); 29804 errdefer msg.destroy(sema.gpa); 29805 try sema.addDeclaredHereNote(msg, tag_ty); 29806 break :msg msg; 29807 }; 29808 return sema.failWithOwnedErrorMsg(msg); 29809 } 29810 29811 const union_obj = mod.typeToUnion(union_ty).?; 29812 { 29813 var msg: ?*Module.ErrorMsg = null; 29814 errdefer if (msg) |some| some.destroy(sema.gpa); 29815 29816 for (union_obj.fields.values(), 0..) |field, i| { 29817 if (field.ty.zigTypeTag(mod) == .NoReturn) { 29818 const err_msg = msg orelse try sema.errMsg( 29819 block, 29820 inst_src, 29821 "runtime coercion from enum '{}' to union '{}' which has a 'noreturn' field", 29822 .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) }, 29823 ); 29824 msg = err_msg; 29825 29826 try sema.addFieldErrNote(union_ty, i, err_msg, "'noreturn' field here", .{}); 29827 } 29828 } 29829 if (msg) |some| { 29830 msg = null; 29831 try sema.addDeclaredHereNote(some, union_ty); 29832 return sema.failWithOwnedErrorMsg(some); 29833 } 29834 } 29835 29836 // If the union has all fields 0 bits, the union value is just the enum value. 29837 if (union_ty.unionHasAllZeroBitFieldTypes(mod)) { 29838 return block.addBitCast(union_ty, enum_tag); 29839 } 29840 29841 const msg = msg: { 29842 const msg = try sema.errMsg( 29843 block, 29844 inst_src, 29845 "runtime coercion from enum '{}' to union '{}' which has non-void fields", 29846 .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) }, 29847 ); 29848 errdefer msg.destroy(sema.gpa); 29849 29850 var it = union_obj.fields.iterator(); 29851 var field_index: usize = 0; 29852 while (it.next()) |field| : (field_index += 1) { 29853 const field_name = field.key_ptr.*; 29854 const field_ty = field.value_ptr.ty; 29855 if (!(try sema.typeHasRuntimeBits(field_ty))) continue; 29856 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{ 29857 field_name.fmt(ip), 29858 field_ty.fmt(sema.mod), 29859 }); 29860 } 29861 try sema.addDeclaredHereNote(msg, union_ty); 29862 break :msg msg; 29863 }; 29864 return sema.failWithOwnedErrorMsg(msg); 29865 } 29866 29867 fn coerceAnonStructToUnion( 29868 sema: *Sema, 29869 block: *Block, 29870 union_ty: Type, 29871 union_ty_src: LazySrcLoc, 29872 inst: Air.Inst.Ref, 29873 inst_src: LazySrcLoc, 29874 ) !Air.Inst.Ref { 29875 const mod = sema.mod; 29876 const inst_ty = sema.typeOf(inst); 29877 const field_info: union(enum) { 29878 name: InternPool.NullTerminatedString, 29879 count: usize, 29880 } = switch (mod.intern_pool.indexToKey(inst_ty.toIntern())) { 29881 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1) 29882 .{ .name = anon_struct_type.names[0] } 29883 else 29884 .{ .count = anon_struct_type.names.len }, 29885 .struct_type => |struct_type| name: { 29886 const field_names = mod.structPtrUnwrap(struct_type.index).?.fields.keys(); 29887 break :name if (field_names.len == 1) 29888 .{ .name = field_names[0] } 29889 else 29890 .{ .count = field_names.len }; 29891 }, 29892 else => unreachable, 29893 }; 29894 switch (field_info) { 29895 .name => |field_name| { 29896 const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty); 29897 return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src); 29898 }, 29899 .count => |field_count| { 29900 assert(field_count != 1); 29901 const msg = msg: { 29902 const msg = if (field_count > 1) try sema.errMsg( 29903 block, 29904 inst_src, 29905 "cannot initialize multiple union fields at once; unions can only have one active field", 29906 .{}, 29907 ) else try sema.errMsg( 29908 block, 29909 inst_src, 29910 "union initializer must initialize one field", 29911 .{}, 29912 ); 29913 errdefer msg.destroy(sema.gpa); 29914 29915 // TODO add notes for where the anon struct was created to point out 29916 // the extra fields. 29917 29918 try sema.addDeclaredHereNote(msg, union_ty); 29919 break :msg msg; 29920 }; 29921 return sema.failWithOwnedErrorMsg(msg); 29922 }, 29923 } 29924 } 29925 29926 fn coerceAnonStructToUnionPtrs( 29927 sema: *Sema, 29928 block: *Block, 29929 ptr_union_ty: Type, 29930 union_ty_src: LazySrcLoc, 29931 ptr_anon_struct: Air.Inst.Ref, 29932 anon_struct_src: LazySrcLoc, 29933 ) !Air.Inst.Ref { 29934 const mod = sema.mod; 29935 const union_ty = ptr_union_ty.childType(mod); 29936 const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); 29937 const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src); 29938 return sema.analyzeRef(block, union_ty_src, union_inst); 29939 } 29940 29941 fn coerceAnonStructToStructPtrs( 29942 sema: *Sema, 29943 block: *Block, 29944 ptr_struct_ty: Type, 29945 struct_ty_src: LazySrcLoc, 29946 ptr_anon_struct: Air.Inst.Ref, 29947 anon_struct_src: LazySrcLoc, 29948 ) !Air.Inst.Ref { 29949 const mod = sema.mod; 29950 const struct_ty = ptr_struct_ty.childType(mod); 29951 const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); 29952 const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, anon_struct, anon_struct_src); 29953 return sema.analyzeRef(block, struct_ty_src, struct_inst); 29954 } 29955 29956 /// If the lengths match, coerces element-wise. 29957 fn coerceArrayLike( 29958 sema: *Sema, 29959 block: *Block, 29960 dest_ty: Type, 29961 dest_ty_src: LazySrcLoc, 29962 inst: Air.Inst.Ref, 29963 inst_src: LazySrcLoc, 29964 ) !Air.Inst.Ref { 29965 const mod = sema.mod; 29966 const inst_ty = sema.typeOf(inst); 29967 const inst_len = inst_ty.arrayLen(mod); 29968 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod)); 29969 const target = mod.getTarget(); 29970 29971 if (dest_len != inst_len) { 29972 const msg = msg: { 29973 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 29974 dest_ty.fmt(mod), inst_ty.fmt(mod), 29975 }); 29976 errdefer msg.destroy(sema.gpa); 29977 try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); 29978 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len}); 29979 break :msg msg; 29980 }; 29981 return sema.failWithOwnedErrorMsg(msg); 29982 } 29983 29984 const dest_elem_ty = dest_ty.childType(mod); 29985 const inst_elem_ty = inst_ty.childType(mod); 29986 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src); 29987 if (in_memory_result == .ok) { 29988 if (try sema.resolveMaybeUndefVal(inst)) |inst_val| { 29989 // These types share the same comptime value representation. 29990 return sema.coerceInMemory(inst_val, dest_ty); 29991 } 29992 try sema.requireRuntimeBlock(block, inst_src, null); 29993 return block.addBitCast(dest_ty, inst); 29994 } 29995 29996 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); 29997 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); 29998 var runtime_src: ?LazySrcLoc = null; 29999 30000 for (element_vals, element_refs, 0..) |*val, *ref, i| { 30001 const index_ref = try sema.addConstant(try mod.intValue(Type.usize, i)); 30002 const src = inst_src; // TODO better source location 30003 const elem_src = inst_src; // TODO better source location 30004 const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true); 30005 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 30006 ref.* = coerced; 30007 if (runtime_src == null) { 30008 if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| { 30009 val.* = try elem_val.intern(dest_elem_ty, mod); 30010 } else { 30011 runtime_src = elem_src; 30012 } 30013 } 30014 } 30015 30016 if (runtime_src) |rs| { 30017 try sema.requireRuntimeBlock(block, inst_src, rs); 30018 return block.addAggregateInit(dest_ty, element_refs); 30019 } 30020 30021 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 30022 .ty = dest_ty.toIntern(), 30023 .storage = .{ .elems = element_vals }, 30024 } })).toValue()); 30025 } 30026 30027 /// If the lengths match, coerces element-wise. 30028 fn coerceTupleToArray( 30029 sema: *Sema, 30030 block: *Block, 30031 dest_ty: Type, 30032 dest_ty_src: LazySrcLoc, 30033 inst: Air.Inst.Ref, 30034 inst_src: LazySrcLoc, 30035 ) !Air.Inst.Ref { 30036 const mod = sema.mod; 30037 const inst_ty = sema.typeOf(inst); 30038 const inst_len = inst_ty.arrayLen(mod); 30039 const dest_len = dest_ty.arrayLen(mod); 30040 30041 if (dest_len != inst_len) { 30042 const msg = msg: { 30043 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ 30044 dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), 30045 }); 30046 errdefer msg.destroy(sema.gpa); 30047 try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); 30048 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len}); 30049 break :msg msg; 30050 }; 30051 return sema.failWithOwnedErrorMsg(msg); 30052 } 30053 30054 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len); 30055 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems); 30056 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); 30057 const dest_elem_ty = dest_ty.childType(mod); 30058 30059 var runtime_src: ?LazySrcLoc = null; 30060 for (element_vals, element_refs, 0..) |*val, *ref, i_usize| { 30061 const i = @as(u32, @intCast(i_usize)); 30062 if (i_usize == inst_len) { 30063 const sentinel_val = dest_ty.sentinel(mod).?; 30064 val.* = sentinel_val.toIntern(); 30065 ref.* = try sema.addConstant(sentinel_val); 30066 break; 30067 } 30068 const elem_src = inst_src; // TODO better source location 30069 const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i); 30070 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); 30071 ref.* = coerced; 30072 if (runtime_src == null) { 30073 if (try sema.resolveMaybeUndefVal(coerced)) |elem_val| { 30074 val.* = try elem_val.intern(dest_elem_ty, mod); 30075 } else { 30076 runtime_src = elem_src; 30077 } 30078 } 30079 } 30080 30081 if (runtime_src) |rs| { 30082 try sema.requireRuntimeBlock(block, inst_src, rs); 30083 return block.addAggregateInit(dest_ty, element_refs); 30084 } 30085 30086 return sema.addConstant((try mod.intern(.{ .aggregate = .{ 30087 .ty = dest_ty.toIntern(), 30088 .storage = .{ .elems = element_vals }, 30089 } })).toValue()); 30090 } 30091 30092 /// If the lengths match, coerces element-wise. 30093 fn coerceTupleToSlicePtrs( 30094 sema: *Sema, 30095 block: *Block, 30096 slice_ty: Type, 30097 slice_ty_src: LazySrcLoc, 30098 ptr_tuple: Air.Inst.Ref, 30099 tuple_src: LazySrcLoc, 30100 ) !Air.Inst.Ref { 30101 const mod = sema.mod; 30102 const tuple_ty = sema.typeOf(ptr_tuple).childType(mod); 30103 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 30104 const slice_info = slice_ty.ptrInfo(mod); 30105 const array_ty = try mod.arrayType(.{ 30106 .len = tuple_ty.structFieldCount(mod), 30107 .sentinel = slice_info.sentinel, 30108 .child = slice_info.child, 30109 }); 30110 const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); 30111 if (slice_info.flags.alignment != .none) { 30112 return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 30113 } 30114 const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst); 30115 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src); 30116 } 30117 30118 /// If the lengths match, coerces element-wise. 30119 fn coerceTupleToArrayPtrs( 30120 sema: *Sema, 30121 block: *Block, 30122 ptr_array_ty: Type, 30123 array_ty_src: LazySrcLoc, 30124 ptr_tuple: Air.Inst.Ref, 30125 tuple_src: LazySrcLoc, 30126 ) !Air.Inst.Ref { 30127 const mod = sema.mod; 30128 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); 30129 const ptr_info = ptr_array_ty.ptrInfo(mod); 30130 const array_ty = ptr_info.child.toType(); 30131 const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src); 30132 if (ptr_info.flags.alignment != .none) { 30133 return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{}); 30134 } 30135 const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst); 30136 return ptr_array; 30137 } 30138 30139 /// Handles both tuples and anon struct literals. Coerces field-wise. Reports 30140 /// errors for both extra fields and missing fields. 30141 fn coerceTupleToStruct( 30142 sema: *Sema, 30143 block: *Block, 30144 dest_ty: Type, 30145 inst: Air.Inst.Ref, 30146 inst_src: LazySrcLoc, 30147 ) !Air.Inst.Ref { 30148 const mod = sema.mod; 30149 const ip = &mod.intern_pool; 30150 const struct_ty = try sema.resolveTypeFields(dest_ty); 30151 30152 if (struct_ty.isTupleOrAnonStruct(mod)) { 30153 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src); 30154 } 30155 30156 const fields = struct_ty.structFields(mod); 30157 const field_vals = try sema.arena.alloc(InternPool.Index, fields.count()); 30158 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); 30159 @memset(field_refs, .none); 30160 30161 const inst_ty = sema.typeOf(inst); 30162 var runtime_src: ?LazySrcLoc = null; 30163 const field_count = switch (ip.indexToKey(inst_ty.toIntern())) { 30164 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 30165 .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| 30166 struct_obj.fields.count() 30167 else 30168 0, 30169 else => unreachable, 30170 }; 30171 for (0..field_count) |field_index_usize| { 30172 const field_i = @as(u32, @intCast(field_index_usize)); 30173 const field_src = inst_src; // TODO better source location 30174 // https://github.com/ziglang/zig/issues/15709 30175 const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { 30176 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) 30177 anon_struct_type.names[field_i] 30178 else 30179 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), 30180 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i], 30181 else => unreachable, 30182 }; 30183 const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); 30184 const field = fields.values()[field_index]; 30185 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); 30186 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src); 30187 field_refs[field_index] = coerced; 30188 if (field.is_comptime) { 30189 const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { 30190 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); 30191 }; 30192 30193 if (!init_val.eql(field.default_val.toValue(), field.ty, sema.mod)) { 30194 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); 30195 } 30196 } 30197 if (runtime_src == null) { 30198 if (try sema.resolveMaybeUndefVal(coerced)) |field_val| { 30199 field_vals[field_index] = field_val.toIntern(); 30200 } else { 30201 runtime_src = field_src; 30202 } 30203 } 30204 } 30205 30206 // Populate default field values and report errors for missing fields. 30207 var root_msg: ?*Module.ErrorMsg = null; 30208 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 30209 30210 for (field_refs, 0..) |*field_ref, i| { 30211 if (field_ref.* != .none) continue; 30212 30213 const field_name = fields.keys()[i]; 30214 const field = fields.values()[i]; 30215 const field_src = inst_src; // TODO better source location 30216 if (field.default_val == .none) { 30217 const template = "missing struct field: {}"; 30218 const args = .{field_name.fmt(ip)}; 30219 if (root_msg) |msg| { 30220 try sema.errNote(block, field_src, msg, template, args); 30221 } else { 30222 root_msg = try sema.errMsg(block, field_src, template, args); 30223 } 30224 continue; 30225 } 30226 if (runtime_src == null) { 30227 field_vals[i] = field.default_val; 30228 } else { 30229 field_ref.* = try sema.addConstant(field.default_val.toValue()); 30230 } 30231 } 30232 30233 if (root_msg) |msg| { 30234 try sema.addDeclaredHereNote(msg, struct_ty); 30235 root_msg = null; 30236 return sema.failWithOwnedErrorMsg(msg); 30237 } 30238 30239 if (runtime_src) |rs| { 30240 try sema.requireRuntimeBlock(block, inst_src, rs); 30241 return block.addAggregateInit(struct_ty, field_refs); 30242 } 30243 30244 const struct_val = try mod.intern(.{ .aggregate = .{ 30245 .ty = struct_ty.toIntern(), 30246 .storage = .{ .elems = field_vals }, 30247 } }); 30248 // TODO: figure out InternPool removals for incremental compilation 30249 //errdefer ip.remove(struct_val); 30250 30251 return sema.addConstant(struct_val.toValue()); 30252 } 30253 30254 fn coerceTupleToTuple( 30255 sema: *Sema, 30256 block: *Block, 30257 tuple_ty: Type, 30258 inst: Air.Inst.Ref, 30259 inst_src: LazySrcLoc, 30260 ) !Air.Inst.Ref { 30261 const mod = sema.mod; 30262 const ip = &mod.intern_pool; 30263 const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) { 30264 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 30265 .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| 30266 struct_obj.fields.count() 30267 else 30268 0, 30269 else => unreachable, 30270 }; 30271 const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); 30272 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); 30273 @memset(field_refs, .none); 30274 30275 const inst_ty = sema.typeOf(inst); 30276 const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) { 30277 .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, 30278 .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| 30279 struct_obj.fields.count() 30280 else 30281 0, 30282 else => unreachable, 30283 }; 30284 if (src_field_count > dest_field_count) return error.NotCoercible; 30285 30286 var runtime_src: ?LazySrcLoc = null; 30287 for (0..dest_field_count) |field_index_usize| { 30288 const field_i = @as(u32, @intCast(field_index_usize)); 30289 const field_src = inst_src; // TODO better source location 30290 // https://github.com/ziglang/zig/issues/15709 30291 const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { 30292 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) 30293 anon_struct_type.names[field_i] 30294 else 30295 try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), 30296 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i], 30297 else => unreachable, 30298 }; 30299 30300 if (ip.stringEqlSlice(field_name, "len")) 30301 return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{}); 30302 30303 const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { 30304 .anon_struct_type => |anon_struct_type| anon_struct_type.types[field_index_usize].toType(), 30305 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].ty, 30306 else => unreachable, 30307 }; 30308 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 30309 .anon_struct_type => |anon_struct_type| anon_struct_type.values[field_index_usize], 30310 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].default_val, 30311 else => unreachable, 30312 }; 30313 30314 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src); 30315 30316 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); 30317 const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); 30318 field_refs[field_index] = coerced; 30319 if (default_val != .none) { 30320 const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { 30321 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); 30322 }; 30323 30324 if (!init_val.eql(default_val.toValue(), field_ty, sema.mod)) { 30325 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); 30326 } 30327 } 30328 if (runtime_src == null) { 30329 if (try sema.resolveMaybeUndefVal(coerced)) |field_val| { 30330 field_vals[field_index] = field_val.toIntern(); 30331 } else { 30332 runtime_src = field_src; 30333 } 30334 } 30335 } 30336 30337 // Populate default field values and report errors for missing fields. 30338 var root_msg: ?*Module.ErrorMsg = null; 30339 errdefer if (root_msg) |msg| msg.destroy(sema.gpa); 30340 30341 for (field_refs, 0..) |*field_ref, i| { 30342 if (field_ref.* != .none) continue; 30343 30344 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { 30345 .anon_struct_type => |anon_struct_type| anon_struct_type.values[i], 30346 .struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[i].default_val, 30347 else => unreachable, 30348 }; 30349 30350 const field_src = inst_src; // TODO better source location 30351 if (default_val == .none) { 30352 if (tuple_ty.isTuple(mod)) { 30353 const template = "missing tuple field: {d}"; 30354 if (root_msg) |msg| { 30355 try sema.errNote(block, field_src, msg, template, .{i}); 30356 } else { 30357 root_msg = try sema.errMsg(block, field_src, template, .{i}); 30358 } 30359 continue; 30360 } 30361 const template = "missing struct field: {}"; 30362 const args = .{tuple_ty.structFieldName(i, mod).fmt(ip)}; 30363 if (root_msg) |msg| { 30364 try sema.errNote(block, field_src, msg, template, args); 30365 } else { 30366 root_msg = try sema.errMsg(block, field_src, template, args); 30367 } 30368 continue; 30369 } 30370 if (runtime_src == null) { 30371 field_vals[i] = default_val; 30372 } else { 30373 field_ref.* = try sema.addConstant(default_val.toValue()); 30374 } 30375 } 30376 30377 if (root_msg) |msg| { 30378 try sema.addDeclaredHereNote(msg, tuple_ty); 30379 root_msg = null; 30380 return sema.failWithOwnedErrorMsg(msg); 30381 } 30382 30383 if (runtime_src) |rs| { 30384 try sema.requireRuntimeBlock(block, inst_src, rs); 30385 return block.addAggregateInit(tuple_ty, field_refs); 30386 } 30387 30388 return sema.addConstant( 30389 (try mod.intern(.{ .aggregate = .{ 30390 .ty = tuple_ty.toIntern(), 30391 .storage = .{ .elems = field_vals }, 30392 } })).toValue(), 30393 ); 30394 } 30395 30396 fn analyzeDeclVal( 30397 sema: *Sema, 30398 block: *Block, 30399 src: LazySrcLoc, 30400 decl_index: Decl.Index, 30401 ) CompileError!Air.Inst.Ref { 30402 try sema.addReferencedBy(block, src, decl_index); 30403 if (sema.decl_val_table.get(decl_index)) |result| { 30404 return result; 30405 } 30406 const decl_ref = try sema.analyzeDeclRefInner(decl_index, false); 30407 const result = try sema.analyzeLoad(block, src, decl_ref, src); 30408 if (Air.refToInterned(result) != null) { 30409 if (!block.is_typeof) { 30410 try sema.decl_val_table.put(sema.gpa, decl_index, result); 30411 } 30412 } 30413 return result; 30414 } 30415 30416 fn addReferencedBy( 30417 sema: *Sema, 30418 block: *Block, 30419 src: LazySrcLoc, 30420 decl_index: Decl.Index, 30421 ) !void { 30422 if (sema.mod.comp.reference_trace == @as(u32, 0)) return; 30423 try sema.mod.reference_table.put(sema.gpa, decl_index, .{ 30424 .referencer = block.src_decl, 30425 .src = src, 30426 }); 30427 } 30428 30429 fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void { 30430 const mod = sema.mod; 30431 const decl = mod.declPtr(decl_index); 30432 if (decl.analysis == .in_progress) { 30433 const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(mod), "dependency loop detected", .{}); 30434 return sema.failWithOwnedErrorMsg(msg); 30435 } 30436 30437 mod.ensureDeclAnalyzed(decl_index) catch |err| { 30438 if (sema.owner_func) |owner_func| { 30439 owner_func.state = .dependency_failure; 30440 } else { 30441 sema.owner_decl.analysis = .dependency_failure; 30442 } 30443 return err; 30444 }; 30445 } 30446 30447 fn ensureFuncBodyAnalyzed(sema: *Sema, func: Module.Fn.Index) CompileError!void { 30448 sema.mod.ensureFuncBodyAnalyzed(func) catch |err| { 30449 if (sema.owner_func) |owner_func| { 30450 owner_func.state = .dependency_failure; 30451 } else { 30452 sema.owner_decl.analysis = .dependency_failure; 30453 } 30454 return err; 30455 }; 30456 } 30457 30458 fn refValue(sema: *Sema, block: *Block, ty: Type, val: Value) !Value { 30459 const mod = sema.mod; 30460 var anon_decl = try block.startAnonDecl(); 30461 defer anon_decl.deinit(); 30462 const decl = try anon_decl.finish( 30463 ty, 30464 val, 30465 .none, // default alignment 30466 ); 30467 try sema.maybeQueueFuncBodyAnalysis(decl); 30468 try mod.declareDeclDependency(sema.owner_decl_index, decl); 30469 const result = try mod.intern(.{ .ptr = .{ 30470 .ty = (try mod.singleConstPtrType(ty)).toIntern(), 30471 .addr = .{ .decl = decl }, 30472 } }); 30473 return result.toValue(); 30474 } 30475 30476 fn optRefValue(sema: *Sema, block: *Block, ty: Type, opt_val: ?Value) !Value { 30477 const mod = sema.mod; 30478 const ptr_anyopaque_ty = try mod.singleConstPtrType(Type.anyopaque); 30479 return (try mod.intern(.{ .opt = .{ 30480 .ty = (try mod.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(), 30481 .val = if (opt_val) |val| (try mod.getCoerced( 30482 try sema.refValue(block, ty, val), 30483 ptr_anyopaque_ty, 30484 )).toIntern() else .none, 30485 } })).toValue(); 30486 } 30487 30488 fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref { 30489 return sema.analyzeDeclRefInner(decl_index, true); 30490 } 30491 30492 /// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but 30493 /// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a 30494 /// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps 30495 /// this function with `analyze_fn_body` set to true. 30496 fn analyzeDeclRefInner(sema: *Sema, decl_index: Decl.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref { 30497 const mod = sema.mod; 30498 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 30499 try sema.ensureDeclAnalyzed(decl_index); 30500 30501 const decl = mod.declPtr(decl_index); 30502 const decl_tv = try decl.typedValue(); 30503 const ptr_ty = try mod.ptrType(.{ 30504 .child = decl_tv.ty.toIntern(), 30505 .flags = .{ 30506 .alignment = decl.alignment, 30507 .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true, 30508 .address_space = decl.@"addrspace", 30509 }, 30510 }); 30511 if (analyze_fn_body) { 30512 try sema.maybeQueueFuncBodyAnalysis(decl_index); 30513 } 30514 return sema.addConstant((try mod.intern(.{ .ptr = .{ 30515 .ty = ptr_ty.toIntern(), 30516 .addr = .{ .decl = decl_index }, 30517 } })).toValue()); 30518 } 30519 30520 fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: Decl.Index) !void { 30521 const mod = sema.mod; 30522 const decl = mod.declPtr(decl_index); 30523 const tv = try decl.typedValue(); 30524 if (tv.ty.zigTypeTag(mod) != .Fn) return; 30525 if (!try sema.fnHasRuntimeBits(tv.ty)) return; 30526 const func_index = mod.intern_pool.indexToFunc(tv.val.toIntern()).unwrap() orelse return; // undef or extern_fn 30527 try mod.ensureFuncBodyAnalysisQueued(func_index); 30528 } 30529 30530 fn analyzeRef( 30531 sema: *Sema, 30532 block: *Block, 30533 src: LazySrcLoc, 30534 operand: Air.Inst.Ref, 30535 ) CompileError!Air.Inst.Ref { 30536 const mod = sema.mod; 30537 const operand_ty = sema.typeOf(operand); 30538 30539 if (try sema.resolveMaybeUndefVal(operand)) |val| { 30540 switch (mod.intern_pool.indexToKey(val.toIntern())) { 30541 .extern_func => |extern_func| return sema.analyzeDeclRef(extern_func.decl), 30542 .func => |func| return sema.analyzeDeclRef(mod.funcPtr(func.index).owner_decl), 30543 else => {}, 30544 } 30545 var anon_decl = try block.startAnonDecl(); 30546 defer anon_decl.deinit(); 30547 return sema.analyzeDeclRef(try anon_decl.finish( 30548 operand_ty, 30549 val, 30550 .none, // default alignment 30551 )); 30552 } 30553 30554 try sema.requireRuntimeBlock(block, src, null); 30555 const address_space = target_util.defaultAddressSpace(mod.getTarget(), .local); 30556 const ptr_type = try mod.ptrType(.{ 30557 .child = operand_ty.toIntern(), 30558 .flags = .{ 30559 .is_const = true, 30560 .address_space = address_space, 30561 }, 30562 }); 30563 const mut_ptr_type = try mod.ptrType(.{ 30564 .child = operand_ty.toIntern(), 30565 .flags = .{ .address_space = address_space }, 30566 }); 30567 const alloc = try block.addTy(.alloc, mut_ptr_type); 30568 try sema.storePtr(block, src, alloc, operand); 30569 30570 // TODO: Replace with sema.coerce when that supports adding pointer constness. 30571 return sema.bitCast(block, ptr_type, alloc, src, null); 30572 } 30573 30574 fn analyzeLoad( 30575 sema: *Sema, 30576 block: *Block, 30577 src: LazySrcLoc, 30578 ptr: Air.Inst.Ref, 30579 ptr_src: LazySrcLoc, 30580 ) CompileError!Air.Inst.Ref { 30581 const mod = sema.mod; 30582 const ptr_ty = sema.typeOf(ptr); 30583 const elem_ty = switch (ptr_ty.zigTypeTag(mod)) { 30584 .Pointer => ptr_ty.childType(mod), 30585 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}), 30586 }; 30587 30588 if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| { 30589 return sema.addConstant(opv); 30590 } 30591 30592 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { 30593 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| { 30594 return sema.addConstant(elem_val); 30595 } 30596 } 30597 30598 if (ptr_ty.ptrInfo(mod).flags.vector_index == .runtime) { 30599 const ptr_inst = Air.refToIndex(ptr).?; 30600 const air_tags = sema.air_instructions.items(.tag); 30601 if (air_tags[ptr_inst] == .ptr_elem_ptr) { 30602 const ty_pl = sema.air_instructions.items(.data)[ptr_inst].ty_pl; 30603 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data; 30604 return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs); 30605 } 30606 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{}'", .{ 30607 ptr_ty.fmt(sema.mod), 30608 }); 30609 } 30610 30611 return block.addTyOp(.load, elem_ty, ptr); 30612 } 30613 30614 fn analyzeSlicePtr( 30615 sema: *Sema, 30616 block: *Block, 30617 slice_src: LazySrcLoc, 30618 slice: Air.Inst.Ref, 30619 slice_ty: Type, 30620 ) CompileError!Air.Inst.Ref { 30621 const mod = sema.mod; 30622 const result_ty = slice_ty.slicePtrFieldType(mod); 30623 if (try sema.resolveMaybeUndefVal(slice)) |val| { 30624 if (val.isUndef(mod)) return sema.addConstUndef(result_ty); 30625 return sema.addConstant(val.slicePtr(mod)); 30626 } 30627 try sema.requireRuntimeBlock(block, slice_src, null); 30628 return block.addTyOp(.slice_ptr, result_ty, slice); 30629 } 30630 30631 fn analyzeSliceLen( 30632 sema: *Sema, 30633 block: *Block, 30634 src: LazySrcLoc, 30635 slice_inst: Air.Inst.Ref, 30636 ) CompileError!Air.Inst.Ref { 30637 const mod = sema.mod; 30638 if (try sema.resolveMaybeUndefVal(slice_inst)) |slice_val| { 30639 if (slice_val.isUndef(mod)) { 30640 return sema.addConstUndef(Type.usize); 30641 } 30642 return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)); 30643 } 30644 try sema.requireRuntimeBlock(block, src, null); 30645 return block.addTyOp(.slice_len, Type.usize, slice_inst); 30646 } 30647 30648 fn analyzeIsNull( 30649 sema: *Sema, 30650 block: *Block, 30651 src: LazySrcLoc, 30652 operand: Air.Inst.Ref, 30653 invert_logic: bool, 30654 ) CompileError!Air.Inst.Ref { 30655 const mod = sema.mod; 30656 const result_ty = Type.bool; 30657 if (try sema.resolveMaybeUndefVal(operand)) |opt_val| { 30658 if (opt_val.isUndef(mod)) { 30659 return sema.addConstUndef(result_ty); 30660 } 30661 const is_null = opt_val.isNull(mod); 30662 const bool_value = if (invert_logic) !is_null else is_null; 30663 if (bool_value) { 30664 return Air.Inst.Ref.bool_true; 30665 } else { 30666 return Air.Inst.Ref.bool_false; 30667 } 30668 } 30669 30670 const inverted_non_null_res = if (invert_logic) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 30671 const operand_ty = sema.typeOf(operand); 30672 if (operand_ty.zigTypeTag(mod) == .Optional and operand_ty.optionalChild(mod).zigTypeTag(mod) == .NoReturn) { 30673 return inverted_non_null_res; 30674 } 30675 if (operand_ty.zigTypeTag(mod) != .Optional and !operand_ty.isPtrLikeOptional(mod)) { 30676 return inverted_non_null_res; 30677 } 30678 try sema.requireRuntimeBlock(block, src, null); 30679 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; 30680 return block.addUnOp(air_tag, operand); 30681 } 30682 30683 fn analyzePtrIsNonErrComptimeOnly( 30684 sema: *Sema, 30685 block: *Block, 30686 src: LazySrcLoc, 30687 operand: Air.Inst.Ref, 30688 ) CompileError!Air.Inst.Ref { 30689 const mod = sema.mod; 30690 const ptr_ty = sema.typeOf(operand); 30691 assert(ptr_ty.zigTypeTag(mod) == .Pointer); 30692 const child_ty = ptr_ty.childType(mod); 30693 30694 const child_tag = child_ty.zigTypeTag(mod); 30695 if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true; 30696 if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false; 30697 assert(child_tag == .ErrorUnion); 30698 30699 _ = block; 30700 _ = src; 30701 30702 return Air.Inst.Ref.none; 30703 } 30704 30705 fn analyzeIsNonErrComptimeOnly( 30706 sema: *Sema, 30707 block: *Block, 30708 src: LazySrcLoc, 30709 operand: Air.Inst.Ref, 30710 ) CompileError!Air.Inst.Ref { 30711 const mod = sema.mod; 30712 const operand_ty = sema.typeOf(operand); 30713 const ot = operand_ty.zigTypeTag(mod); 30714 if (ot != .ErrorSet and ot != .ErrorUnion) return Air.Inst.Ref.bool_true; 30715 if (ot == .ErrorSet) return Air.Inst.Ref.bool_false; 30716 assert(ot == .ErrorUnion); 30717 30718 const payload_ty = operand_ty.errorUnionPayload(mod); 30719 if (payload_ty.zigTypeTag(mod) == .NoReturn) { 30720 return Air.Inst.Ref.bool_false; 30721 } 30722 30723 if (Air.refToIndex(operand)) |operand_inst| { 30724 switch (sema.air_instructions.items(.tag)[operand_inst]) { 30725 .wrap_errunion_payload => return Air.Inst.Ref.bool_true, 30726 .wrap_errunion_err => return Air.Inst.Ref.bool_false, 30727 else => {}, 30728 } 30729 } else if (operand == .undef) { 30730 return sema.addConstUndef(Type.bool); 30731 } else if (@intFromEnum(operand) < InternPool.static_len) { 30732 // None of the ref tags can be errors. 30733 return Air.Inst.Ref.bool_true; 30734 } 30735 30736 const maybe_operand_val = try sema.resolveMaybeUndefVal(operand); 30737 30738 // exception if the error union error set is known to be empty, 30739 // we allow the comparison but always make it comptime-known. 30740 const set_ty = operand_ty.errorUnionSet(mod); 30741 switch (set_ty.toIntern()) { 30742 .anyerror_type => {}, 30743 else => switch (mod.intern_pool.indexToKey(set_ty.toIntern())) { 30744 .error_set_type => |error_set_type| { 30745 if (error_set_type.names.len == 0) return Air.Inst.Ref.bool_true; 30746 }, 30747 .inferred_error_set_type => |ies_index| blk: { 30748 // If the error set is empty, we must return a comptime true or false. 30749 // However we want to avoid unnecessarily resolving an inferred error set 30750 // in case it is already non-empty. 30751 const ies = mod.inferredErrorSetPtr(ies_index); 30752 if (ies.is_anyerror) break :blk; 30753 if (ies.errors.count() != 0) break :blk; 30754 if (maybe_operand_val == null) { 30755 // Try to avoid resolving inferred error set if possible. 30756 if (ies.errors.count() != 0) break :blk; 30757 if (ies.is_anyerror) break :blk; 30758 for (ies.inferred_error_sets.keys()) |other_ies_index| { 30759 if (ies_index == other_ies_index) continue; 30760 try sema.resolveInferredErrorSet(block, src, other_ies_index); 30761 const other_ies = mod.inferredErrorSetPtr(other_ies_index); 30762 if (other_ies.is_anyerror) { 30763 ies.is_anyerror = true; 30764 ies.is_resolved = true; 30765 break :blk; 30766 } 30767 30768 if (other_ies.errors.count() != 0) break :blk; 30769 } 30770 if (ies.func == sema.owner_func_index.unwrap()) { 30771 // We're checking the inferred errorset of the current function and none of 30772 // its child inferred error sets contained any errors meaning that any value 30773 // so far with this type can't contain errors either. 30774 return Air.Inst.Ref.bool_true; 30775 } 30776 try sema.resolveInferredErrorSet(block, src, ies_index); 30777 if (ies.is_anyerror) break :blk; 30778 if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true; 30779 } 30780 }, 30781 else => unreachable, 30782 }, 30783 } 30784 30785 if (maybe_operand_val) |err_union| { 30786 if (err_union.isUndef(mod)) { 30787 return sema.addConstUndef(Type.bool); 30788 } 30789 if (err_union.getErrorName(mod) == .none) { 30790 return Air.Inst.Ref.bool_true; 30791 } else { 30792 return Air.Inst.Ref.bool_false; 30793 } 30794 } 30795 return Air.Inst.Ref.none; 30796 } 30797 30798 fn analyzeIsNonErr( 30799 sema: *Sema, 30800 block: *Block, 30801 src: LazySrcLoc, 30802 operand: Air.Inst.Ref, 30803 ) CompileError!Air.Inst.Ref { 30804 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); 30805 if (result == .none) { 30806 try sema.requireRuntimeBlock(block, src, null); 30807 return block.addUnOp(.is_non_err, operand); 30808 } else { 30809 return result; 30810 } 30811 } 30812 30813 fn analyzePtrIsNonErr( 30814 sema: *Sema, 30815 block: *Block, 30816 src: LazySrcLoc, 30817 operand: Air.Inst.Ref, 30818 ) CompileError!Air.Inst.Ref { 30819 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand); 30820 if (result == .none) { 30821 try sema.requireRuntimeBlock(block, src, null); 30822 return block.addUnOp(.is_non_err_ptr, operand); 30823 } else { 30824 return result; 30825 } 30826 } 30827 30828 fn analyzeSlice( 30829 sema: *Sema, 30830 block: *Block, 30831 src: LazySrcLoc, 30832 ptr_ptr: Air.Inst.Ref, 30833 uncasted_start: Air.Inst.Ref, 30834 uncasted_end_opt: Air.Inst.Ref, 30835 sentinel_opt: Air.Inst.Ref, 30836 sentinel_src: LazySrcLoc, 30837 ptr_src: LazySrcLoc, 30838 start_src: LazySrcLoc, 30839 end_src: LazySrcLoc, 30840 by_length: bool, 30841 ) CompileError!Air.Inst.Ref { 30842 const mod = sema.mod; 30843 // Slice expressions can operate on a variable whose type is an array. This requires 30844 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. 30845 const ptr_ptr_ty = sema.typeOf(ptr_ptr); 30846 const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(mod)) { 30847 .Pointer => ptr_ptr_ty.childType(mod), 30848 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(mod)}), 30849 }; 30850 30851 var array_ty = ptr_ptr_child_ty; 30852 var slice_ty = ptr_ptr_ty; 30853 var ptr_or_slice = ptr_ptr; 30854 var elem_ty: Type = undefined; 30855 var ptr_sentinel: ?Value = null; 30856 switch (ptr_ptr_child_ty.zigTypeTag(mod)) { 30857 .Array => { 30858 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 30859 elem_ty = ptr_ptr_child_ty.childType(mod); 30860 }, 30861 .Pointer => switch (ptr_ptr_child_ty.ptrSize(mod)) { 30862 .One => { 30863 const double_child_ty = ptr_ptr_child_ty.childType(mod); 30864 if (double_child_ty.zigTypeTag(mod) == .Array) { 30865 ptr_sentinel = double_child_ty.sentinel(mod); 30866 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 30867 slice_ty = ptr_ptr_child_ty; 30868 array_ty = double_child_ty; 30869 elem_ty = double_child_ty.childType(mod); 30870 } else { 30871 return sema.fail(block, src, "slice of single-item pointer", .{}); 30872 } 30873 }, 30874 .Many, .C => { 30875 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 30876 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 30877 slice_ty = ptr_ptr_child_ty; 30878 array_ty = ptr_ptr_child_ty; 30879 elem_ty = ptr_ptr_child_ty.childType(mod); 30880 30881 if (ptr_ptr_child_ty.ptrSize(mod) == .C) { 30882 if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| { 30883 if (ptr_val.isNull(mod)) { 30884 return sema.fail(block, src, "slice of null pointer", .{}); 30885 } 30886 } 30887 } 30888 }, 30889 .Slice => { 30890 ptr_sentinel = ptr_ptr_child_ty.sentinel(mod); 30891 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); 30892 slice_ty = ptr_ptr_child_ty; 30893 array_ty = ptr_ptr_child_ty; 30894 elem_ty = ptr_ptr_child_ty.childType(mod); 30895 }, 30896 }, 30897 else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}), 30898 } 30899 30900 const ptr = if (slice_ty.isSlice(mod)) 30901 try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) 30902 else if (array_ty.zigTypeTag(mod) == .Array) ptr: { 30903 var manyptr_ty_key = mod.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type; 30904 assert(manyptr_ty_key.child == array_ty.toIntern()); 30905 assert(manyptr_ty_key.flags.size == .One); 30906 manyptr_ty_key.child = elem_ty.toIntern(); 30907 manyptr_ty_key.flags.size = .Many; 30908 break :ptr try sema.coerceCompatiblePtrs(block, try mod.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src); 30909 } else ptr_or_slice; 30910 30911 const start = try sema.coerce(block, Type.usize, uncasted_start, start_src); 30912 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src); 30913 const new_ptr_ty = sema.typeOf(new_ptr); 30914 30915 // true if and only if the end index of the slice, implicitly or explicitly, equals 30916 // the length of the underlying object being sliced. we might learn the length of the 30917 // underlying object because it is an array (which has the length in the type), or 30918 // we might learn of the length because it is a comptime-known slice value. 30919 var end_is_len = uncasted_end_opt == .none; 30920 const end = e: { 30921 if (array_ty.zigTypeTag(mod) == .Array) { 30922 const len_val = try mod.intValue(Type.usize, array_ty.arrayLen(mod)); 30923 30924 if (!end_is_len) { 30925 const end = if (by_length) end: { 30926 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 30927 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 30928 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); 30929 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 30930 if (try sema.resolveMaybeUndefVal(end)) |end_val| { 30931 const len_s_val = try mod.intValue( 30932 Type.usize, 30933 array_ty.arrayLenIncludingSentinel(mod), 30934 ); 30935 if (!(try sema.compareAll(end_val, .lte, len_s_val, Type.usize))) { 30936 const sentinel_label: []const u8 = if (array_ty.sentinel(mod) != null) 30937 " +1 (sentinel)" 30938 else 30939 ""; 30940 30941 return sema.fail( 30942 block, 30943 end_src, 30944 "end index {} out of bounds for array of length {}{s}", 30945 .{ 30946 end_val.fmtValue(Type.usize, mod), 30947 len_val.fmtValue(Type.usize, mod), 30948 sentinel_label, 30949 }, 30950 ); 30951 } 30952 30953 // end_is_len is only true if we are NOT using the sentinel 30954 // length. For sentinel-length, we don't want the type to 30955 // contain the sentinel. 30956 if (end_val.eql(len_val, Type.usize, mod)) { 30957 end_is_len = true; 30958 } 30959 } 30960 break :e end; 30961 } 30962 30963 break :e try sema.addConstant(len_val); 30964 } else if (slice_ty.isSlice(mod)) { 30965 if (!end_is_len) { 30966 const end = if (by_length) end: { 30967 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 30968 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 30969 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); 30970 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 30971 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 30972 if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| { 30973 if (slice_val.isUndef(mod)) { 30974 return sema.fail(block, src, "slice of undefined", .{}); 30975 } 30976 const has_sentinel = slice_ty.sentinel(mod) != null; 30977 const slice_len = slice_val.sliceLen(mod); 30978 const len_plus_sent = slice_len + @intFromBool(has_sentinel); 30979 const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent); 30980 if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) { 30981 const sentinel_label: []const u8 = if (has_sentinel) 30982 " +1 (sentinel)" 30983 else 30984 ""; 30985 30986 return sema.fail( 30987 block, 30988 end_src, 30989 "end index {} out of bounds for slice of length {d}{s}", 30990 .{ 30991 end_val.fmtValue(Type.usize, mod), 30992 slice_val.sliceLen(mod), 30993 sentinel_label, 30994 }, 30995 ); 30996 } 30997 30998 // If the slice has a sentinel, we consider end_is_len 30999 // is only true if it equals the length WITHOUT the 31000 // sentinel, so we don't add a sentinel type. 31001 const slice_len_val = try mod.intValue(Type.usize, slice_len); 31002 if (end_val.eql(slice_len_val, Type.usize, mod)) { 31003 end_is_len = true; 31004 } 31005 } 31006 } 31007 break :e end; 31008 } 31009 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); 31010 } 31011 if (!end_is_len) { 31012 if (by_length) { 31013 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 31014 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); 31015 break :e try sema.coerce(block, Type.usize, uncasted_end, end_src); 31016 } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); 31017 } 31018 return sema.fail(block, src, "slice of pointer must include end value", .{}); 31019 }; 31020 31021 const sentinel = s: { 31022 if (sentinel_opt != .none) { 31023 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); 31024 break :s try sema.resolveConstValue(block, sentinel_src, casted, "slice sentinel must be comptime-known"); 31025 } 31026 // If we are slicing to the end of something that is sentinel-terminated 31027 // then the resulting slice type is also sentinel-terminated. 31028 if (end_is_len) { 31029 if (ptr_sentinel) |sent| { 31030 break :s sent; 31031 } 31032 } 31033 break :s null; 31034 }; 31035 const slice_sentinel = if (sentinel_opt != .none) sentinel else null; 31036 31037 var checked_start_lte_end = by_length; 31038 var runtime_src: ?LazySrcLoc = null; 31039 31040 // requirement: start <= end 31041 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { 31042 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { 31043 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) { 31044 return sema.fail( 31045 block, 31046 start_src, 31047 "start index {} is larger than end index {}", 31048 .{ 31049 start_val.fmtValue(Type.usize, mod), 31050 end_val.fmtValue(Type.usize, mod), 31051 }, 31052 ); 31053 } 31054 checked_start_lte_end = true; 31055 if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: { 31056 const expected_sentinel = sentinel orelse break :sentinel_check; 31057 const start_int = start_val.getUnsignedInt(mod).?; 31058 const end_int = end_val.getUnsignedInt(mod).?; 31059 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); 31060 31061 const many_ptr_ty = try mod.manyConstPtrType(elem_ty); 31062 const many_ptr_val = try mod.getCoerced(ptr_val, many_ptr_ty); 31063 const elem_ptr_ty = try mod.singleConstPtrType(elem_ty); 31064 const elem_ptr = try many_ptr_val.elemPtr(elem_ptr_ty, sentinel_index, mod); 31065 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty); 31066 const actual_sentinel = switch (res) { 31067 .runtime_load => break :sentinel_check, 31068 .val => |v| v, 31069 .needed_well_defined => |ty| return sema.fail( 31070 block, 31071 src, 31072 "comptime dereference requires '{}' to have a well-defined layout, but it does not.", 31073 .{ty.fmt(mod)}, 31074 ), 31075 .out_of_bounds => |ty| return sema.fail( 31076 block, 31077 end_src, 31078 "slice end index {d} exceeds bounds of containing decl of type '{}'", 31079 .{ end_int, ty.fmt(mod) }, 31080 ), 31081 }; 31082 31083 if (!actual_sentinel.eql(expected_sentinel, elem_ty, mod)) { 31084 const msg = msg: { 31085 const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{}); 31086 errdefer msg.destroy(sema.gpa); 31087 try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{ 31088 expected_sentinel.fmtValue(elem_ty, mod), 31089 actual_sentinel.fmtValue(elem_ty, mod), 31090 }); 31091 31092 break :msg msg; 31093 }; 31094 return sema.failWithOwnedErrorMsg(msg); 31095 } 31096 } else { 31097 runtime_src = ptr_src; 31098 } 31099 } else { 31100 runtime_src = start_src; 31101 } 31102 } else { 31103 runtime_src = end_src; 31104 } 31105 31106 if (!checked_start_lte_end and block.wantSafety() and !block.is_comptime) { 31107 // requirement: start <= end 31108 assert(!block.is_comptime); 31109 try sema.requireRuntimeBlock(block, src, runtime_src.?); 31110 const ok = try block.addBinOp(.cmp_lte, start, end); 31111 if (!sema.mod.comp.formatted_panics) { 31112 try sema.addSafetyCheck(block, ok, .start_index_greater_than_end); 31113 } else { 31114 try sema.safetyCheckFormatted(block, ok, "panicStartGreaterThanEnd", &.{ start, end }); 31115 } 31116 } 31117 const new_len = if (by_length) 31118 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src) 31119 else 31120 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); 31121 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); 31122 31123 const new_ptr_ty_info = new_ptr_ty.ptrInfo(mod); 31124 const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(mod) != .C; 31125 31126 if (opt_new_len_val) |new_len_val| { 31127 const new_len_int = new_len_val.toUnsignedInt(mod); 31128 31129 const return_ty = try mod.ptrType(.{ 31130 .child = (try mod.arrayType(.{ 31131 .len = new_len_int, 31132 .sentinel = if (sentinel) |s| s.toIntern() else .none, 31133 .child = elem_ty.toIntern(), 31134 })).toIntern(), 31135 .flags = .{ 31136 .alignment = new_ptr_ty_info.flags.alignment, 31137 .is_const = new_ptr_ty_info.flags.is_const, 31138 .is_allowzero = new_allowzero, 31139 .is_volatile = new_ptr_ty_info.flags.is_volatile, 31140 .address_space = new_ptr_ty_info.flags.address_space, 31141 }, 31142 }); 31143 31144 const opt_new_ptr_val = try sema.resolveMaybeUndefVal(new_ptr); 31145 const new_ptr_val = opt_new_ptr_val orelse { 31146 const result = try block.addBitCast(return_ty, new_ptr); 31147 if (block.wantSafety()) { 31148 // requirement: slicing C ptr is non-null 31149 if (ptr_ptr_child_ty.isCPtr(mod)) { 31150 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); 31151 try sema.addSafetyCheck(block, is_non_null, .unwrap_null); 31152 } 31153 31154 if (slice_ty.isSlice(mod)) { 31155 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); 31156 const actual_len = if (slice_ty.sentinel(mod) == null) 31157 slice_len_inst 31158 else 31159 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); 31160 31161 const actual_end = if (slice_sentinel != null) 31162 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 31163 else 31164 end; 31165 31166 try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte); 31167 } 31168 31169 // requirement: result[new_len] == slice_sentinel 31170 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len); 31171 } 31172 return result; 31173 }; 31174 31175 if (!new_ptr_val.isUndef(mod)) { 31176 return sema.addConstant(try mod.getCoerced( 31177 (try new_ptr_val.intern(new_ptr_ty, mod)).toValue(), 31178 return_ty, 31179 )); 31180 } 31181 31182 // Special case: @as([]i32, undefined)[x..x] 31183 if (new_len_int == 0) { 31184 return sema.addConstUndef(return_ty); 31185 } 31186 31187 return sema.fail(block, src, "non-zero length slice of undefined pointer", .{}); 31188 } 31189 31190 const return_ty = try mod.ptrType(.{ 31191 .child = elem_ty.toIntern(), 31192 .sentinel = if (sentinel) |s| s.toIntern() else .none, 31193 .flags = .{ 31194 .size = .Slice, 31195 .alignment = new_ptr_ty_info.flags.alignment, 31196 .is_const = new_ptr_ty_info.flags.is_const, 31197 .is_volatile = new_ptr_ty_info.flags.is_volatile, 31198 .is_allowzero = new_allowzero, 31199 .address_space = new_ptr_ty_info.flags.address_space, 31200 }, 31201 }); 31202 31203 try sema.requireRuntimeBlock(block, src, runtime_src.?); 31204 if (block.wantSafety()) { 31205 // requirement: slicing C ptr is non-null 31206 if (ptr_ptr_child_ty.isCPtr(mod)) { 31207 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); 31208 try sema.addSafetyCheck(block, is_non_null, .unwrap_null); 31209 } 31210 31211 // requirement: end <= len 31212 const opt_len_inst = if (array_ty.zigTypeTag(mod) == .Array) 31213 try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel(mod)) 31214 else if (slice_ty.isSlice(mod)) blk: { 31215 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { 31216 // we don't need to add one for sentinels because the 31217 // underlying value data includes the sentinel 31218 break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)); 31219 } 31220 31221 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); 31222 if (slice_ty.sentinel(mod) == null) break :blk slice_len_inst; 31223 31224 // we have to add one because slice lengths don't include the sentinel 31225 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); 31226 } else null; 31227 if (opt_len_inst) |len_inst| { 31228 const actual_end = if (slice_sentinel != null) 31229 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) 31230 else 31231 end; 31232 try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte); 31233 } 31234 31235 // requirement: start <= end 31236 try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte); 31237 } 31238 const result = try block.addInst(.{ 31239 .tag = .slice, 31240 .data = .{ .ty_pl = .{ 31241 .ty = try sema.addType(return_ty), 31242 .payload = try sema.addExtra(Air.Bin{ 31243 .lhs = new_ptr, 31244 .rhs = new_len, 31245 }), 31246 } }, 31247 }); 31248 if (block.wantSafety()) { 31249 // requirement: result[new_len] == slice_sentinel 31250 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len); 31251 } 31252 return result; 31253 } 31254 31255 /// Asserts that lhs and rhs types are both numeric. 31256 fn cmpNumeric( 31257 sema: *Sema, 31258 block: *Block, 31259 src: LazySrcLoc, 31260 uncasted_lhs: Air.Inst.Ref, 31261 uncasted_rhs: Air.Inst.Ref, 31262 op: std.math.CompareOperator, 31263 lhs_src: LazySrcLoc, 31264 rhs_src: LazySrcLoc, 31265 ) CompileError!Air.Inst.Ref { 31266 const mod = sema.mod; 31267 const lhs_ty = sema.typeOf(uncasted_lhs); 31268 const rhs_ty = sema.typeOf(uncasted_rhs); 31269 31270 assert(lhs_ty.isNumeric(mod)); 31271 assert(rhs_ty.isNumeric(mod)); 31272 31273 const lhs_ty_tag = lhs_ty.zigTypeTag(mod); 31274 const rhs_ty_tag = rhs_ty.zigTypeTag(mod); 31275 const target = mod.getTarget(); 31276 31277 // One exception to heterogeneous comparison: comptime_float needs to 31278 // coerce to fixed-width float. 31279 31280 const lhs = if (lhs_ty_tag == .ComptimeFloat and rhs_ty_tag == .Float) 31281 try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src) 31282 else 31283 uncasted_lhs; 31284 31285 const rhs = if (lhs_ty_tag == .Float and rhs_ty_tag == .ComptimeFloat) 31286 try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src) 31287 else 31288 uncasted_rhs; 31289 31290 const runtime_src: LazySrcLoc = src: { 31291 if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| { 31292 if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { 31293 // Compare ints: const vs. undefined (or vice versa) 31294 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)) { 31295 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| { 31296 return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 31297 } 31298 } 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)) { 31299 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| { 31300 return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 31301 } 31302 } 31303 31304 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 31305 return sema.addConstUndef(Type.bool); 31306 } 31307 if (lhs_val.isNan(mod) or rhs_val.isNan(mod)) { 31308 if (op == std.math.CompareOperator.neq) { 31309 return Air.Inst.Ref.bool_true; 31310 } else { 31311 return Air.Inst.Ref.bool_false; 31312 } 31313 } 31314 if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, mod, sema)) { 31315 return Air.Inst.Ref.bool_true; 31316 } else { 31317 return Air.Inst.Ref.bool_false; 31318 } 31319 } else { 31320 if (!lhs_val.isUndef(mod) and (lhs_ty.isInt(mod) or lhs_ty_tag == .ComptimeInt) and rhs_ty.isInt(mod)) { 31321 // Compare ints: const vs. var 31322 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(lhs_val), op, rhs_ty)) |res| { 31323 return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 31324 } 31325 } 31326 break :src rhs_src; 31327 } 31328 } else { 31329 if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| { 31330 if (!rhs_val.isUndef(mod) and (rhs_ty.isInt(mod) or rhs_ty_tag == .ComptimeInt) and lhs_ty.isInt(mod)) { 31331 // Compare ints: var vs. const 31332 if (try sema.compareIntsOnlyPossibleResult(try sema.resolveLazyValue(rhs_val), op.reverse(), lhs_ty)) |res| { 31333 return if (res) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false; 31334 } 31335 } 31336 } 31337 break :src lhs_src; 31338 } 31339 }; 31340 31341 // TODO handle comparisons against lazy zero values 31342 // Some values can be compared against zero without being runtime-known or without forcing 31343 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to 31344 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout 31345 // of this function if we don't need to. 31346 try sema.requireRuntimeBlock(block, src, runtime_src); 31347 31348 // For floats, emit a float comparison instruction. 31349 const lhs_is_float = switch (lhs_ty_tag) { 31350 .Float, .ComptimeFloat => true, 31351 else => false, 31352 }; 31353 const rhs_is_float = switch (rhs_ty_tag) { 31354 .Float, .ComptimeFloat => true, 31355 else => false, 31356 }; 31357 31358 if (lhs_is_float and rhs_is_float) { 31359 // Smaller fixed-width floats coerce to larger fixed-width floats. 31360 // comptime_float coerces to fixed-width float. 31361 const dest_ty = x: { 31362 if (lhs_ty_tag == .ComptimeFloat) { 31363 break :x rhs_ty; 31364 } else if (rhs_ty_tag == .ComptimeFloat) { 31365 break :x lhs_ty; 31366 } 31367 if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) { 31368 break :x lhs_ty; 31369 } else { 31370 break :x rhs_ty; 31371 } 31372 }; 31373 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 31374 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 31375 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs); 31376 } 31377 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. 31378 // For mixed signed and unsigned integers, implicit cast both operands to a signed 31379 // integer with + 1 bit. 31380 // For mixed floats and integers, extract the integer part from the float, cast that to 31381 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, 31382 // add/subtract 1. 31383 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| 31384 !(try lhs_val.compareAllWithZeroAdvanced(.gte, sema)) 31385 else 31386 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(mod)); 31387 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| 31388 !(try rhs_val.compareAllWithZeroAdvanced(.gte, sema)) 31389 else 31390 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(mod)); 31391 const dest_int_is_signed = lhs_is_signed or rhs_is_signed; 31392 31393 var dest_float_type: ?Type = null; 31394 31395 var lhs_bits: usize = undefined; 31396 if (try sema.resolveMaybeUndefLazyVal(lhs)) |lhs_val| { 31397 if (lhs_val.isUndef(mod)) 31398 return sema.addConstUndef(Type.bool); 31399 if (lhs_val.isNan(mod)) switch (op) { 31400 .neq => return Air.Inst.Ref.bool_true, 31401 else => return Air.Inst.Ref.bool_false, 31402 }; 31403 if (lhs_val.isInf(mod)) switch (op) { 31404 .neq => return Air.Inst.Ref.bool_true, 31405 .eq => return Air.Inst.Ref.bool_false, 31406 .gt, .gte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true, 31407 .lt, .lte => return if (lhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false, 31408 }; 31409 if (!rhs_is_signed) { 31410 switch (lhs_val.orderAgainstZero(mod)) { 31411 .gt => {}, 31412 .eq => switch (op) { // LHS = 0, RHS is unsigned 31413 .lte => return Air.Inst.Ref.bool_true, 31414 .gt => return Air.Inst.Ref.bool_false, 31415 else => {}, 31416 }, 31417 .lt => switch (op) { // LHS < 0, RHS is unsigned 31418 .neq, .lt, .lte => return Air.Inst.Ref.bool_true, 31419 .eq, .gt, .gte => return Air.Inst.Ref.bool_false, 31420 }, 31421 } 31422 } 31423 if (lhs_is_float) { 31424 if (lhs_val.floatHasFraction(mod)) { 31425 switch (op) { 31426 .eq => return Air.Inst.Ref.bool_false, 31427 .neq => return Air.Inst.Ref.bool_true, 31428 else => {}, 31429 } 31430 } 31431 31432 var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, mod)); 31433 defer bigint.deinit(); 31434 if (lhs_val.floatHasFraction(mod)) { 31435 if (lhs_is_signed) { 31436 try bigint.addScalar(&bigint, -1); 31437 } else { 31438 try bigint.addScalar(&bigint, 1); 31439 } 31440 } 31441 lhs_bits = bigint.toConst().bitCountTwosComp(); 31442 } else { 31443 lhs_bits = lhs_val.intBitCountTwosComp(mod); 31444 } 31445 lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed); 31446 } else if (lhs_is_float) { 31447 dest_float_type = lhs_ty; 31448 } else { 31449 const int_info = lhs_ty.intInfo(mod); 31450 lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 31451 } 31452 31453 var rhs_bits: usize = undefined; 31454 if (try sema.resolveMaybeUndefLazyVal(rhs)) |rhs_val| { 31455 if (rhs_val.isUndef(mod)) 31456 return sema.addConstUndef(Type.bool); 31457 if (rhs_val.isNan(mod)) switch (op) { 31458 .neq => return Air.Inst.Ref.bool_true, 31459 else => return Air.Inst.Ref.bool_false, 31460 }; 31461 if (rhs_val.isInf(mod)) switch (op) { 31462 .neq => return Air.Inst.Ref.bool_true, 31463 .eq => return Air.Inst.Ref.bool_false, 31464 .gt, .gte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false, 31465 .lt, .lte => return if (rhs_val.isNegativeInf(mod)) Air.Inst.Ref.bool_false else Air.Inst.Ref.bool_true, 31466 }; 31467 if (!lhs_is_signed) { 31468 switch (rhs_val.orderAgainstZero(mod)) { 31469 .gt => {}, 31470 .eq => switch (op) { // RHS = 0, LHS is unsigned 31471 .gte => return Air.Inst.Ref.bool_true, 31472 .lt => return Air.Inst.Ref.bool_false, 31473 else => {}, 31474 }, 31475 .lt => switch (op) { // RHS < 0, LHS is unsigned 31476 .neq, .gt, .gte => return Air.Inst.Ref.bool_true, 31477 .eq, .lt, .lte => return Air.Inst.Ref.bool_false, 31478 }, 31479 } 31480 } 31481 if (rhs_is_float) { 31482 if (rhs_val.floatHasFraction(mod)) { 31483 switch (op) { 31484 .eq => return Air.Inst.Ref.bool_false, 31485 .neq => return Air.Inst.Ref.bool_true, 31486 else => {}, 31487 } 31488 } 31489 31490 var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, mod)); 31491 defer bigint.deinit(); 31492 if (rhs_val.floatHasFraction(mod)) { 31493 if (rhs_is_signed) { 31494 try bigint.addScalar(&bigint, -1); 31495 } else { 31496 try bigint.addScalar(&bigint, 1); 31497 } 31498 } 31499 rhs_bits = bigint.toConst().bitCountTwosComp(); 31500 } else { 31501 rhs_bits = rhs_val.intBitCountTwosComp(mod); 31502 } 31503 rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed); 31504 } else if (rhs_is_float) { 31505 dest_float_type = rhs_ty; 31506 } else { 31507 const int_info = rhs_ty.intInfo(mod); 31508 rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); 31509 } 31510 31511 const dest_ty = if (dest_float_type) |ft| ft else blk: { 31512 const max_bits = @max(lhs_bits, rhs_bits); 31513 const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}); 31514 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned; 31515 break :blk try mod.intType(signedness, casted_bits); 31516 }; 31517 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); 31518 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); 31519 31520 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .Optimized), casted_lhs, casted_rhs); 31521 } 31522 31523 /// Asserts that LHS value is an int or comptime int and not undefined, and 31524 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to 31525 /// determine whether `op` has a guaranteed result. 31526 /// If it cannot be determined, returns null. 31527 /// Otherwise returns a bool for the guaranteed comparison operation. 31528 fn compareIntsOnlyPossibleResult( 31529 sema: *Sema, 31530 lhs_val: Value, 31531 op: std.math.CompareOperator, 31532 rhs_ty: Type, 31533 ) Allocator.Error!?bool { 31534 const mod = sema.mod; 31535 const rhs_info = rhs_ty.intInfo(mod); 31536 const vs_zero = lhs_val.orderAgainstZeroAdvanced(mod, sema) catch unreachable; 31537 const is_zero = vs_zero == .eq; 31538 const is_negative = vs_zero == .lt; 31539 const is_positive = vs_zero == .gt; 31540 31541 // Anything vs. zero-sized type has guaranteed outcome. 31542 if (rhs_info.bits == 0) return switch (op) { 31543 .eq, .lte, .gte => is_zero, 31544 .neq, .lt, .gt => !is_zero, 31545 }; 31546 31547 // Special case for i1, which can only be 0 or -1. 31548 // Zero and positive ints have guaranteed outcome. 31549 if (rhs_info.bits == 1 and rhs_info.signedness == .signed) { 31550 if (is_positive) return switch (op) { 31551 .gt, .gte, .neq => true, 31552 .lt, .lte, .eq => false, 31553 }; 31554 if (is_zero) return switch (op) { 31555 .gte => true, 31556 .lt => false, 31557 .gt, .lte, .eq, .neq => null, 31558 }; 31559 } 31560 31561 // Negative vs. unsigned has guaranteed outcome. 31562 if (rhs_info.signedness == .unsigned and is_negative) return switch (op) { 31563 .eq, .gt, .gte => false, 31564 .neq, .lt, .lte => true, 31565 }; 31566 31567 const sign_adj = @intFromBool(!is_negative and rhs_info.signedness == .signed); 31568 const req_bits = lhs_val.intBitCountTwosComp(mod) + sign_adj; 31569 31570 // No sized type can have more than 65535 bits. 31571 // The RHS type operand is either a runtime value or sized (but undefined) constant. 31572 if (req_bits > 65535) return switch (op) { 31573 .lt, .lte => is_negative, 31574 .gt, .gte => is_positive, 31575 .eq => false, 31576 .neq => true, 31577 }; 31578 const fits = req_bits <= rhs_info.bits; 31579 31580 // Oversized int has guaranteed outcome. 31581 switch (op) { 31582 .eq => return if (!fits) false else null, 31583 .neq => return if (!fits) true else null, 31584 .lt, .lte => if (!fits) return is_negative, 31585 .gt, .gte => if (!fits) return !is_negative, 31586 } 31587 31588 // For any other comparison, we need to know if the LHS value is 31589 // equal to the maximum or minimum possible value of the RHS type. 31590 const edge: struct { min: bool, max: bool } = edge: { 31591 if (is_zero and rhs_info.signedness == .unsigned) break :edge .{ 31592 .min = true, 31593 .max = false, 31594 }; 31595 31596 if (req_bits != rhs_info.bits) break :edge .{ 31597 .min = false, 31598 .max = false, 31599 }; 31600 31601 const ty = try mod.intType( 31602 if (is_negative) .signed else .unsigned, 31603 @as(u16, @intCast(req_bits)), 31604 ); 31605 const pop_count = lhs_val.popCount(ty, mod); 31606 31607 if (is_negative) { 31608 break :edge .{ 31609 .min = pop_count == 1, 31610 .max = false, 31611 }; 31612 } else { 31613 break :edge .{ 31614 .min = false, 31615 .max = pop_count == req_bits - sign_adj, 31616 }; 31617 } 31618 }; 31619 31620 assert(fits); 31621 return switch (op) { 31622 .lt => if (edge.max) false else null, 31623 .lte => if (edge.min) true else null, 31624 .gt => if (edge.min) false else null, 31625 .gte => if (edge.max) true else null, 31626 .eq, .neq => unreachable, 31627 }; 31628 } 31629 31630 /// Asserts that lhs and rhs types are both vectors. 31631 fn cmpVector( 31632 sema: *Sema, 31633 block: *Block, 31634 src: LazySrcLoc, 31635 lhs: Air.Inst.Ref, 31636 rhs: Air.Inst.Ref, 31637 op: std.math.CompareOperator, 31638 lhs_src: LazySrcLoc, 31639 rhs_src: LazySrcLoc, 31640 ) CompileError!Air.Inst.Ref { 31641 const mod = sema.mod; 31642 const lhs_ty = sema.typeOf(lhs); 31643 const rhs_ty = sema.typeOf(rhs); 31644 assert(lhs_ty.zigTypeTag(mod) == .Vector); 31645 assert(rhs_ty.zigTypeTag(mod) == .Vector); 31646 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); 31647 31648 const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } }); 31649 const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src); 31650 const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src); 31651 31652 const result_ty = try mod.vectorType(.{ 31653 .len = lhs_ty.vectorLen(mod), 31654 .child = .bool_type, 31655 }); 31656 31657 const runtime_src: LazySrcLoc = src: { 31658 if (try sema.resolveMaybeUndefVal(casted_lhs)) |lhs_val| { 31659 if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| { 31660 if (lhs_val.isUndef(mod) or rhs_val.isUndef(mod)) { 31661 return sema.addConstUndef(result_ty); 31662 } 31663 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty); 31664 return sema.addConstant(cmp_val); 31665 } else { 31666 break :src rhs_src; 31667 } 31668 } else { 31669 break :src lhs_src; 31670 } 31671 }; 31672 31673 try sema.requireRuntimeBlock(block, src, runtime_src); 31674 return block.addCmpVector(casted_lhs, casted_rhs, op); 31675 } 31676 31677 fn wrapOptional( 31678 sema: *Sema, 31679 block: *Block, 31680 dest_ty: Type, 31681 inst: Air.Inst.Ref, 31682 inst_src: LazySrcLoc, 31683 ) !Air.Inst.Ref { 31684 if (try sema.resolveMaybeUndefVal(inst)) |val| { 31685 return sema.addConstant((try sema.mod.intern(.{ .opt = .{ 31686 .ty = dest_ty.toIntern(), 31687 .val = val.toIntern(), 31688 } })).toValue()); 31689 } 31690 31691 try sema.requireRuntimeBlock(block, inst_src, null); 31692 return block.addTyOp(.wrap_optional, dest_ty, inst); 31693 } 31694 31695 fn wrapErrorUnionPayload( 31696 sema: *Sema, 31697 block: *Block, 31698 dest_ty: Type, 31699 inst: Air.Inst.Ref, 31700 inst_src: LazySrcLoc, 31701 ) !Air.Inst.Ref { 31702 const mod = sema.mod; 31703 const dest_payload_ty = dest_ty.errorUnionPayload(mod); 31704 const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); 31705 if (try sema.resolveMaybeUndefVal(coerced)) |val| { 31706 return sema.addConstant((try mod.intern(.{ .error_union = .{ 31707 .ty = dest_ty.toIntern(), 31708 .val = .{ .payload = try val.intern(dest_payload_ty, mod) }, 31709 } })).toValue()); 31710 } 31711 try sema.requireRuntimeBlock(block, inst_src, null); 31712 try sema.queueFullTypeResolution(dest_payload_ty); 31713 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); 31714 } 31715 31716 fn wrapErrorUnionSet( 31717 sema: *Sema, 31718 block: *Block, 31719 dest_ty: Type, 31720 inst: Air.Inst.Ref, 31721 inst_src: LazySrcLoc, 31722 ) !Air.Inst.Ref { 31723 const mod = sema.mod; 31724 const ip = &mod.intern_pool; 31725 const inst_ty = sema.typeOf(inst); 31726 const dest_err_set_ty = dest_ty.errorUnionSet(mod); 31727 if (try sema.resolveMaybeUndefVal(inst)) |val| { 31728 switch (dest_err_set_ty.toIntern()) { 31729 .anyerror_type => {}, 31730 else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) { 31731 .error_set_type => |error_set_type| ok: { 31732 const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; 31733 if (error_set_type.nameIndex(ip, expected_name) != null) break :ok; 31734 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 31735 }, 31736 .inferred_error_set_type => |ies_index| ok: { 31737 const ies = mod.inferredErrorSetPtr(ies_index); 31738 const expected_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; 31739 31740 // We carefully do this in an order that avoids unnecessarily 31741 // resolving the destination error set type. 31742 if (ies.is_anyerror) break :ok; 31743 31744 if (ies.errors.contains(expected_name)) break :ok; 31745 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) break :ok; 31746 31747 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty); 31748 }, 31749 else => unreachable, 31750 }, 31751 } 31752 return sema.addConstant((try mod.intern(.{ .error_union = .{ 31753 .ty = dest_ty.toIntern(), 31754 .val = .{ 31755 .err_name = mod.intern_pool.indexToKey(try val.intern(dest_err_set_ty, mod)).err.name, 31756 }, 31757 } })).toValue()); 31758 } 31759 31760 try sema.requireRuntimeBlock(block, inst_src, null); 31761 const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src); 31762 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); 31763 } 31764 31765 fn unionToTag( 31766 sema: *Sema, 31767 block: *Block, 31768 enum_ty: Type, 31769 un: Air.Inst.Ref, 31770 un_src: LazySrcLoc, 31771 ) !Air.Inst.Ref { 31772 const mod = sema.mod; 31773 if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| { 31774 return sema.addConstant(opv); 31775 } 31776 if (try sema.resolveMaybeUndefVal(un)) |un_val| { 31777 return sema.addConstant(un_val.unionTag(mod)); 31778 } 31779 try sema.requireRuntimeBlock(block, un_src, null); 31780 return block.addTyOp(.get_union_tag, enum_ty, un); 31781 } 31782 31783 const PeerResolveStrategy = enum { 31784 /// The type is not known. 31785 /// If refined no further, this is equivalent to `exact`. 31786 unknown, 31787 /// The type may be an error set or error union. 31788 /// If refined no further, it is an error set. 31789 error_set, 31790 /// The type must be some error union. 31791 error_union, 31792 /// The type may be @TypeOf(null), an optional or a C pointer. 31793 /// If refined no further, it is @TypeOf(null). 31794 nullable, 31795 /// The type must be some optional or a C pointer. 31796 /// If refined no further, it is an optional. 31797 optional, 31798 /// The type must be either an array or a vector. 31799 /// If refined no further, it is an array. 31800 array, 31801 /// The type must be a vector. 31802 vector, 31803 /// The type must be a C pointer. 31804 c_ptr, 31805 /// The type must be a pointer (C or not). 31806 /// If refined no further, it is a non-C pointer. 31807 ptr, 31808 /// The type must be a function or a pointer to a function. 31809 /// If refined no further, it is a function. 31810 func, 31811 /// The type must be an enum literal, or some specific enum or union. Which one is decided 31812 /// afterwards based on the types in question. 31813 enum_or_union, 31814 /// The type must be some integer or float type. 31815 /// If refined no further, it is `comptime_int`. 31816 comptime_int, 31817 /// The type must be some float type. 31818 /// If refined no further, it is `comptime_float`. 31819 comptime_float, 31820 /// The type must be some float or fixed-width integer type. 31821 /// If refined no further, it is some fixed-width integer type. 31822 fixed_int, 31823 /// The type must be some fixed-width float type. 31824 fixed_float, 31825 /// The type must be a struct literal or tuple type. 31826 coercible_struct, 31827 /// The peers must all be of the same type. 31828 exact, 31829 31830 /// Given two strategies, find a strategy that satisfies both, if one exists. If no such 31831 /// strategy exists, any strategy may be returned; an error will be emitted when the caller 31832 /// attempts to use the strategy to resolve the type. 31833 /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at 31834 /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy. 31835 fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy { 31836 // Our merging should be order-independent. Thus, even though the union order is arbitrary, 31837 // by sorting the tags and switching first on the smaller, we have half as many cases to 31838 // worry about (since we avoid the duplicates). 31839 const s0_is_a = @intFromEnum(a) <= @intFromEnum(b); 31840 const s0 = if (s0_is_a) a else b; 31841 const s1 = if (s0_is_a) b else a; 31842 31843 const ReasonMethod = enum { 31844 all_s0, 31845 all_s1, 31846 either, 31847 }; 31848 31849 const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) { 31850 .unknown => .{ .all_s1, s1 }, 31851 .error_set => switch (s1) { 31852 .error_set => .{ .either, .error_set }, 31853 else => .{ .all_s0, .error_union }, 31854 }, 31855 .error_union => switch (s1) { 31856 .error_union => .{ .either, .error_union }, 31857 else => .{ .all_s0, .error_union }, 31858 }, 31859 .nullable => switch (s1) { 31860 .nullable => .{ .either, .nullable }, 31861 .c_ptr => .{ .all_s1, .c_ptr }, 31862 else => .{ .all_s0, .optional }, 31863 }, 31864 .optional => switch (s1) { 31865 .optional => .{ .either, .optional }, 31866 .c_ptr => .{ .all_s1, .c_ptr }, 31867 else => .{ .all_s0, .optional }, 31868 }, 31869 .array => switch (s1) { 31870 .array => .{ .either, .array }, 31871 .vector => .{ .all_s1, .vector }, 31872 else => .{ .all_s0, .array }, 31873 }, 31874 .vector => switch (s1) { 31875 .vector => .{ .either, .vector }, 31876 else => .{ .all_s0, .vector }, 31877 }, 31878 .c_ptr => switch (s1) { 31879 .c_ptr => .{ .either, .c_ptr }, 31880 else => .{ .all_s0, .c_ptr }, 31881 }, 31882 .ptr => switch (s1) { 31883 .ptr => .{ .either, .ptr }, 31884 else => .{ .all_s0, .ptr }, 31885 }, 31886 .func => switch (s1) { 31887 .func => .{ .either, .func }, 31888 else => .{ .all_s1, s1 }, // doesn't override anything later 31889 }, 31890 .enum_or_union => switch (s1) { 31891 .enum_or_union => .{ .either, .enum_or_union }, 31892 else => .{ .all_s0, .enum_or_union }, 31893 }, 31894 .comptime_int => switch (s1) { 31895 .comptime_int => .{ .either, .comptime_int }, 31896 else => .{ .all_s1, s1 }, // doesn't override anything later 31897 }, 31898 .comptime_float => switch (s1) { 31899 .comptime_float => .{ .either, .comptime_float }, 31900 else => .{ .all_s1, s1 }, // doesn't override anything later 31901 }, 31902 .fixed_int => switch (s1) { 31903 .fixed_int => .{ .either, .fixed_int }, 31904 else => .{ .all_s1, s1 }, // doesn't override anything later 31905 }, 31906 .fixed_float => switch (s1) { 31907 .fixed_float => .{ .either, .fixed_float }, 31908 else => .{ .all_s1, s1 }, // doesn't override anything later 31909 }, 31910 .coercible_struct => switch (s1) { 31911 .exact => .{ .all_s1, .exact }, 31912 else => .{ .all_s0, .coercible_struct }, 31913 }, 31914 .exact => .{ .all_s0, .exact }, 31915 }; 31916 31917 switch (res[0]) { 31918 .all_s0 => { 31919 if (!s0_is_a) { 31920 reason_peer.* = b_peer_idx; 31921 } 31922 }, 31923 .all_s1 => { 31924 if (s0_is_a) { 31925 reason_peer.* = b_peer_idx; 31926 } 31927 }, 31928 .either => { 31929 // Prefer the earliest peer 31930 reason_peer.* = @min(reason_peer.*, b_peer_idx); 31931 }, 31932 } 31933 31934 return res[1]; 31935 } 31936 31937 fn select(ty: Type, mod: *Module) PeerResolveStrategy { 31938 return switch (ty.zigTypeTag(mod)) { 31939 .Type, .Void, .Bool, .Opaque, .Frame, .AnyFrame => .exact, 31940 .NoReturn, .Undefined => .unknown, 31941 .Null => .nullable, 31942 .ComptimeInt => .comptime_int, 31943 .Int => .fixed_int, 31944 .ComptimeFloat => .comptime_float, 31945 .Float => .fixed_float, 31946 .Pointer => if (ty.ptrInfo(mod).flags.size == .C) .c_ptr else .ptr, 31947 .Array => .array, 31948 .Vector => .vector, 31949 .Optional => .optional, 31950 .ErrorSet => .error_set, 31951 .ErrorUnion => .error_union, 31952 .EnumLiteral, .Enum, .Union => .enum_or_union, 31953 .Struct => if (ty.isTupleOrAnonStruct(mod)) .coercible_struct else .exact, 31954 .Fn => .func, 31955 }; 31956 } 31957 }; 31958 31959 const PeerResolveResult = union(enum) { 31960 /// The peer type resolution was successful, and resulted in the given type. 31961 success: Type, 31962 /// There was some generic conflict between two peers. 31963 conflict: struct { 31964 peer_idx_a: usize, 31965 peer_idx_b: usize, 31966 }, 31967 /// There was an error when resolving the type of a struct or tuple field. 31968 field_error: struct { 31969 /// The name of the field which caused the failure. 31970 field_name: []const u8, 31971 /// The type of this field in each peer. 31972 field_types: []Type, 31973 /// The error from resolving the field type. Guaranteed not to be `success`. 31974 sub_result: *PeerResolveResult, 31975 }, 31976 31977 fn report( 31978 result: PeerResolveResult, 31979 sema: *Sema, 31980 block: *Block, 31981 src: LazySrcLoc, 31982 instructions: []const Air.Inst.Ref, 31983 candidate_srcs: Module.PeerTypeCandidateSrc, 31984 ) !*Module.ErrorMsg { 31985 const mod = sema.mod; 31986 const decl_ptr = mod.declPtr(block.src_decl); 31987 31988 var opt_msg: ?*Module.ErrorMsg = null; 31989 errdefer if (opt_msg) |msg| msg.destroy(sema.gpa); 31990 31991 // If we mention fields we'll want to include field types, so put peer types in a buffer 31992 var peer_tys = try sema.arena.alloc(Type, instructions.len); 31993 for (peer_tys, instructions) |*ty, inst| { 31994 ty.* = sema.typeOf(inst); 31995 } 31996 31997 var cur = result; 31998 while (true) { 31999 var conflict_idx: [2]usize = undefined; 32000 32001 switch (cur) { 32002 .success => unreachable, 32003 .conflict => |conflict| { 32004 // Fall through to two-peer conflict handling below 32005 conflict_idx = .{ 32006 conflict.peer_idx_a, 32007 conflict.peer_idx_b, 32008 }; 32009 }, 32010 .field_error => |field_error| { 32011 const fmt = "struct field '{s}' has conflicting types"; 32012 const args = .{field_error.field_name}; 32013 if (opt_msg) |msg| { 32014 try sema.errNote(block, src, msg, fmt, args); 32015 } else { 32016 opt_msg = try sema.errMsg(block, src, fmt, args); 32017 } 32018 32019 // Continue on to child error 32020 cur = field_error.sub_result.*; 32021 peer_tys = field_error.field_types; 32022 continue; 32023 }, 32024 } 32025 32026 // This is the path for reporting a generic conflict between two peers. 32027 32028 if (conflict_idx[1] < conflict_idx[0]) { 32029 // b comes first in source, so it's better if it comes first in the error 32030 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]); 32031 } 32032 32033 const conflict_tys: [2]Type = .{ 32034 peer_tys[conflict_idx[0]], 32035 peer_tys[conflict_idx[1]], 32036 }; 32037 const conflict_srcs: [2]?LazySrcLoc = .{ 32038 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[0]), 32039 candidate_srcs.resolve(mod, decl_ptr, conflict_idx[1]), 32040 }; 32041 32042 const fmt = "incompatible types: '{}' and '{}'"; 32043 const args = .{ 32044 conflict_tys[0].fmt(mod), 32045 conflict_tys[1].fmt(mod), 32046 }; 32047 const msg = if (opt_msg) |msg| msg: { 32048 try sema.errNote(block, src, msg, fmt, args); 32049 break :msg msg; 32050 } else msg: { 32051 const msg = try sema.errMsg(block, src, fmt, args); 32052 opt_msg = msg; 32053 break :msg msg; 32054 }; 32055 32056 if (conflict_srcs[0]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[0].fmt(mod)}); 32057 if (conflict_srcs[1]) |src_loc| try sema.errNote(block, src_loc, msg, "type '{}' here", .{conflict_tys[1].fmt(mod)}); 32058 32059 // No child error 32060 break; 32061 } 32062 32063 return opt_msg.?; 32064 } 32065 }; 32066 32067 fn resolvePeerTypes( 32068 sema: *Sema, 32069 block: *Block, 32070 src: LazySrcLoc, 32071 instructions: []const Air.Inst.Ref, 32072 candidate_srcs: Module.PeerTypeCandidateSrc, 32073 ) !Type { 32074 switch (instructions.len) { 32075 0 => return Type.noreturn, 32076 1 => return sema.typeOf(instructions[0]), 32077 else => {}, 32078 } 32079 32080 var peer_tys = try sema.arena.alloc(?Type, instructions.len); 32081 var peer_vals = try sema.arena.alloc(?Value, instructions.len); 32082 32083 for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { 32084 ty.* = sema.typeOf(inst); 32085 val.* = try sema.resolveMaybeUndefVal(inst); 32086 } 32087 32088 switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { 32089 .success => |ty| return ty, 32090 else => |result| { 32091 const msg = try result.report(sema, block, src, instructions, candidate_srcs); 32092 return sema.failWithOwnedErrorMsg(msg); 32093 }, 32094 } 32095 } 32096 32097 fn resolvePeerTypesInner( 32098 sema: *Sema, 32099 block: *Block, 32100 src: LazySrcLoc, 32101 peer_tys: []?Type, 32102 peer_vals: []?Value, 32103 ) !PeerResolveResult { 32104 const mod = sema.mod; 32105 32106 var strat_reason: usize = 0; 32107 var s: PeerResolveStrategy = .unknown; 32108 for (peer_tys, 0..) |opt_ty, i| { 32109 const ty = opt_ty orelse continue; 32110 s = s.merge(PeerResolveStrategy.select(ty, mod), &strat_reason, i); 32111 } 32112 32113 if (s == .unknown) { 32114 // The whole thing was noreturn or undefined - try to do an exact match 32115 s = .exact; 32116 } else { 32117 // There was something other than noreturn and undefined, so we can ignore those peers 32118 for (peer_tys) |*ty_ptr| { 32119 const ty = ty_ptr.* orelse continue; 32120 switch (ty.zigTypeTag(mod)) { 32121 .NoReturn, .Undefined => ty_ptr.* = null, 32122 else => {}, 32123 } 32124 } 32125 } 32126 32127 const target = mod.getTarget(); 32128 32129 switch (s) { 32130 .unknown => unreachable, 32131 32132 .error_set => { 32133 var final_set: ?Type = null; 32134 for (peer_tys, 0..) |opt_ty, i| { 32135 const ty = opt_ty orelse continue; 32136 if (ty.zigTypeTag(mod) != .ErrorSet) return .{ .conflict = .{ 32137 .peer_idx_a = strat_reason, 32138 .peer_idx_b = i, 32139 } }; 32140 if (final_set) |cur_set| { 32141 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty); 32142 } else { 32143 final_set = ty; 32144 } 32145 } 32146 return .{ .success = final_set.? }; 32147 }, 32148 32149 .error_union => { 32150 var final_set: ?Type = null; 32151 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 32152 const ty = ty_ptr.* orelse continue; 32153 const set_ty = switch (ty.zigTypeTag(mod)) { 32154 .ErrorSet => blk: { 32155 ty_ptr.* = null; // no payload to decide on 32156 val_ptr.* = null; 32157 break :blk ty; 32158 }, 32159 .ErrorUnion => blk: { 32160 const set_ty = ty.errorUnionSet(mod); 32161 ty_ptr.* = ty.errorUnionPayload(mod); 32162 if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) { 32163 .error_union => |eu| switch (eu.val) { 32164 .payload => |payload_ip| val_ptr.* = payload_ip.toValue(), 32165 .err_name => val_ptr.* = null, 32166 }, 32167 .undef => val_ptr.* = (try sema.mod.intern(.{ .undef = ty_ptr.*.?.toIntern() })).toValue(), 32168 else => unreachable, 32169 }; 32170 break :blk set_ty; 32171 }, 32172 else => continue, // whole type is the payload 32173 }; 32174 if (final_set) |cur_set| { 32175 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty); 32176 } else { 32177 final_set = set_ty; 32178 } 32179 } 32180 assert(final_set != null); 32181 const final_payload = switch (try sema.resolvePeerTypesInner( 32182 block, 32183 src, 32184 peer_tys, 32185 peer_vals, 32186 )) { 32187 .success => |ty| ty, 32188 else => |result| return result, 32189 }; 32190 return .{ .success = try mod.errorUnionType(final_set.?, final_payload) }; 32191 }, 32192 32193 .nullable => { 32194 for (peer_tys, 0..) |opt_ty, i| { 32195 const ty = opt_ty orelse continue; 32196 if (!ty.eql(Type.null, mod)) return .{ .conflict = .{ 32197 .peer_idx_a = strat_reason, 32198 .peer_idx_b = i, 32199 } }; 32200 } 32201 return .{ .success = Type.null }; 32202 }, 32203 32204 .optional => { 32205 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { 32206 const ty = ty_ptr.* orelse continue; 32207 switch (ty.zigTypeTag(mod)) { 32208 .Null => { 32209 ty_ptr.* = null; 32210 val_ptr.* = null; 32211 }, 32212 .Optional => { 32213 ty_ptr.* = ty.optionalChild(mod); 32214 if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(mod)) opt_val.optionalValue(mod) else null; 32215 }, 32216 else => {}, 32217 } 32218 } 32219 const child_ty = switch (try sema.resolvePeerTypesInner( 32220 block, 32221 src, 32222 peer_tys, 32223 peer_vals, 32224 )) { 32225 .success => |ty| ty, 32226 else => |result| return result, 32227 }; 32228 return .{ .success = try mod.optionalType(child_ty.toIntern()) }; 32229 }, 32230 32231 .array => { 32232 // Index of the first non-null peer 32233 var opt_first_idx: ?usize = null; 32234 // Index of the first array or vector peer (i.e. not a tuple) 32235 var opt_first_arr_idx: ?usize = null; 32236 // Set to non-null once we see any peer, even a tuple 32237 var len: u64 = undefined; 32238 var sentinel: ?Value = undefined; 32239 // Only set once we see a non-tuple peer 32240 var elem_ty: Type = undefined; 32241 32242 for (peer_tys, 0..) |*ty_ptr, i| { 32243 const ty = ty_ptr.* orelse continue; 32244 32245 if (!ty.isArrayOrVector(mod)) { 32246 // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced. 32247 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 32248 .peer_idx_a = strat_reason, 32249 .peer_idx_b = i, 32250 } }; 32251 32252 if (opt_first_idx) |first_idx| { 32253 if (arr_like.len != len) return .{ .conflict = .{ 32254 .peer_idx_a = first_idx, 32255 .peer_idx_b = i, 32256 } }; 32257 } else { 32258 opt_first_idx = i; 32259 len = arr_like.len; 32260 } 32261 32262 sentinel = null; 32263 32264 continue; 32265 } 32266 32267 const first_arr_idx = opt_first_arr_idx orelse { 32268 if (opt_first_idx == null) { 32269 opt_first_idx = i; 32270 len = ty.arrayLen(mod); 32271 sentinel = ty.sentinel(mod); 32272 } 32273 opt_first_arr_idx = i; 32274 elem_ty = ty.childType(mod); 32275 continue; 32276 }; 32277 32278 if (ty.arrayLen(mod) != len) return .{ .conflict = .{ 32279 .peer_idx_a = first_arr_idx, 32280 .peer_idx_b = i, 32281 } }; 32282 32283 if (!ty.childType(mod).eql(elem_ty, mod)) { 32284 return .{ .conflict = .{ 32285 .peer_idx_a = first_arr_idx, 32286 .peer_idx_b = i, 32287 } }; 32288 } 32289 32290 if (sentinel) |cur_sent| { 32291 if (ty.sentinel(mod)) |peer_sent| { 32292 if (!peer_sent.eql(cur_sent, elem_ty, mod)) sentinel = null; 32293 } else { 32294 sentinel = null; 32295 } 32296 } 32297 } 32298 32299 // There should always be at least one array or vector peer 32300 assert(opt_first_arr_idx != null); 32301 32302 return .{ .success = try mod.arrayType(.{ 32303 .len = len, 32304 .child = elem_ty.toIntern(), 32305 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none, 32306 }) }; 32307 }, 32308 32309 .vector => { 32310 var len: ?u64 = null; 32311 var first_idx: usize = undefined; 32312 for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| { 32313 const ty = ty_ptr.* orelse continue; 32314 32315 if (!ty.isArrayOrVector(mod)) { 32316 // Allow tuples of the correct length 32317 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ 32318 .peer_idx_a = strat_reason, 32319 .peer_idx_b = i, 32320 } }; 32321 32322 if (len) |expect_len| { 32323 if (arr_like.len != expect_len) return .{ .conflict = .{ 32324 .peer_idx_a = first_idx, 32325 .peer_idx_b = i, 32326 } }; 32327 } else { 32328 len = arr_like.len; 32329 first_idx = i; 32330 } 32331 32332 // Tuples won't participate in the child type resolution. We'll resolve without 32333 // them, and if the tuples have a bad type, we'll get a coercion error later. 32334 ty_ptr.* = null; 32335 val_ptr.* = null; 32336 32337 continue; 32338 } 32339 32340 if (len) |expect_len| { 32341 if (ty.arrayLen(mod) != expect_len) return .{ .conflict = .{ 32342 .peer_idx_a = first_idx, 32343 .peer_idx_b = i, 32344 } }; 32345 } else { 32346 len = ty.arrayLen(mod); 32347 first_idx = i; 32348 } 32349 32350 ty_ptr.* = ty.childType(mod); 32351 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR 32352 } 32353 32354 const child_ty = switch (try sema.resolvePeerTypesInner( 32355 block, 32356 src, 32357 peer_tys, 32358 peer_vals, 32359 )) { 32360 .success => |ty| ty, 32361 else => |result| return result, 32362 }; 32363 32364 return .{ .success = try mod.vectorType(.{ 32365 .len = @as(u32, @intCast(len.?)), 32366 .child = child_ty.toIntern(), 32367 }) }; 32368 }, 32369 32370 .c_ptr => { 32371 var opt_ptr_info: ?InternPool.Key.PtrType = null; 32372 var first_idx: usize = undefined; 32373 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 32374 const ty = opt_ty orelse continue; 32375 switch (ty.zigTypeTag(mod)) { 32376 .ComptimeInt => continue, // comptime-known integers can always coerce to C pointers 32377 .Int => { 32378 if (opt_val != null) { 32379 // Always allow the coercion for comptime-known ints 32380 continue; 32381 } else { 32382 // Runtime-known, so check if the type is no bigger than a usize 32383 const ptr_bits = target.ptrBitWidth(); 32384 const bits = ty.intInfo(mod).bits; 32385 if (bits <= ptr_bits) continue; 32386 } 32387 }, 32388 .Null => continue, 32389 else => {}, 32390 } 32391 32392 if (!ty.isPtrAtRuntime(mod)) return .{ .conflict = .{ 32393 .peer_idx_a = strat_reason, 32394 .peer_idx_b = i, 32395 } }; 32396 32397 // Goes through optionals 32398 const peer_info = ty.ptrInfo(mod); 32399 32400 var ptr_info = opt_ptr_info orelse { 32401 opt_ptr_info = peer_info; 32402 opt_ptr_info.?.flags.size = .C; 32403 first_idx = i; 32404 continue; 32405 }; 32406 32407 // Try peer -> cur, then cur -> peer 32408 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) orelse { 32409 return .{ .conflict = .{ 32410 .peer_idx_a = first_idx, 32411 .peer_idx_b = i, 32412 } }; 32413 }).toIntern(); 32414 32415 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) { 32416 const peer_sent = try mod.intern_pool.getCoerced(sema.gpa, ptr_info.sentinel, ptr_info.child); 32417 const ptr_sent = try mod.intern_pool.getCoerced(sema.gpa, peer_info.sentinel, ptr_info.child); 32418 if (ptr_sent == peer_sent) { 32419 ptr_info.sentinel = ptr_sent; 32420 } else { 32421 ptr_info.sentinel = .none; 32422 } 32423 } else { 32424 ptr_info.sentinel = .none; 32425 } 32426 32427 // Note that the align can be always non-zero; Module.ptrType will canonicalize it 32428 ptr_info.flags.alignment = Alignment.fromByteUnits(@min( 32429 ptr_info.flags.alignment.toByteUnitsOptional() orelse 32430 ptr_info.child.toType().abiAlignment(mod), 32431 peer_info.flags.alignment.toByteUnitsOptional() orelse 32432 peer_info.child.toType().abiAlignment(mod), 32433 )); 32434 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 32435 return .{ .conflict = .{ 32436 .peer_idx_a = first_idx, 32437 .peer_idx_b = i, 32438 } }; 32439 } 32440 32441 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 32442 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 32443 { 32444 return .{ .conflict = .{ 32445 .peer_idx_a = first_idx, 32446 .peer_idx_b = i, 32447 } }; 32448 } 32449 32450 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 32451 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 32452 32453 opt_ptr_info = ptr_info; 32454 } 32455 return .{ .success = try mod.ptrType(opt_ptr_info.?) }; 32456 }, 32457 32458 .ptr => { 32459 // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only 32460 // if there were no actual slices. Else, we want the slice index to report a conflict. 32461 var opt_slice_idx: ?usize = null; 32462 32463 var opt_ptr_info: ?InternPool.Key.PtrType = null; 32464 var first_idx: usize = undefined; 32465 var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error 32466 32467 for (peer_tys, 0..) |opt_ty, i| { 32468 const ty = opt_ty orelse continue; 32469 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(mod)) { 32470 .Pointer => ty.ptrInfo(mod), 32471 .Fn => .{ 32472 .child = ty.toIntern(), 32473 .flags = .{ 32474 .address_space = target_util.defaultAddressSpace(target, .global_constant), 32475 }, 32476 }, 32477 else => return .{ .conflict = .{ 32478 .peer_idx_a = strat_reason, 32479 .peer_idx_b = i, 32480 } }, 32481 }; 32482 32483 switch (peer_info.flags.size) { 32484 .One, .Many => {}, 32485 .Slice => opt_slice_idx = i, 32486 .C => return .{ .conflict = .{ 32487 .peer_idx_a = strat_reason, 32488 .peer_idx_b = i, 32489 } }, 32490 } 32491 32492 var ptr_info = opt_ptr_info orelse { 32493 opt_ptr_info = peer_info; 32494 first_idx = i; 32495 continue; 32496 }; 32497 32498 other_idx = i; 32499 32500 // We want to return this in a lot of cases, so alias it here for convenience 32501 const generic_err: PeerResolveResult = .{ .conflict = .{ 32502 .peer_idx_a = first_idx, 32503 .peer_idx_b = i, 32504 } }; 32505 32506 // Note that the align can be always non-zero; Type.ptr will canonicalize it 32507 ptr_info.flags.alignment = Alignment.fromByteUnits(@min( 32508 ptr_info.flags.alignment.toByteUnitsOptional() orelse 32509 ptr_info.child.toType().abiAlignment(mod), 32510 peer_info.flags.alignment.toByteUnitsOptional() orelse 32511 peer_info.child.toType().abiAlignment(mod), 32512 )); 32513 32514 if (ptr_info.flags.address_space != peer_info.flags.address_space) { 32515 return generic_err; 32516 } 32517 32518 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or 32519 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) 32520 { 32521 return generic_err; 32522 } 32523 32524 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; 32525 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; 32526 32527 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) { 32528 .One => switch (mod.intern_pool.indexToKey(peer_info.child)) { 32529 .array_type => |array_type| array_type.sentinel, 32530 else => .none, 32531 }, 32532 .Many, .Slice => peer_info.sentinel, 32533 .C => unreachable, 32534 }; 32535 32536 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) { 32537 .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) { 32538 .array_type => |array_type| array_type.sentinel, 32539 else => .none, 32540 }, 32541 .Many, .Slice => ptr_info.sentinel, 32542 .C => unreachable, 32543 }; 32544 32545 // We abstract array handling slightly so that tuple pointers can work like array pointers 32546 const peer_pointee_array = sema.typeIsArrayLike(peer_info.child.toType()); 32547 const cur_pointee_array = sema.typeIsArrayLike(ptr_info.child.toType()); 32548 32549 // This switch is just responsible for deciding the size and pointee (not including 32550 // single-pointer array sentinel). 32551 good: { 32552 switch (peer_info.flags.size) { 32553 .One => switch (ptr_info.flags.size) { 32554 .One => { 32555 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| { 32556 ptr_info.child = pointee.toIntern(); 32557 break :good; 32558 } 32559 32560 const cur_arr = cur_pointee_array orelse return generic_err; 32561 const peer_arr = peer_pointee_array orelse return generic_err; 32562 32563 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| { 32564 // *[n:x]T + *[n:y]T = *[n]T 32565 if (cur_arr.len == peer_arr.len) { 32566 ptr_info.child = (try mod.arrayType(.{ 32567 .len = cur_arr.len, 32568 .child = elem_ty.toIntern(), 32569 })).toIntern(); 32570 break :good; 32571 } 32572 // *[a]T + *[b]T = []T 32573 ptr_info.flags.size = .Slice; 32574 ptr_info.child = elem_ty.toIntern(); 32575 break :good; 32576 } 32577 32578 if (peer_arr.elem_ty.toIntern() == .noreturn_type) { 32579 // *struct{} + *[a]T = []T 32580 ptr_info.flags.size = .Slice; 32581 ptr_info.child = cur_arr.elem_ty.toIntern(); 32582 break :good; 32583 } 32584 32585 if (cur_arr.elem_ty.toIntern() == .noreturn_type) { 32586 // *[a]T + *struct{} = []T 32587 ptr_info.flags.size = .Slice; 32588 ptr_info.child = peer_arr.elem_ty.toIntern(); 32589 break :good; 32590 } 32591 32592 return generic_err; 32593 }, 32594 .Many => { 32595 // Only works for *[n]T + [*]T -> [*]T 32596 const arr = peer_pointee_array orelse return generic_err; 32597 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), arr.elem_ty)) |pointee| { 32598 ptr_info.child = pointee.toIntern(); 32599 break :good; 32600 } 32601 if (arr.elem_ty.toIntern() == .noreturn_type) { 32602 // *struct{} + [*]T -> [*]T 32603 break :good; 32604 } 32605 return generic_err; 32606 }, 32607 .Slice => { 32608 // Only works for *[n]T + []T -> []T 32609 const arr = peer_pointee_array orelse return generic_err; 32610 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), arr.elem_ty)) |pointee| { 32611 ptr_info.child = pointee.toIntern(); 32612 break :good; 32613 } 32614 if (arr.elem_ty.toIntern() == .noreturn_type) { 32615 // *struct{} + []T -> []T 32616 break :good; 32617 } 32618 return generic_err; 32619 }, 32620 .C => unreachable, 32621 }, 32622 .Many => switch (ptr_info.flags.size) { 32623 .One => { 32624 // Only works for [*]T + *[n]T -> [*]T 32625 const arr = cur_pointee_array orelse return generic_err; 32626 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.child.toType())) |pointee| { 32627 ptr_info.flags.size = .Many; 32628 ptr_info.child = pointee.toIntern(); 32629 break :good; 32630 } 32631 if (arr.elem_ty.toIntern() == .noreturn_type) { 32632 // [*]T + *struct{} -> [*]T 32633 ptr_info.flags.size = .Many; 32634 ptr_info.child = peer_info.child; 32635 break :good; 32636 } 32637 return generic_err; 32638 }, 32639 .Many => { 32640 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| { 32641 ptr_info.child = pointee.toIntern(); 32642 break :good; 32643 } 32644 return generic_err; 32645 }, 32646 .Slice => { 32647 // Only works if no peers are actually slices 32648 if (opt_slice_idx) |slice_idx| { 32649 return .{ .conflict = .{ 32650 .peer_idx_a = slice_idx, 32651 .peer_idx_b = i, 32652 } }; 32653 } 32654 // Okay, then works for [*]T + "[]T" -> [*]T 32655 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| { 32656 ptr_info.flags.size = .Many; 32657 ptr_info.child = pointee.toIntern(); 32658 break :good; 32659 } 32660 return generic_err; 32661 }, 32662 .C => unreachable, 32663 }, 32664 .Slice => switch (ptr_info.flags.size) { 32665 .One => { 32666 // Only works for []T + *[n]T -> []T 32667 const arr = cur_pointee_array orelse return generic_err; 32668 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, peer_info.child.toType())) |pointee| { 32669 ptr_info.flags.size = .Slice; 32670 ptr_info.child = pointee.toIntern(); 32671 break :good; 32672 } 32673 if (arr.elem_ty.toIntern() == .noreturn_type) { 32674 // []T + *struct{} -> []T 32675 ptr_info.flags.size = .Slice; 32676 ptr_info.child = peer_info.child; 32677 break :good; 32678 } 32679 return generic_err; 32680 }, 32681 .Many => { 32682 // Impossible! (current peer is an actual slice) 32683 return generic_err; 32684 }, 32685 .Slice => { 32686 if (try sema.resolvePairInMemoryCoercible(block, src, ptr_info.child.toType(), peer_info.child.toType())) |pointee| { 32687 ptr_info.child = pointee.toIntern(); 32688 break :good; 32689 } 32690 return generic_err; 32691 }, 32692 .C => unreachable, 32693 }, 32694 .C => unreachable, 32695 } 32696 } 32697 32698 const sentinel_ty = switch (ptr_info.flags.size) { 32699 .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) { 32700 .array_type => |array_type| array_type.child, 32701 else => ptr_info.child, 32702 }, 32703 .Many, .Slice, .C => ptr_info.child, 32704 }; 32705 32706 sentinel: { 32707 no_sentinel: { 32708 if (peer_sentinel == .none) break :no_sentinel; 32709 if (cur_sentinel == .none) break :no_sentinel; 32710 const peer_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, peer_sentinel, sentinel_ty); 32711 const cur_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, cur_sentinel, sentinel_ty); 32712 if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel; 32713 // Sentinels match 32714 if (ptr_info.flags.size == .One) switch (mod.intern_pool.indexToKey(ptr_info.child)) { 32715 .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{ 32716 .len = array_type.len, 32717 .child = array_type.child, 32718 .sentinel = cur_sent_coerced, 32719 })).toIntern(), 32720 else => unreachable, 32721 } else { 32722 ptr_info.sentinel = cur_sent_coerced; 32723 } 32724 break :sentinel; 32725 } 32726 // Clear existing sentinel 32727 ptr_info.sentinel = .none; 32728 switch (mod.intern_pool.indexToKey(ptr_info.child)) { 32729 .array_type => |array_type| ptr_info.child = (try mod.arrayType(.{ 32730 .len = array_type.len, 32731 .child = array_type.child, 32732 .sentinel = .none, 32733 })).toIntern(), 32734 else => {}, 32735 } 32736 } 32737 32738 opt_ptr_info = ptr_info; 32739 } 32740 32741 // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance) 32742 // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to 32743 // coerce the empty struct to a specific type, but no peer provided one. We need to 32744 // detect this case and emit an error. 32745 const pointee = opt_ptr_info.?.child; 32746 switch (pointee) { 32747 .noreturn_type => return .{ .conflict = .{ 32748 .peer_idx_a = first_idx, 32749 .peer_idx_b = other_idx, 32750 } }, 32751 else => switch (mod.intern_pool.indexToKey(pointee)) { 32752 .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{ 32753 .peer_idx_a = first_idx, 32754 .peer_idx_b = other_idx, 32755 } }, 32756 else => {}, 32757 }, 32758 } 32759 32760 return .{ .success = try mod.ptrType(opt_ptr_info.?) }; 32761 }, 32762 32763 .func => { 32764 var opt_cur_ty: ?Type = null; 32765 var first_idx: usize = undefined; 32766 for (peer_tys, 0..) |opt_ty, i| { 32767 const ty = opt_ty orelse continue; 32768 const cur_ty = opt_cur_ty orelse { 32769 opt_cur_ty = ty; 32770 first_idx = i; 32771 continue; 32772 }; 32773 if (ty.zigTypeTag(mod) != .Fn) return .{ .conflict = .{ 32774 .peer_idx_a = strat_reason, 32775 .peer_idx_b = i, 32776 } }; 32777 // ty -> cur_ty 32778 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, target, src, src)) { 32779 continue; 32780 } 32781 // cur_ty -> ty 32782 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, target, src, src)) { 32783 opt_cur_ty = ty; 32784 continue; 32785 } 32786 return .{ .conflict = .{ 32787 .peer_idx_a = first_idx, 32788 .peer_idx_b = i, 32789 } }; 32790 } 32791 return .{ .success = opt_cur_ty.? }; 32792 }, 32793 32794 .enum_or_union => { 32795 var opt_cur_ty: ?Type = null; 32796 // The peer index which gave the current type 32797 var cur_ty_idx: usize = undefined; 32798 32799 for (peer_tys, 0..) |opt_ty, i| { 32800 const ty = opt_ty orelse continue; 32801 switch (ty.zigTypeTag(mod)) { 32802 .EnumLiteral, .Enum, .Union => {}, 32803 else => return .{ .conflict = .{ 32804 .peer_idx_a = strat_reason, 32805 .peer_idx_b = i, 32806 } }, 32807 } 32808 const cur_ty = opt_cur_ty orelse { 32809 opt_cur_ty = ty; 32810 cur_ty_idx = i; 32811 continue; 32812 }; 32813 32814 // We want to return this in a lot of cases, so alias it here for convenience 32815 const generic_err: PeerResolveResult = .{ .conflict = .{ 32816 .peer_idx_a = cur_ty_idx, 32817 .peer_idx_b = i, 32818 } }; 32819 32820 switch (cur_ty.zigTypeTag(mod)) { 32821 .EnumLiteral => { 32822 opt_cur_ty = ty; 32823 cur_ty_idx = i; 32824 }, 32825 .Enum => switch (ty.zigTypeTag(mod)) { 32826 .EnumLiteral => {}, 32827 .Enum => { 32828 if (!ty.eql(cur_ty, mod)) return generic_err; 32829 }, 32830 .Union => { 32831 const tag_ty = ty.unionTagTypeHypothetical(mod); 32832 if (!tag_ty.eql(cur_ty, mod)) return generic_err; 32833 opt_cur_ty = ty; 32834 cur_ty_idx = i; 32835 }, 32836 else => unreachable, 32837 }, 32838 .Union => switch (ty.zigTypeTag(mod)) { 32839 .EnumLiteral => {}, 32840 .Enum => { 32841 const cur_tag_ty = cur_ty.unionTagTypeHypothetical(mod); 32842 if (!ty.eql(cur_tag_ty, mod)) return generic_err; 32843 }, 32844 .Union => { 32845 if (!ty.eql(cur_ty, mod)) return generic_err; 32846 }, 32847 else => unreachable, 32848 }, 32849 else => unreachable, 32850 } 32851 } 32852 return .{ .success = opt_cur_ty.? }; 32853 }, 32854 32855 .comptime_int => { 32856 for (peer_tys, 0..) |opt_ty, i| { 32857 const ty = opt_ty orelse continue; 32858 switch (ty.zigTypeTag(mod)) { 32859 .ComptimeInt => {}, 32860 else => return .{ .conflict = .{ 32861 .peer_idx_a = strat_reason, 32862 .peer_idx_b = i, 32863 } }, 32864 } 32865 } 32866 return .{ .success = Type.comptime_int }; 32867 }, 32868 32869 .comptime_float => { 32870 for (peer_tys, 0..) |opt_ty, i| { 32871 const ty = opt_ty orelse continue; 32872 switch (ty.zigTypeTag(mod)) { 32873 .ComptimeInt, .ComptimeFloat => {}, 32874 else => return .{ .conflict = .{ 32875 .peer_idx_a = strat_reason, 32876 .peer_idx_b = i, 32877 } }, 32878 } 32879 } 32880 return .{ .success = Type.comptime_float }; 32881 }, 32882 32883 .fixed_int => { 32884 var idx_unsigned: ?usize = null; 32885 var idx_signed: ?usize = null; 32886 32887 // TODO: this is for compatibility with legacy behavior. See beneath the loop. 32888 var any_comptime_known = false; 32889 32890 for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| { 32891 const ty = opt_ty orelse continue; 32892 const opt_val = ptr_opt_val.*; 32893 32894 const peer_tag = ty.zigTypeTag(mod); 32895 switch (peer_tag) { 32896 .ComptimeInt => { 32897 // If the value is undefined, we can't refine to a fixed-width int 32898 if (opt_val == null or opt_val.?.isUndef(mod)) return .{ .conflict = .{ 32899 .peer_idx_a = strat_reason, 32900 .peer_idx_b = i, 32901 } }; 32902 any_comptime_known = true; 32903 ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?); 32904 continue; 32905 }, 32906 .Int => {}, 32907 else => return .{ .conflict = .{ 32908 .peer_idx_a = strat_reason, 32909 .peer_idx_b = i, 32910 } }, 32911 } 32912 32913 if (opt_val != null) any_comptime_known = true; 32914 32915 const info = ty.intInfo(mod); 32916 32917 const idx_ptr = switch (info.signedness) { 32918 .unsigned => &idx_unsigned, 32919 .signed => &idx_signed, 32920 }; 32921 32922 const largest_idx = idx_ptr.* orelse { 32923 idx_ptr.* = i; 32924 continue; 32925 }; 32926 32927 const cur_info = peer_tys[largest_idx].?.intInfo(mod); 32928 if (info.bits > cur_info.bits) { 32929 idx_ptr.* = i; 32930 } 32931 } 32932 32933 if (idx_signed == null) { 32934 return .{ .success = peer_tys[idx_unsigned.?].? }; 32935 } 32936 32937 if (idx_unsigned == null) { 32938 return .{ .success = peer_tys[idx_signed.?].? }; 32939 } 32940 32941 const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(mod); 32942 const signed_info = peer_tys[idx_signed.?].?.intInfo(mod); 32943 if (signed_info.bits > unsigned_info.bits) { 32944 return .{ .success = peer_tys[idx_signed.?].? }; 32945 } 32946 32947 // TODO: this is for compatibility with legacy behavior. Before this version of PTR was 32948 // implemented, the algorithm very often returned false positives, with the expectation 32949 // that you'd just hit a coercion error later. One of these was that for integers, the 32950 // largest type would always be returned, even if it couldn't fit everything. This had 32951 // an unintentional consequence to semantics, which is that if values were known at 32952 // comptime, they would be coerced down to the smallest type where possible. This 32953 // behavior is unintuitive and order-dependent, so in my opinion should be eliminated, 32954 // but for now we'll retain compatibility. 32955 if (any_comptime_known) { 32956 if (unsigned_info.bits > signed_info.bits) { 32957 return .{ .success = peer_tys[idx_unsigned.?].? }; 32958 } 32959 const idx = @min(idx_unsigned.?, idx_signed.?); 32960 return .{ .success = peer_tys[idx].? }; 32961 } 32962 32963 return .{ .conflict = .{ 32964 .peer_idx_a = idx_unsigned.?, 32965 .peer_idx_b = idx_signed.?, 32966 } }; 32967 }, 32968 32969 .fixed_float => { 32970 var opt_cur_ty: ?Type = null; 32971 32972 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { 32973 const ty = opt_ty orelse continue; 32974 switch (ty.zigTypeTag(mod)) { 32975 .ComptimeFloat, .ComptimeInt => {}, 32976 .Int => { 32977 if (opt_val == null) return .{ .conflict = .{ 32978 .peer_idx_a = strat_reason, 32979 .peer_idx_b = i, 32980 } }; 32981 }, 32982 .Float => { 32983 if (opt_cur_ty) |cur_ty| { 32984 if (cur_ty.eql(ty, mod)) continue; 32985 // Recreate the type so we eliminate any c_longdouble 32986 const bits = @max(cur_ty.floatBits(target), ty.floatBits(target)); 32987 opt_cur_ty = switch (bits) { 32988 16 => Type.f16, 32989 32 => Type.f32, 32990 64 => Type.f64, 32991 80 => Type.f80, 32992 128 => Type.f128, 32993 else => unreachable, 32994 }; 32995 } else { 32996 opt_cur_ty = ty; 32997 } 32998 }, 32999 else => return .{ .conflict = .{ 33000 .peer_idx_a = strat_reason, 33001 .peer_idx_b = i, 33002 } }, 33003 } 33004 } 33005 33006 // Note that fixed_float is only chosen if there is at least one fixed-width float peer, 33007 // so opt_cur_ty must be non-null. 33008 return .{ .success = opt_cur_ty.? }; 33009 }, 33010 33011 .coercible_struct => { 33012 // First, check that every peer has the same approximate structure (field count and names) 33013 33014 var opt_first_idx: ?usize = null; 33015 var is_tuple: bool = undefined; 33016 var field_count: usize = undefined; 33017 // Only defined for non-tuples. 33018 var field_names: []InternPool.NullTerminatedString = undefined; 33019 33020 for (peer_tys, 0..) |opt_ty, i| { 33021 const ty = opt_ty orelse continue; 33022 33023 if (!ty.isTupleOrAnonStruct(mod)) { 33024 return .{ .conflict = .{ 33025 .peer_idx_a = strat_reason, 33026 .peer_idx_b = i, 33027 } }; 33028 } 33029 33030 const first_idx = opt_first_idx orelse { 33031 opt_first_idx = i; 33032 is_tuple = ty.isTuple(mod); 33033 field_count = ty.structFieldCount(mod); 33034 if (!is_tuple) { 33035 const names = mod.intern_pool.indexToKey(ty.toIntern()).anon_struct_type.names; 33036 field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names); 33037 } 33038 continue; 33039 }; 33040 33041 if (ty.isTuple(mod) != is_tuple or ty.structFieldCount(mod) != field_count) { 33042 return .{ .conflict = .{ 33043 .peer_idx_a = first_idx, 33044 .peer_idx_b = i, 33045 } }; 33046 } 33047 33048 if (!is_tuple) { 33049 for (field_names, 0..) |expected, field_idx| { 33050 const actual = ty.structFieldName(field_idx, mod); 33051 if (actual == expected) continue; 33052 return .{ .conflict = .{ 33053 .peer_idx_a = first_idx, 33054 .peer_idx_b = i, 33055 } }; 33056 } 33057 } 33058 } 33059 33060 assert(opt_first_idx != null); 33061 33062 // Now, we'll recursively resolve the field types 33063 const field_types = try sema.arena.alloc(InternPool.Index, field_count); 33064 // Values for `comptime` fields - `.none` used for non-comptime fields 33065 const field_vals = try sema.arena.alloc(InternPool.Index, field_count); 33066 const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); 33067 const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); 33068 33069 for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| { 33070 // Fill buffers with types and values of the field 33071 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { 33072 const ty = opt_ty orelse { 33073 peer_field_ty.* = null; 33074 peer_field_val.* = null; 33075 continue; 33076 }; 33077 peer_field_ty.* = ty.structFieldType(field_idx, mod); 33078 peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null; 33079 } 33080 33081 // Resolve field type recursively 33082 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) { 33083 .success => |ty| ty.toIntern(), 33084 else => |result| { 33085 const result_buf = try sema.arena.create(PeerResolveResult); 33086 result_buf.* = result; 33087 const field_name = if (is_tuple) name: { 33088 break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx}); 33089 } else try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(field_names[field_idx])); 33090 33091 // The error info needs the field types, but we can't reuse sub_peer_tys 33092 // since the recursive call may have clobbered it. 33093 const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len); 33094 for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| { 33095 // Already-resolved types won't be referenced by the error so it's fine 33096 // to leave them undefined. 33097 const ty = opt_ty orelse continue; 33098 peer_field_ty.* = ty.structFieldType(field_idx, mod); 33099 } 33100 33101 return .{ .field_error = .{ 33102 .field_name = field_name, 33103 .field_types = peer_field_tys, 33104 .sub_result = result_buf, 33105 } }; 33106 }, 33107 }; 33108 33109 // Decide if this is a comptime field. If it is comptime in all peers, and the 33110 // coerced comptime values are all the same, we say it is comptime, else not. 33111 33112 var comptime_val: ?Value = null; 33113 for (peer_tys) |opt_ty| { 33114 const struct_ty = opt_ty orelse continue; 33115 const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse { 33116 comptime_val = null; 33117 break; 33118 }; 33119 const uncoerced_field = try sema.addConstant(uncoerced_field_val); 33120 const coerced_inst = sema.coerceExtra(block, field_ty.toType(), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) { 33121 // 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 33122 error.NotCoercible => { 33123 comptime_val = null; 33124 break; 33125 }, 33126 else => |e| return e, 33127 }; 33128 const coerced_val = (try sema.resolveMaybeUndefVal(coerced_inst)) orelse continue; 33129 const existing = comptime_val orelse { 33130 comptime_val = coerced_val; 33131 continue; 33132 }; 33133 if (!coerced_val.eql(existing, field_ty.toType(), mod)) { 33134 comptime_val = null; 33135 break; 33136 } 33137 } 33138 33139 field_val.* = if (comptime_val) |v| v.toIntern() else .none; 33140 } 33141 33142 const final_ty = try mod.intern(.{ .anon_struct_type = .{ 33143 .types = field_types, 33144 .names = if (is_tuple) &.{} else field_names, 33145 .values = field_vals, 33146 } }); 33147 33148 return .{ .success = final_ty.toType() }; 33149 }, 33150 33151 .exact => { 33152 var expect_ty: ?Type = null; 33153 var first_idx: usize = undefined; 33154 for (peer_tys, 0..) |opt_ty, i| { 33155 const ty = opt_ty orelse continue; 33156 if (expect_ty) |expect| { 33157 if (!ty.eql(expect, mod)) return .{ .conflict = .{ 33158 .peer_idx_a = first_idx, 33159 .peer_idx_b = i, 33160 } }; 33161 } else { 33162 expect_ty = ty; 33163 first_idx = i; 33164 } 33165 } 33166 return .{ .success = expect_ty.? }; 33167 }, 33168 } 33169 } 33170 33171 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { 33172 // e0 -> e1 33173 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { 33174 return e1; 33175 } 33176 33177 // e1 -> e0 33178 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { 33179 return e0; 33180 } 33181 33182 return sema.errorSetMerge(e0, e1); 33183 } 33184 33185 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { 33186 // ty_b -> ty_a 33187 if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, true, sema.mod.getTarget(), src, src)) { 33188 return ty_a; 33189 } 33190 33191 // ty_a -> ty_b 33192 if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, true, sema.mod.getTarget(), src, src)) { 33193 return ty_b; 33194 } 33195 33196 return null; 33197 } 33198 33199 const ArrayLike = struct { 33200 len: u64, 33201 /// `noreturn` indicates that this type is `struct{}` so can coerce to anything 33202 elem_ty: Type, 33203 }; 33204 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { 33205 const mod = sema.mod; 33206 return switch (ty.zigTypeTag(mod)) { 33207 .Array => .{ 33208 .len = ty.arrayLen(mod), 33209 .elem_ty = ty.childType(mod), 33210 }, 33211 .Struct => { 33212 const field_count = ty.structFieldCount(mod); 33213 if (field_count == 0) return .{ 33214 .len = 0, 33215 .elem_ty = Type.noreturn, 33216 }; 33217 if (!ty.isTuple(mod)) return null; 33218 const elem_ty = ty.structFieldType(0, mod); 33219 for (1..field_count) |i| { 33220 if (!ty.structFieldType(i, mod).eql(elem_ty, mod)) { 33221 return null; 33222 } 33223 } 33224 return .{ 33225 .len = field_count, 33226 .elem_ty = elem_ty, 33227 }; 33228 }, 33229 else => null, 33230 }; 33231 } 33232 33233 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { 33234 const mod = sema.mod; 33235 try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.return_type.toType()); 33236 33237 if (mod.comp.bin_file.options.error_return_tracing and mod.typeToFunc(fn_ty).?.return_type.toType().isError(mod)) { 33238 // Ensure the type exists so that backends can assume that. 33239 _ = try sema.getBuiltinType("StackTrace"); 33240 } 33241 33242 for (0..mod.typeToFunc(fn_ty).?.param_types.len) |i| { 33243 try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.param_types[i].toType()); 33244 } 33245 } 33246 33247 /// Make it so that calling hash() and eql() on `val` will not assert due 33248 /// to a type not having its layout resolved. 33249 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { 33250 const mod = sema.mod; 33251 switch (mod.intern_pool.indexToKey(val.toIntern())) { 33252 .int => |int| switch (int.storage) { 33253 .u64, .i64, .big_int => return val, 33254 .lazy_align, .lazy_size => return (try mod.intern(.{ .int = .{ 33255 .ty = int.ty, 33256 .storage = .{ .u64 = (try val.getUnsignedIntAdvanced(mod, sema)).? }, 33257 } })).toValue(), 33258 }, 33259 .ptr => |ptr| { 33260 const resolved_len = switch (ptr.len) { 33261 .none => .none, 33262 else => (try sema.resolveLazyValue(ptr.len.toValue())).toIntern(), 33263 }; 33264 switch (ptr.addr) { 33265 .decl, .mut_decl => return if (resolved_len == ptr.len) 33266 val 33267 else 33268 (try mod.intern(.{ .ptr = .{ 33269 .ty = ptr.ty, 33270 .addr = switch (ptr.addr) { 33271 .decl => |decl| .{ .decl = decl }, 33272 .mut_decl => |mut_decl| .{ .mut_decl = mut_decl }, 33273 else => unreachable, 33274 }, 33275 .len = resolved_len, 33276 } })).toValue(), 33277 .comptime_field => |field_val| { 33278 const resolved_field_val = 33279 (try sema.resolveLazyValue(field_val.toValue())).toIntern(); 33280 return if (resolved_field_val == field_val and resolved_len == ptr.len) 33281 val 33282 else 33283 (try mod.intern(.{ .ptr = .{ 33284 .ty = ptr.ty, 33285 .addr = .{ .comptime_field = resolved_field_val }, 33286 .len = resolved_len, 33287 } })).toValue(); 33288 }, 33289 .int => |int| { 33290 const resolved_int = (try sema.resolveLazyValue(int.toValue())).toIntern(); 33291 return if (resolved_int == int and resolved_len == ptr.len) 33292 val 33293 else 33294 (try mod.intern(.{ .ptr = .{ 33295 .ty = ptr.ty, 33296 .addr = .{ .int = resolved_int }, 33297 .len = resolved_len, 33298 } })).toValue(); 33299 }, 33300 .eu_payload, .opt_payload => |base| { 33301 const resolved_base = (try sema.resolveLazyValue(base.toValue())).toIntern(); 33302 return if (resolved_base == base and resolved_len == ptr.len) 33303 val 33304 else 33305 (try mod.intern(.{ .ptr = .{ 33306 .ty = ptr.ty, 33307 .addr = switch (ptr.addr) { 33308 .eu_payload => .{ .eu_payload = resolved_base }, 33309 .opt_payload => .{ .opt_payload = resolved_base }, 33310 else => unreachable, 33311 }, 33312 .len = ptr.len, 33313 } })).toValue(); 33314 }, 33315 .elem, .field => |base_index| { 33316 const resolved_base = (try sema.resolveLazyValue(base_index.base.toValue())).toIntern(); 33317 return if (resolved_base == base_index.base and resolved_len == ptr.len) 33318 val 33319 else 33320 (try mod.intern(.{ .ptr = .{ 33321 .ty = ptr.ty, 33322 .addr = switch (ptr.addr) { 33323 .elem => .{ .elem = .{ 33324 .base = resolved_base, 33325 .index = base_index.index, 33326 } }, 33327 .field => .{ .field = .{ 33328 .base = resolved_base, 33329 .index = base_index.index, 33330 } }, 33331 else => unreachable, 33332 }, 33333 .len = ptr.len, 33334 } })).toValue(); 33335 }, 33336 } 33337 }, 33338 .aggregate => |aggregate| switch (aggregate.storage) { 33339 .bytes => return val, 33340 .elems => |elems| { 33341 var resolved_elems: []InternPool.Index = &.{}; 33342 for (elems, 0..) |elem, i| { 33343 const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern(); 33344 if (resolved_elems.len == 0 and resolved_elem != elem) { 33345 resolved_elems = try sema.arena.alloc(InternPool.Index, elems.len); 33346 @memcpy(resolved_elems[0..i], elems[0..i]); 33347 } 33348 if (resolved_elems.len > 0) resolved_elems[i] = resolved_elem; 33349 } 33350 return if (resolved_elems.len == 0) val else (try mod.intern(.{ .aggregate = .{ 33351 .ty = aggregate.ty, 33352 .storage = .{ .elems = resolved_elems }, 33353 } })).toValue(); 33354 }, 33355 .repeated_elem => |elem| { 33356 const resolved_elem = (try sema.resolveLazyValue(elem.toValue())).toIntern(); 33357 return if (resolved_elem == elem) val else (try mod.intern(.{ .aggregate = .{ 33358 .ty = aggregate.ty, 33359 .storage = .{ .repeated_elem = resolved_elem }, 33360 } })).toValue(); 33361 }, 33362 }, 33363 .un => |un| { 33364 const resolved_tag = (try sema.resolveLazyValue(un.tag.toValue())).toIntern(); 33365 const resolved_val = (try sema.resolveLazyValue(un.val.toValue())).toIntern(); 33366 return if (resolved_tag == un.tag and resolved_val == un.val) 33367 val 33368 else 33369 (try mod.intern(.{ .un = .{ 33370 .ty = un.ty, 33371 .tag = resolved_tag, 33372 .val = resolved_val, 33373 } })).toValue(); 33374 }, 33375 else => return val, 33376 } 33377 } 33378 33379 pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void { 33380 const mod = sema.mod; 33381 switch (ty.zigTypeTag(mod)) { 33382 .Struct => return sema.resolveStructLayout(ty), 33383 .Union => return sema.resolveUnionLayout(ty), 33384 .Array => { 33385 if (ty.arrayLenIncludingSentinel(mod) == 0) return; 33386 const elem_ty = ty.childType(mod); 33387 return sema.resolveTypeLayout(elem_ty); 33388 }, 33389 .Optional => { 33390 const payload_ty = ty.optionalChild(mod); 33391 // In case of querying the ABI alignment of this optional, we will ask 33392 // for hasRuntimeBits() of the payload type, so we need "requires comptime" 33393 // to be known already before this function returns. 33394 _ = try sema.typeRequiresComptime(payload_ty); 33395 return sema.resolveTypeLayout(payload_ty); 33396 }, 33397 .ErrorUnion => { 33398 const payload_ty = ty.errorUnionPayload(mod); 33399 return sema.resolveTypeLayout(payload_ty); 33400 }, 33401 .Fn => { 33402 const info = mod.typeToFunc(ty).?; 33403 if (info.is_generic) { 33404 // Resolving of generic function types is deferred to when 33405 // the function is instantiated. 33406 return; 33407 } 33408 for (info.param_types) |param_ty| { 33409 try sema.resolveTypeLayout(param_ty.toType()); 33410 } 33411 try sema.resolveTypeLayout(info.return_type.toType()); 33412 }, 33413 else => {}, 33414 } 33415 } 33416 33417 fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { 33418 const mod = sema.mod; 33419 const resolved_ty = try sema.resolveTypeFields(ty); 33420 if (mod.typeToStruct(resolved_ty)) |struct_obj| { 33421 switch (struct_obj.status) { 33422 .none, .have_field_types => {}, 33423 .field_types_wip, .layout_wip => { 33424 const msg = try Module.ErrorMsg.create( 33425 sema.gpa, 33426 struct_obj.srcLoc(mod), 33427 "struct '{}' depends on itself", 33428 .{ty.fmt(mod)}, 33429 ); 33430 return sema.failWithOwnedErrorMsg(msg); 33431 }, 33432 .have_layout, .fully_resolved_wip, .fully_resolved => return, 33433 } 33434 const prev_status = struct_obj.status; 33435 errdefer if (struct_obj.status == .layout_wip) { 33436 struct_obj.status = prev_status; 33437 }; 33438 33439 struct_obj.status = .layout_wip; 33440 for (struct_obj.fields.values(), 0..) |field, i| { 33441 sema.resolveTypeLayout(field.ty) catch |err| switch (err) { 33442 error.AnalysisFail => { 33443 const msg = sema.err orelse return err; 33444 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); 33445 return err; 33446 }, 33447 else => return err, 33448 }; 33449 } 33450 33451 if (struct_obj.layout == .Packed) { 33452 try semaBackingIntType(mod, struct_obj); 33453 } 33454 33455 struct_obj.status = .have_layout; 33456 _ = try sema.resolveTypeRequiresComptime(resolved_ty); 33457 33458 if (struct_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) { 33459 const msg = try Module.ErrorMsg.create( 33460 sema.gpa, 33461 struct_obj.srcLoc(mod), 33462 "struct layout depends on it having runtime bits", 33463 .{}, 33464 ); 33465 return sema.failWithOwnedErrorMsg(msg); 33466 } 33467 33468 if (struct_obj.layout == .Auto and mod.backendSupportsFeature(.field_reordering)) { 33469 const optimized_order = try mod.tmp_hack_arena.allocator().alloc(u32, struct_obj.fields.count()); 33470 33471 for (struct_obj.fields.values(), 0..) |field, i| { 33472 optimized_order[i] = if (try sema.typeHasRuntimeBits(field.ty)) 33473 @as(u32, @intCast(i)) 33474 else 33475 Module.Struct.omitted_field; 33476 } 33477 33478 const AlignSortContext = struct { 33479 struct_obj: *Module.Struct, 33480 sema: *Sema, 33481 33482 fn lessThan(ctx: @This(), a: u32, b: u32) bool { 33483 const m = ctx.sema.mod; 33484 if (a == Module.Struct.omitted_field) return false; 33485 if (b == Module.Struct.omitted_field) return true; 33486 return ctx.struct_obj.fields.values()[a].ty.abiAlignment(m) > 33487 ctx.struct_obj.fields.values()[b].ty.abiAlignment(m); 33488 } 33489 }; 33490 mem.sort(u32, optimized_order, AlignSortContext{ 33491 .struct_obj = struct_obj, 33492 .sema = sema, 33493 }, AlignSortContext.lessThan); 33494 struct_obj.optimized_order = optimized_order.ptr; 33495 } 33496 } 33497 // otherwise it's a tuple; no need to resolve anything 33498 } 33499 33500 fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!void { 33501 const gpa = mod.gpa; 33502 33503 var fields_bit_sum: u64 = 0; 33504 for (struct_obj.fields.values()) |field| { 33505 fields_bit_sum += field.ty.bitSize(mod); 33506 } 33507 33508 const decl_index = struct_obj.owner_decl; 33509 const decl = mod.declPtr(decl_index); 33510 33511 const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir; 33512 const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; 33513 assert(extended.opcode == .struct_decl); 33514 const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small)); 33515 33516 if (small.has_backing_int) { 33517 var extra_index: usize = extended.operand; 33518 extra_index += @intFromBool(small.has_src_node); 33519 extra_index += @intFromBool(small.has_fields_len); 33520 extra_index += @intFromBool(small.has_decls_len); 33521 33522 const backing_int_body_len = zir.extra[extra_index]; 33523 extra_index += 1; 33524 33525 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 33526 defer analysis_arena.deinit(); 33527 33528 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); 33529 defer comptime_mutable_decls.deinit(); 33530 33531 var sema: Sema = .{ 33532 .mod = mod, 33533 .gpa = gpa, 33534 .arena = analysis_arena.allocator(), 33535 .code = zir, 33536 .owner_decl = decl, 33537 .owner_decl_index = decl_index, 33538 .func = null, 33539 .func_index = .none, 33540 .fn_ret_ty = Type.void, 33541 .owner_func = null, 33542 .owner_func_index = .none, 33543 .comptime_mutable_decls = &comptime_mutable_decls, 33544 }; 33545 defer sema.deinit(); 33546 33547 var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); 33548 defer wip_captures.deinit(); 33549 33550 var block: Block = .{ 33551 .parent = null, 33552 .sema = &sema, 33553 .src_decl = decl_index, 33554 .namespace = struct_obj.namespace, 33555 .wip_capture_scope = wip_captures.scope, 33556 .instructions = .{}, 33557 .inlining = null, 33558 .is_comptime = true, 33559 }; 33560 defer { 33561 assert(block.instructions.items.len == 0); 33562 block.params.deinit(gpa); 33563 } 33564 33565 const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 }; 33566 const backing_int_ty = blk: { 33567 if (backing_int_body_len == 0) { 33568 const backing_int_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 33569 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref); 33570 } else { 33571 const body = zir.extra[extra_index..][0..backing_int_body_len]; 33572 const ty_ref = try sema.resolveBody(&block, body, struct_obj.zir_index); 33573 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref); 33574 } 33575 }; 33576 33577 try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); 33578 struct_obj.backing_int_ty = backing_int_ty; 33579 try wip_captures.finalize(); 33580 for (comptime_mutable_decls.items) |ct_decl_index| { 33581 const ct_decl = mod.declPtr(ct_decl_index); 33582 try ct_decl.intern(mod); 33583 } 33584 } else { 33585 if (fields_bit_sum > std.math.maxInt(u16)) { 33586 var sema: Sema = .{ 33587 .mod = mod, 33588 .gpa = gpa, 33589 .arena = undefined, 33590 .code = zir, 33591 .owner_decl = decl, 33592 .owner_decl_index = decl_index, 33593 .func = null, 33594 .func_index = .none, 33595 .fn_ret_ty = Type.void, 33596 .owner_func = null, 33597 .owner_func_index = .none, 33598 .comptime_mutable_decls = undefined, 33599 }; 33600 defer sema.deinit(); 33601 33602 var block: Block = .{ 33603 .parent = null, 33604 .sema = &sema, 33605 .src_decl = decl_index, 33606 .namespace = struct_obj.namespace, 33607 .wip_capture_scope = undefined, 33608 .instructions = .{}, 33609 .inlining = null, 33610 .is_comptime = true, 33611 }; 33612 return sema.fail(&block, LazySrcLoc.nodeOffset(0), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum}); 33613 } 33614 struct_obj.backing_int_ty = try mod.intType(.unsigned, @as(u16, @intCast(fields_bit_sum))); 33615 } 33616 } 33617 33618 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { 33619 const mod = sema.mod; 33620 33621 if (!backing_int_ty.isInt(mod)) { 33622 return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)}); 33623 } 33624 if (backing_int_ty.bitSize(mod) != fields_bit_sum) { 33625 return sema.fail( 33626 block, 33627 src, 33628 "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}", 33629 .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(mod), fields_bit_sum }, 33630 ); 33631 } 33632 } 33633 33634 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 33635 const mod = sema.mod; 33636 if (!ty.isIndexable(mod)) { 33637 const msg = msg: { 33638 const msg = try sema.errMsg(block, src, "type '{}' does not support indexing", .{ty.fmt(sema.mod)}); 33639 errdefer msg.destroy(sema.gpa); 33640 try sema.errNote(block, src, msg, "operand must be an array, slice, tuple, or vector", .{}); 33641 break :msg msg; 33642 }; 33643 return sema.failWithOwnedErrorMsg(msg); 33644 } 33645 } 33646 33647 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { 33648 const mod = sema.mod; 33649 if (ty.zigTypeTag(mod) == .Pointer) { 33650 switch (ty.ptrSize(mod)) { 33651 .Slice, .Many, .C => return, 33652 .One => { 33653 const elem_ty = ty.childType(mod); 33654 if (elem_ty.zigTypeTag(mod) == .Array) return; 33655 // TODO https://github.com/ziglang/zig/issues/15479 33656 // if (elem_ty.isTuple()) return; 33657 }, 33658 } 33659 } 33660 const msg = msg: { 33661 const msg = try sema.errMsg(block, src, "type '{}' is not an indexable pointer", .{ty.fmt(sema.mod)}); 33662 errdefer msg.destroy(sema.gpa); 33663 try sema.errNote(block, src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); 33664 break :msg msg; 33665 }; 33666 return sema.failWithOwnedErrorMsg(msg); 33667 } 33668 33669 fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { 33670 const mod = sema.mod; 33671 const resolved_ty = try sema.resolveTypeFields(ty); 33672 const union_obj = mod.typeToUnion(resolved_ty).?; 33673 switch (union_obj.status) { 33674 .none, .have_field_types => {}, 33675 .field_types_wip, .layout_wip => { 33676 const msg = try Module.ErrorMsg.create( 33677 sema.gpa, 33678 union_obj.srcLoc(sema.mod), 33679 "union '{}' depends on itself", 33680 .{ty.fmt(sema.mod)}, 33681 ); 33682 return sema.failWithOwnedErrorMsg(msg); 33683 }, 33684 .have_layout, .fully_resolved_wip, .fully_resolved => return, 33685 } 33686 const prev_status = union_obj.status; 33687 errdefer if (union_obj.status == .layout_wip) { 33688 union_obj.status = prev_status; 33689 }; 33690 33691 union_obj.status = .layout_wip; 33692 for (union_obj.fields.values(), 0..) |field, i| { 33693 sema.resolveTypeLayout(field.ty) catch |err| switch (err) { 33694 error.AnalysisFail => { 33695 const msg = sema.err orelse return err; 33696 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); 33697 return err; 33698 }, 33699 else => return err, 33700 }; 33701 } 33702 union_obj.status = .have_layout; 33703 _ = try sema.resolveTypeRequiresComptime(resolved_ty); 33704 33705 if (union_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) { 33706 const msg = try Module.ErrorMsg.create( 33707 sema.gpa, 33708 union_obj.srcLoc(sema.mod), 33709 "union layout depends on it having runtime bits", 33710 .{}, 33711 ); 33712 return sema.failWithOwnedErrorMsg(msg); 33713 } 33714 } 33715 33716 // In case of querying the ABI alignment of this struct, we will ask 33717 // for hasRuntimeBits() of each field, so we need "requires comptime" 33718 // to be known already before this function returns. 33719 pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { 33720 const mod = sema.mod; 33721 33722 return switch (ty.toIntern()) { 33723 .empty_struct_type => false, 33724 else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 33725 .int_type => false, 33726 .ptr_type => |ptr_type| { 33727 const child_ty = ptr_type.child.toType(); 33728 if (child_ty.zigTypeTag(mod) == .Fn) { 33729 return mod.typeToFunc(child_ty).?.is_generic; 33730 } else { 33731 return sema.resolveTypeRequiresComptime(child_ty); 33732 } 33733 }, 33734 .anyframe_type => |child| { 33735 if (child == .none) return false; 33736 return sema.resolveTypeRequiresComptime(child.toType()); 33737 }, 33738 .array_type => |array_type| return sema.resolveTypeRequiresComptime(array_type.child.toType()), 33739 .vector_type => |vector_type| return sema.resolveTypeRequiresComptime(vector_type.child.toType()), 33740 .opt_type => |child| return sema.resolveTypeRequiresComptime(child.toType()), 33741 .error_union_type => |error_union_type| return sema.resolveTypeRequiresComptime(error_union_type.payload_type.toType()), 33742 .error_set_type, .inferred_error_set_type => false, 33743 33744 .func_type => true, 33745 33746 .simple_type => |t| switch (t) { 33747 .f16, 33748 .f32, 33749 .f64, 33750 .f80, 33751 .f128, 33752 .usize, 33753 .isize, 33754 .c_char, 33755 .c_short, 33756 .c_ushort, 33757 .c_int, 33758 .c_uint, 33759 .c_long, 33760 .c_ulong, 33761 .c_longlong, 33762 .c_ulonglong, 33763 .c_longdouble, 33764 .anyopaque, 33765 .bool, 33766 .void, 33767 .anyerror, 33768 .noreturn, 33769 .generic_poison, 33770 .atomic_order, 33771 .atomic_rmw_op, 33772 .calling_convention, 33773 .address_space, 33774 .float_mode, 33775 .reduce_op, 33776 .call_modifier, 33777 .prefetch_options, 33778 .export_options, 33779 .extern_options, 33780 => false, 33781 33782 .type, 33783 .comptime_int, 33784 .comptime_float, 33785 .null, 33786 .undefined, 33787 .enum_literal, 33788 .type_info, 33789 => true, 33790 }, 33791 .struct_type => |struct_type| { 33792 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; 33793 switch (struct_obj.requires_comptime) { 33794 .no, .wip => return false, 33795 .yes => return true, 33796 .unknown => { 33797 var requires_comptime = false; 33798 struct_obj.requires_comptime = .wip; 33799 for (struct_obj.fields.values()) |field| { 33800 if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; 33801 } 33802 if (requires_comptime) { 33803 struct_obj.requires_comptime = .yes; 33804 } else { 33805 struct_obj.requires_comptime = .no; 33806 } 33807 return requires_comptime; 33808 }, 33809 } 33810 }, 33811 33812 .anon_struct_type => |tuple| { 33813 for (tuple.types, tuple.values) |field_ty, field_val| { 33814 const have_comptime_val = field_val != .none; 33815 if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty.toType())) { 33816 return true; 33817 } 33818 } 33819 return false; 33820 }, 33821 33822 .union_type => |union_type| { 33823 const union_obj = mod.unionPtr(union_type.index); 33824 switch (union_obj.requires_comptime) { 33825 .no, .wip => return false, 33826 .yes => return true, 33827 .unknown => { 33828 var requires_comptime = false; 33829 union_obj.requires_comptime = .wip; 33830 for (union_obj.fields.values()) |field| { 33831 if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; 33832 } 33833 if (requires_comptime) { 33834 union_obj.requires_comptime = .yes; 33835 } else { 33836 union_obj.requires_comptime = .no; 33837 } 33838 return requires_comptime; 33839 }, 33840 } 33841 }, 33842 33843 .opaque_type => false, 33844 33845 .enum_type => |enum_type| try sema.resolveTypeRequiresComptime(enum_type.tag_ty.toType()), 33846 33847 // values, not types 33848 .undef, 33849 .runtime_value, 33850 .simple_value, 33851 .variable, 33852 .extern_func, 33853 .func, 33854 .int, 33855 .err, 33856 .error_union, 33857 .enum_literal, 33858 .enum_tag, 33859 .empty_enum_value, 33860 .float, 33861 .ptr, 33862 .opt, 33863 .aggregate, 33864 .un, 33865 // memoization, not types 33866 .memoized_call, 33867 => unreachable, 33868 }, 33869 }; 33870 } 33871 33872 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to 33873 /// be resolved. 33874 pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { 33875 const mod = sema.mod; 33876 switch (ty.zigTypeTag(mod)) { 33877 .Pointer => { 33878 const child_ty = try sema.resolveTypeFields(ty.childType(mod)); 33879 return sema.resolveTypeFully(child_ty); 33880 }, 33881 .Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 33882 .struct_type => return sema.resolveStructFully(ty), 33883 .anon_struct_type => |tuple| { 33884 for (tuple.types) |field_ty| { 33885 try sema.resolveTypeFully(field_ty.toType()); 33886 } 33887 }, 33888 else => {}, 33889 }, 33890 .Union => return sema.resolveUnionFully(ty), 33891 .Array => return sema.resolveTypeFully(ty.childType(mod)), 33892 .Optional => { 33893 return sema.resolveTypeFully(ty.optionalChild(mod)); 33894 }, 33895 .ErrorUnion => return sema.resolveTypeFully(ty.errorUnionPayload(mod)), 33896 .Fn => { 33897 const info = mod.typeToFunc(ty).?; 33898 if (info.is_generic) { 33899 // Resolving of generic function types is deferred to when 33900 // the function is instantiated. 33901 return; 33902 } 33903 for (info.param_types) |param_ty| { 33904 try sema.resolveTypeFully(param_ty.toType()); 33905 } 33906 try sema.resolveTypeFully(info.return_type.toType()); 33907 }, 33908 else => {}, 33909 } 33910 } 33911 33912 fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { 33913 try sema.resolveStructLayout(ty); 33914 33915 const mod = sema.mod; 33916 const resolved_ty = try sema.resolveTypeFields(ty); 33917 const struct_obj = mod.typeToStruct(resolved_ty).?; 33918 33919 switch (struct_obj.status) { 33920 .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, 33921 .fully_resolved_wip, .fully_resolved => return, 33922 } 33923 33924 { 33925 // After we have resolve struct layout we have to go over the fields again to 33926 // make sure pointer fields get their child types resolved as well. 33927 // See also similar code for unions. 33928 const prev_status = struct_obj.status; 33929 errdefer struct_obj.status = prev_status; 33930 33931 struct_obj.status = .fully_resolved_wip; 33932 for (struct_obj.fields.values()) |field| { 33933 try sema.resolveTypeFully(field.ty); 33934 } 33935 struct_obj.status = .fully_resolved; 33936 } 33937 33938 // And let's not forget comptime-only status. 33939 _ = try sema.typeRequiresComptime(ty); 33940 } 33941 33942 fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { 33943 try sema.resolveUnionLayout(ty); 33944 33945 const mod = sema.mod; 33946 const resolved_ty = try sema.resolveTypeFields(ty); 33947 const union_obj = mod.typeToUnion(resolved_ty).?; 33948 switch (union_obj.status) { 33949 .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, 33950 .fully_resolved_wip, .fully_resolved => return, 33951 } 33952 33953 { 33954 // After we have resolve union layout we have to go over the fields again to 33955 // make sure pointer fields get their child types resolved as well. 33956 // See also similar code for structs. 33957 const prev_status = union_obj.status; 33958 errdefer union_obj.status = prev_status; 33959 33960 union_obj.status = .fully_resolved_wip; 33961 for (union_obj.fields.values()) |field| { 33962 try sema.resolveTypeFully(field.ty); 33963 } 33964 union_obj.status = .fully_resolved; 33965 } 33966 33967 // And let's not forget comptime-only status. 33968 _ = try sema.typeRequiresComptime(ty); 33969 } 33970 33971 pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type { 33972 const mod = sema.mod; 33973 33974 switch (ty.toIntern()) { 33975 .var_args_param_type => unreachable, 33976 33977 .none => unreachable, 33978 33979 .u0_type, 33980 .i0_type, 33981 .u1_type, 33982 .u8_type, 33983 .i8_type, 33984 .u16_type, 33985 .i16_type, 33986 .u29_type, 33987 .u32_type, 33988 .i32_type, 33989 .u64_type, 33990 .i64_type, 33991 .u80_type, 33992 .u128_type, 33993 .i128_type, 33994 .usize_type, 33995 .isize_type, 33996 .c_char_type, 33997 .c_short_type, 33998 .c_ushort_type, 33999 .c_int_type, 34000 .c_uint_type, 34001 .c_long_type, 34002 .c_ulong_type, 34003 .c_longlong_type, 34004 .c_ulonglong_type, 34005 .c_longdouble_type, 34006 .f16_type, 34007 .f32_type, 34008 .f64_type, 34009 .f80_type, 34010 .f128_type, 34011 .anyopaque_type, 34012 .bool_type, 34013 .void_type, 34014 .type_type, 34015 .anyerror_type, 34016 .comptime_int_type, 34017 .comptime_float_type, 34018 .noreturn_type, 34019 .anyframe_type, 34020 .null_type, 34021 .undefined_type, 34022 .enum_literal_type, 34023 .manyptr_u8_type, 34024 .manyptr_const_u8_type, 34025 .manyptr_const_u8_sentinel_0_type, 34026 .single_const_pointer_to_comptime_int_type, 34027 .slice_const_u8_type, 34028 .slice_const_u8_sentinel_0_type, 34029 .optional_noreturn_type, 34030 .anyerror_void_error_union_type, 34031 .generic_poison_type, 34032 .empty_struct_type, 34033 => return ty, 34034 34035 .undef => unreachable, 34036 .zero => unreachable, 34037 .zero_usize => unreachable, 34038 .zero_u8 => unreachable, 34039 .one => unreachable, 34040 .one_usize => unreachable, 34041 .one_u8 => unreachable, 34042 .four_u8 => unreachable, 34043 .negative_one => unreachable, 34044 .calling_convention_c => unreachable, 34045 .calling_convention_inline => unreachable, 34046 .void_value => unreachable, 34047 .unreachable_value => unreachable, 34048 .null_value => unreachable, 34049 .bool_true => unreachable, 34050 .bool_false => unreachable, 34051 .empty_struct => unreachable, 34052 .generic_poison => unreachable, 34053 34054 .type_info_type => return sema.getBuiltinType("Type"), 34055 .extern_options_type => return sema.getBuiltinType("ExternOptions"), 34056 .export_options_type => return sema.getBuiltinType("ExportOptions"), 34057 .atomic_order_type => return sema.getBuiltinType("AtomicOrder"), 34058 .atomic_rmw_op_type => return sema.getBuiltinType("AtomicRmwOp"), 34059 .calling_convention_type => return sema.getBuiltinType("CallingConvention"), 34060 .address_space_type => return sema.getBuiltinType("AddressSpace"), 34061 .float_mode_type => return sema.getBuiltinType("FloatMode"), 34062 .reduce_op_type => return sema.getBuiltinType("ReduceOp"), 34063 .call_modifier_type => return sema.getBuiltinType("CallModifier"), 34064 .prefetch_options_type => return sema.getBuiltinType("PrefetchOptions"), 34065 34066 _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) { 34067 .type_struct, 34068 .type_struct_ns, 34069 .type_union_tagged, 34070 .type_union_untagged, 34071 .type_union_safety, 34072 => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 34073 .struct_type => |struct_type| { 34074 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return ty; 34075 try sema.resolveTypeFieldsStruct(ty, struct_obj); 34076 return ty; 34077 }, 34078 .union_type => |union_type| { 34079 const union_obj = mod.unionPtr(union_type.index); 34080 try sema.resolveTypeFieldsUnion(ty, union_obj); 34081 return ty; 34082 }, 34083 else => unreachable, 34084 }, 34085 else => return ty, 34086 }, 34087 } 34088 } 34089 34090 fn resolveTypeFieldsStruct( 34091 sema: *Sema, 34092 ty: Type, 34093 struct_obj: *Module.Struct, 34094 ) CompileError!void { 34095 switch (sema.mod.declPtr(struct_obj.owner_decl).analysis) { 34096 .file_failure, 34097 .dependency_failure, 34098 .sema_failure, 34099 .sema_failure_retryable, 34100 => { 34101 sema.owner_decl.analysis = .dependency_failure; 34102 sema.owner_decl.generation = sema.mod.generation; 34103 return error.AnalysisFail; 34104 }, 34105 else => {}, 34106 } 34107 switch (struct_obj.status) { 34108 .none => {}, 34109 .field_types_wip => { 34110 const msg = try Module.ErrorMsg.create( 34111 sema.gpa, 34112 struct_obj.srcLoc(sema.mod), 34113 "struct '{}' depends on itself", 34114 .{ty.fmt(sema.mod)}, 34115 ); 34116 return sema.failWithOwnedErrorMsg(msg); 34117 }, 34118 .have_field_types, 34119 .have_layout, 34120 .layout_wip, 34121 .fully_resolved_wip, 34122 .fully_resolved, 34123 => return, 34124 } 34125 34126 struct_obj.status = .field_types_wip; 34127 errdefer struct_obj.status = .none; 34128 try semaStructFields(sema.mod, struct_obj); 34129 } 34130 34131 fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void { 34132 switch (sema.mod.declPtr(union_obj.owner_decl).analysis) { 34133 .file_failure, 34134 .dependency_failure, 34135 .sema_failure, 34136 .sema_failure_retryable, 34137 => { 34138 sema.owner_decl.analysis = .dependency_failure; 34139 sema.owner_decl.generation = sema.mod.generation; 34140 return error.AnalysisFail; 34141 }, 34142 else => {}, 34143 } 34144 switch (union_obj.status) { 34145 .none => {}, 34146 .field_types_wip => { 34147 const msg = try Module.ErrorMsg.create( 34148 sema.gpa, 34149 union_obj.srcLoc(sema.mod), 34150 "union '{}' depends on itself", 34151 .{ty.fmt(sema.mod)}, 34152 ); 34153 return sema.failWithOwnedErrorMsg(msg); 34154 }, 34155 .have_field_types, 34156 .have_layout, 34157 .layout_wip, 34158 .fully_resolved_wip, 34159 .fully_resolved, 34160 => return, 34161 } 34162 34163 union_obj.status = .field_types_wip; 34164 errdefer union_obj.status = .none; 34165 try semaUnionFields(sema.mod, union_obj); 34166 union_obj.status = .have_field_types; 34167 } 34168 34169 fn resolveInferredErrorSet( 34170 sema: *Sema, 34171 block: *Block, 34172 src: LazySrcLoc, 34173 ies_index: Module.Fn.InferredErrorSet.Index, 34174 ) CompileError!void { 34175 const mod = sema.mod; 34176 const ies = mod.inferredErrorSetPtr(ies_index); 34177 34178 if (ies.is_resolved) return; 34179 34180 const func = mod.funcPtr(ies.func); 34181 if (func.state == .in_progress) { 34182 return sema.fail(block, src, "unable to resolve inferred error set", .{}); 34183 } 34184 34185 // In order to ensure that all dependencies are properly added to the set, we 34186 // need to ensure the function body is analyzed of the inferred error set. 34187 // However, in the case of comptime/inline function calls with inferred error sets, 34188 // each call gets a new InferredErrorSet object, which contains the same 34189 // `Module.Fn.Index`. Not only is the function not relevant to the inferred error set 34190 // in this case, it may be a generic function which would cause an assertion failure 34191 // if we called `ensureFuncBodyAnalyzed` on it here. 34192 const ies_func_owner_decl = mod.declPtr(func.owner_decl); 34193 const ies_func_info = mod.typeToFunc(ies_func_owner_decl.ty).?; 34194 // if ies declared by a inline function with generic return type, the return_type should be generic_poison, 34195 // because inline function does not create a new declaration, and the ies has been filled with analyzeCall, 34196 // so here we can simply skip this case. 34197 if (ies_func_info.return_type == .generic_poison_type) { 34198 assert(ies_func_info.cc == .Inline); 34199 } else if (mod.typeToInferredErrorSet(ies_func_info.return_type.toType().errorUnionSet(mod)).? == ies) { 34200 if (ies_func_info.is_generic) { 34201 const msg = msg: { 34202 const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{}); 34203 errdefer msg.destroy(sema.gpa); 34204 34205 try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(mod), msg, "generic function declared here", .{}); 34206 break :msg msg; 34207 }; 34208 return sema.failWithOwnedErrorMsg(msg); 34209 } 34210 // In this case we are dealing with the actual InferredErrorSet object that 34211 // corresponds to the function, not one created to track an inline/comptime call. 34212 try sema.ensureFuncBodyAnalyzed(ies.func); 34213 } 34214 34215 ies.is_resolved = true; 34216 34217 for (ies.inferred_error_sets.keys()) |other_ies_index| { 34218 if (ies_index == other_ies_index) continue; 34219 try sema.resolveInferredErrorSet(block, src, other_ies_index); 34220 34221 const other_ies = mod.inferredErrorSetPtr(other_ies_index); 34222 for (other_ies.errors.keys()) |key| { 34223 try ies.errors.put(sema.gpa, key, {}); 34224 } 34225 if (other_ies.is_anyerror) 34226 ies.is_anyerror = true; 34227 } 34228 } 34229 34230 fn resolveInferredErrorSetTy( 34231 sema: *Sema, 34232 block: *Block, 34233 src: LazySrcLoc, 34234 ty: Type, 34235 ) CompileError!void { 34236 const mod = sema.mod; 34237 if (mod.typeToInferredErrorSetIndex(ty).unwrap()) |ies_index| { 34238 try sema.resolveInferredErrorSet(block, src, ies_index); 34239 } 34240 } 34241 34242 fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void { 34243 const gpa = mod.gpa; 34244 const ip = &mod.intern_pool; 34245 const decl_index = struct_obj.owner_decl; 34246 const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir; 34247 const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; 34248 assert(extended.opcode == .struct_decl); 34249 const small = @as(Zir.Inst.StructDecl.Small, @bitCast(extended.small)); 34250 var extra_index: usize = extended.operand; 34251 34252 const src = LazySrcLoc.nodeOffset(0); 34253 extra_index += @intFromBool(small.has_src_node); 34254 34255 const fields_len = if (small.has_fields_len) blk: { 34256 const fields_len = zir.extra[extra_index]; 34257 extra_index += 1; 34258 break :blk fields_len; 34259 } else 0; 34260 34261 const decls_len = if (small.has_decls_len) decls_len: { 34262 const decls_len = zir.extra[extra_index]; 34263 extra_index += 1; 34264 break :decls_len decls_len; 34265 } else 0; 34266 34267 // The backing integer cannot be handled until `resolveStructLayout()`. 34268 if (small.has_backing_int) { 34269 const backing_int_body_len = zir.extra[extra_index]; 34270 extra_index += 1; // backing_int_body_len 34271 if (backing_int_body_len == 0) { 34272 extra_index += 1; // backing_int_ref 34273 } else { 34274 extra_index += backing_int_body_len; // backing_int_body_inst 34275 } 34276 } 34277 34278 // Skip over decls. 34279 var decls_it = zir.declIteratorInner(extra_index, decls_len); 34280 while (decls_it.next()) |_| {} 34281 extra_index = decls_it.extra_index; 34282 34283 if (fields_len == 0) { 34284 if (struct_obj.layout == .Packed) { 34285 try semaBackingIntType(mod, struct_obj); 34286 } 34287 struct_obj.status = .have_layout; 34288 return; 34289 } 34290 34291 const decl = mod.declPtr(decl_index); 34292 34293 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 34294 defer analysis_arena.deinit(); 34295 34296 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); 34297 defer comptime_mutable_decls.deinit(); 34298 34299 var sema: Sema = .{ 34300 .mod = mod, 34301 .gpa = gpa, 34302 .arena = analysis_arena.allocator(), 34303 .code = zir, 34304 .owner_decl = decl, 34305 .owner_decl_index = decl_index, 34306 .func = null, 34307 .func_index = .none, 34308 .fn_ret_ty = Type.void, 34309 .owner_func = null, 34310 .owner_func_index = .none, 34311 .comptime_mutable_decls = &comptime_mutable_decls, 34312 }; 34313 defer sema.deinit(); 34314 34315 var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); 34316 defer wip_captures.deinit(); 34317 34318 var block_scope: Block = .{ 34319 .parent = null, 34320 .sema = &sema, 34321 .src_decl = decl_index, 34322 .namespace = struct_obj.namespace, 34323 .wip_capture_scope = wip_captures.scope, 34324 .instructions = .{}, 34325 .inlining = null, 34326 .is_comptime = true, 34327 }; 34328 defer { 34329 assert(block_scope.instructions.items.len == 0); 34330 block_scope.params.deinit(gpa); 34331 } 34332 34333 struct_obj.fields = .{}; 34334 try struct_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); 34335 34336 const Field = struct { 34337 type_body_len: u32 = 0, 34338 align_body_len: u32 = 0, 34339 init_body_len: u32 = 0, 34340 type_ref: Zir.Inst.Ref = .none, 34341 }; 34342 const fields = try sema.arena.alloc(Field, fields_len); 34343 var any_inits = false; 34344 34345 { 34346 const bits_per_field = 4; 34347 const fields_per_u32 = 32 / bits_per_field; 34348 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 34349 const flags_index = extra_index; 34350 var bit_bag_index: usize = flags_index; 34351 extra_index += bit_bags_count; 34352 var cur_bit_bag: u32 = undefined; 34353 var field_i: u32 = 0; 34354 while (field_i < fields_len) : (field_i += 1) { 34355 if (field_i % fields_per_u32 == 0) { 34356 cur_bit_bag = zir.extra[bit_bag_index]; 34357 bit_bag_index += 1; 34358 } 34359 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 34360 cur_bit_bag >>= 1; 34361 const has_init = @as(u1, @truncate(cur_bit_bag)) != 0; 34362 cur_bit_bag >>= 1; 34363 const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0; 34364 cur_bit_bag >>= 1; 34365 const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; 34366 cur_bit_bag >>= 1; 34367 34368 var field_name_zir: ?[:0]const u8 = null; 34369 if (!small.is_tuple) { 34370 field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); 34371 extra_index += 1; 34372 } 34373 extra_index += 1; // doc_comment 34374 34375 fields[field_i] = .{}; 34376 34377 if (has_type_body) { 34378 fields[field_i].type_body_len = zir.extra[extra_index]; 34379 } else { 34380 fields[field_i].type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34381 } 34382 extra_index += 1; 34383 34384 // This string needs to outlive the ZIR code. 34385 const field_name = try ip.getOrPutString(gpa, if (field_name_zir) |s| 34386 s 34387 else 34388 try std.fmt.allocPrint(sema.arena, "{d}", .{field_i})); 34389 34390 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); 34391 if (gop.found_existing) { 34392 const msg = msg: { 34393 const field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i }).lazy; 34394 const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{}'", .{field_name.fmt(ip)}); 34395 errdefer msg.destroy(gpa); 34396 34397 const prev_field_index = struct_obj.fields.getIndex(field_name).?; 34398 const prev_field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = prev_field_index }); 34399 try mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{}); 34400 try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); 34401 break :msg msg; 34402 }; 34403 return sema.failWithOwnedErrorMsg(msg); 34404 } 34405 gop.value_ptr.* = .{ 34406 .ty = Type.noreturn, 34407 .abi_align = .none, 34408 .default_val = .none, 34409 .is_comptime = is_comptime, 34410 .offset = undefined, 34411 }; 34412 34413 if (has_align) { 34414 fields[field_i].align_body_len = zir.extra[extra_index]; 34415 extra_index += 1; 34416 } 34417 if (has_init) { 34418 fields[field_i].init_body_len = zir.extra[extra_index]; 34419 extra_index += 1; 34420 any_inits = true; 34421 } 34422 } 34423 } 34424 34425 // Next we do only types and alignments, saving the inits for a second pass, 34426 // so that init values may depend on type layout. 34427 const bodies_index = extra_index; 34428 34429 for (fields, 0..) |zir_field, field_i| { 34430 const field_ty: Type = ty: { 34431 if (zir_field.type_ref != .none) { 34432 break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) { 34433 error.NeededSourceLocation => { 34434 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34435 .index = field_i, 34436 .range = .type, 34437 }).lazy; 34438 _ = try sema.resolveType(&block_scope, ty_src, zir_field.type_ref); 34439 unreachable; 34440 }, 34441 else => |e| return e, 34442 }; 34443 } 34444 assert(zir_field.type_body_len != 0); 34445 const body = zir.extra[extra_index..][0..zir_field.type_body_len]; 34446 extra_index += body.len; 34447 const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); 34448 break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) { 34449 error.NeededSourceLocation => { 34450 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34451 .index = field_i, 34452 .range = .type, 34453 }).lazy; 34454 _ = try sema.analyzeAsType(&block_scope, ty_src, ty_ref); 34455 unreachable; 34456 }, 34457 else => |e| return e, 34458 }; 34459 }; 34460 if (field_ty.isGenericPoison()) { 34461 return error.GenericPoison; 34462 } 34463 34464 const field = &struct_obj.fields.values()[field_i]; 34465 field.ty = field_ty; 34466 34467 if (field_ty.zigTypeTag(mod) == .Opaque) { 34468 const msg = msg: { 34469 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34470 .index = field_i, 34471 .range = .type, 34472 }).lazy; 34473 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); 34474 errdefer msg.destroy(sema.gpa); 34475 34476 try sema.addDeclaredHereNote(msg, field_ty); 34477 break :msg msg; 34478 }; 34479 return sema.failWithOwnedErrorMsg(msg); 34480 } 34481 if (field_ty.zigTypeTag(mod) == .NoReturn) { 34482 const msg = msg: { 34483 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34484 .index = field_i, 34485 .range = .type, 34486 }).lazy; 34487 const msg = try sema.errMsg(&block_scope, ty_src, "struct fields cannot be 'noreturn'", .{}); 34488 errdefer msg.destroy(sema.gpa); 34489 34490 try sema.addDeclaredHereNote(msg, field_ty); 34491 break :msg msg; 34492 }; 34493 return sema.failWithOwnedErrorMsg(msg); 34494 } 34495 if (struct_obj.layout == .Extern and !try sema.validateExternType(field.ty, .struct_field)) { 34496 const msg = msg: { 34497 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34498 .index = field_i, 34499 .range = .type, 34500 }); 34501 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)}); 34502 errdefer msg.destroy(sema.gpa); 34503 34504 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field.ty, .struct_field); 34505 34506 try sema.addDeclaredHereNote(msg, field.ty); 34507 break :msg msg; 34508 }; 34509 return sema.failWithOwnedErrorMsg(msg); 34510 } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty, mod))) { 34511 const msg = msg: { 34512 const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34513 .index = field_i, 34514 .range = .type, 34515 }); 34516 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(mod)}); 34517 errdefer msg.destroy(sema.gpa); 34518 34519 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field.ty); 34520 34521 try sema.addDeclaredHereNote(msg, field.ty); 34522 break :msg msg; 34523 }; 34524 return sema.failWithOwnedErrorMsg(msg); 34525 } 34526 34527 if (zir_field.align_body_len > 0) { 34528 const body = zir.extra[extra_index..][0..zir_field.align_body_len]; 34529 extra_index += body.len; 34530 const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); 34531 field.abi_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { 34532 error.NeededSourceLocation => { 34533 const align_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34534 .index = field_i, 34535 .range = .alignment, 34536 }).lazy; 34537 _ = try sema.analyzeAsAlign(&block_scope, align_src, align_ref); 34538 unreachable; 34539 }, 34540 else => |e| return e, 34541 }; 34542 } 34543 34544 extra_index += zir_field.init_body_len; 34545 } 34546 34547 struct_obj.status = .have_field_types; 34548 34549 if (any_inits) { 34550 extra_index = bodies_index; 34551 for (fields, 0..) |zir_field, field_i| { 34552 extra_index += zir_field.type_body_len; 34553 extra_index += zir_field.align_body_len; 34554 if (zir_field.init_body_len > 0) { 34555 const body = zir.extra[extra_index..][0..zir_field.init_body_len]; 34556 extra_index += body.len; 34557 const init = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); 34558 const field = &struct_obj.fields.values()[field_i]; 34559 const coerced = sema.coerce(&block_scope, field.ty, init, .unneeded) catch |err| switch (err) { 34560 error.NeededSourceLocation => { 34561 const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34562 .index = field_i, 34563 .range = .value, 34564 }).lazy; 34565 _ = try sema.coerce(&block_scope, field.ty, init, init_src); 34566 unreachable; 34567 }, 34568 else => |e| return e, 34569 }; 34570 const default_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { 34571 const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ 34572 .index = field_i, 34573 .range = .value, 34574 }).lazy; 34575 return sema.failWithNeededComptime(&block_scope, init_src, "struct field default value must be comptime-known"); 34576 }; 34577 field.default_val = try default_val.intern(field.ty, mod); 34578 } 34579 } 34580 } 34581 try wip_captures.finalize(); 34582 for (comptime_mutable_decls.items) |ct_decl_index| { 34583 const ct_decl = mod.declPtr(ct_decl_index); 34584 try ct_decl.intern(mod); 34585 } 34586 34587 struct_obj.have_field_inits = true; 34588 } 34589 34590 fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { 34591 const tracy = trace(@src()); 34592 defer tracy.end(); 34593 34594 const gpa = mod.gpa; 34595 const ip = &mod.intern_pool; 34596 const decl_index = union_obj.owner_decl; 34597 const zir = mod.namespacePtr(union_obj.namespace).file_scope.zir; 34598 const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; 34599 assert(extended.opcode == .union_decl); 34600 const small = @as(Zir.Inst.UnionDecl.Small, @bitCast(extended.small)); 34601 var extra_index: usize = extended.operand; 34602 34603 const src = LazySrcLoc.nodeOffset(0); 34604 extra_index += @intFromBool(small.has_src_node); 34605 34606 const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { 34607 const ty_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34608 extra_index += 1; 34609 break :blk ty_ref; 34610 } else .none; 34611 34612 const body_len = if (small.has_body_len) blk: { 34613 const body_len = zir.extra[extra_index]; 34614 extra_index += 1; 34615 break :blk body_len; 34616 } else 0; 34617 34618 const fields_len = if (small.has_fields_len) blk: { 34619 const fields_len = zir.extra[extra_index]; 34620 extra_index += 1; 34621 break :blk fields_len; 34622 } else 0; 34623 34624 const decls_len = if (small.has_decls_len) decls_len: { 34625 const decls_len = zir.extra[extra_index]; 34626 extra_index += 1; 34627 break :decls_len decls_len; 34628 } else 0; 34629 34630 // Skip over decls. 34631 var decls_it = zir.declIteratorInner(extra_index, decls_len); 34632 while (decls_it.next()) |_| {} 34633 extra_index = decls_it.extra_index; 34634 34635 const body = zir.extra[extra_index..][0..body_len]; 34636 extra_index += body.len; 34637 34638 const decl = mod.declPtr(decl_index); 34639 34640 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 34641 defer analysis_arena.deinit(); 34642 34643 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); 34644 defer comptime_mutable_decls.deinit(); 34645 34646 var sema: Sema = .{ 34647 .mod = mod, 34648 .gpa = gpa, 34649 .arena = analysis_arena.allocator(), 34650 .code = zir, 34651 .owner_decl = decl, 34652 .owner_decl_index = decl_index, 34653 .func = null, 34654 .func_index = .none, 34655 .fn_ret_ty = Type.void, 34656 .owner_func = null, 34657 .owner_func_index = .none, 34658 .comptime_mutable_decls = &comptime_mutable_decls, 34659 }; 34660 defer sema.deinit(); 34661 34662 var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope); 34663 defer wip_captures.deinit(); 34664 34665 var block_scope: Block = .{ 34666 .parent = null, 34667 .sema = &sema, 34668 .src_decl = decl_index, 34669 .namespace = union_obj.namespace, 34670 .wip_capture_scope = wip_captures.scope, 34671 .instructions = .{}, 34672 .inlining = null, 34673 .is_comptime = true, 34674 }; 34675 defer { 34676 assert(block_scope.instructions.items.len == 0); 34677 block_scope.params.deinit(gpa); 34678 } 34679 34680 if (body.len != 0) { 34681 try sema.analyzeBody(&block_scope, body); 34682 } 34683 34684 try wip_captures.finalize(); 34685 for (comptime_mutable_decls.items) |ct_decl_index| { 34686 const ct_decl = mod.declPtr(ct_decl_index); 34687 try ct_decl.intern(mod); 34688 } 34689 34690 try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); 34691 34692 var int_tag_ty: Type = undefined; 34693 var enum_field_names: []InternPool.NullTerminatedString = &.{}; 34694 var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}; 34695 var explicit_tags_seen: []bool = &.{}; 34696 if (tag_type_ref != .none) { 34697 const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; 34698 const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); 34699 if (small.auto_enum_tag) { 34700 // The provided type is an integer type and we must construct the enum tag type here. 34701 int_tag_ty = provided_ty; 34702 if (int_tag_ty.zigTypeTag(mod) != .Int and int_tag_ty.zigTypeTag(mod) != .ComptimeInt) { 34703 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(mod)}); 34704 } 34705 34706 if (fields_len > 0) { 34707 const field_count_val = try mod.intValue(Type.comptime_int, fields_len - 1); 34708 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) { 34709 const msg = msg: { 34710 const msg = try sema.errMsg(&block_scope, tag_ty_src, "specified integer tag type cannot represent every field", .{}); 34711 errdefer msg.destroy(sema.gpa); 34712 try sema.errNote(&block_scope, tag_ty_src, msg, "type '{}' cannot fit values in range 0...{d}", .{ 34713 int_tag_ty.fmt(mod), 34714 fields_len - 1, 34715 }); 34716 break :msg msg; 34717 }; 34718 return sema.failWithOwnedErrorMsg(msg); 34719 } 34720 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 34721 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len); 34722 } 34723 } else { 34724 // The provided type is the enum tag type. 34725 union_obj.tag_ty = provided_ty; 34726 const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) { 34727 .enum_type => |x| x, 34728 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(mod)}), 34729 }; 34730 // The fields of the union must match the enum exactly. 34731 // A flag per field is used to check for missing and extraneous fields. 34732 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); 34733 @memset(explicit_tags_seen, false); 34734 } 34735 } else { 34736 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis 34737 // purposes, we still auto-generate an enum tag type the same way. That the union is 34738 // untagged is represented by the Type tag (union vs union_tagged). 34739 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); 34740 } 34741 34742 const bits_per_field = 4; 34743 const fields_per_u32 = 32 / bits_per_field; 34744 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 34745 var bit_bag_index: usize = extra_index; 34746 extra_index += bit_bags_count; 34747 var cur_bit_bag: u32 = undefined; 34748 var field_i: u32 = 0; 34749 var last_tag_val: ?Value = null; 34750 while (field_i < fields_len) : (field_i += 1) { 34751 if (field_i % fields_per_u32 == 0) { 34752 cur_bit_bag = zir.extra[bit_bag_index]; 34753 bit_bag_index += 1; 34754 } 34755 const has_type = @as(u1, @truncate(cur_bit_bag)) != 0; 34756 cur_bit_bag >>= 1; 34757 const has_align = @as(u1, @truncate(cur_bit_bag)) != 0; 34758 cur_bit_bag >>= 1; 34759 const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0; 34760 cur_bit_bag >>= 1; 34761 const unused = @as(u1, @truncate(cur_bit_bag)) != 0; 34762 cur_bit_bag >>= 1; 34763 _ = unused; 34764 34765 const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); 34766 extra_index += 1; 34767 34768 // doc_comment 34769 extra_index += 1; 34770 34771 const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { 34772 const field_type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34773 extra_index += 1; 34774 break :blk field_type_ref; 34775 } else .none; 34776 34777 const align_ref: Zir.Inst.Ref = if (has_align) blk: { 34778 const align_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34779 extra_index += 1; 34780 break :blk align_ref; 34781 } else .none; 34782 34783 const tag_ref: Air.Inst.Ref = if (has_tag) blk: { 34784 const tag_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); 34785 extra_index += 1; 34786 break :blk try sema.resolveInst(tag_ref); 34787 } else .none; 34788 34789 if (enum_field_vals.capacity() > 0) { 34790 const enum_tag_val = if (tag_ref != .none) blk: { 34791 const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) { 34792 error.NeededSourceLocation => { 34793 const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34794 .index = field_i, 34795 .range = .value, 34796 }).lazy; 34797 _ = try sema.semaUnionFieldVal(&block_scope, val_src, int_tag_ty, tag_ref); 34798 unreachable; 34799 }, 34800 else => |e| return e, 34801 }; 34802 last_tag_val = val; 34803 34804 break :blk val; 34805 } else blk: { 34806 const val = if (last_tag_val) |val| 34807 try sema.intAdd(val, Value.one_comptime_int, int_tag_ty, undefined) 34808 else 34809 try mod.intValue(int_tag_ty, 0); 34810 last_tag_val = val; 34811 34812 break :blk val; 34813 }; 34814 const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern()); 34815 if (gop.found_existing) { 34816 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; 34817 const other_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = gop.index }).lazy; 34818 const msg = msg: { 34819 const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)}); 34820 errdefer msg.destroy(gpa); 34821 try sema.errNote(&block_scope, other_field_src, msg, "other occurrence here", .{}); 34822 break :msg msg; 34823 }; 34824 return sema.failWithOwnedErrorMsg(msg); 34825 } 34826 } 34827 34828 // This string needs to outlive the ZIR code. 34829 const field_name = try ip.getOrPutString(gpa, field_name_zir); 34830 if (enum_field_names.len != 0) { 34831 enum_field_names[field_i] = field_name; 34832 } 34833 34834 const field_ty: Type = if (!has_type) 34835 Type.void 34836 else if (field_type_ref == .none) 34837 Type.noreturn 34838 else 34839 sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) { 34840 error.NeededSourceLocation => { 34841 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34842 .index = field_i, 34843 .range = .type, 34844 }).lazy; 34845 _ = try sema.resolveType(&block_scope, ty_src, field_type_ref); 34846 unreachable; 34847 }, 34848 else => |e| return e, 34849 }; 34850 34851 if (field_ty.isGenericPoison()) { 34852 return error.GenericPoison; 34853 } 34854 34855 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); 34856 if (gop.found_existing) { 34857 const msg = msg: { 34858 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; 34859 const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{}'", .{ 34860 field_name.fmt(ip), 34861 }); 34862 errdefer msg.destroy(gpa); 34863 34864 const prev_field_index = union_obj.fields.getIndex(field_name).?; 34865 const prev_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = prev_field_index }).lazy; 34866 try mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{}); 34867 try sema.errNote(&block_scope, src, msg, "union declared here", .{}); 34868 break :msg msg; 34869 }; 34870 return sema.failWithOwnedErrorMsg(msg); 34871 } 34872 34873 if (explicit_tags_seen.len > 0) { 34874 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; 34875 const enum_index = tag_info.nameIndex(ip, field_name) orelse { 34876 const msg = msg: { 34877 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34878 .index = field_i, 34879 .range = .type, 34880 }).lazy; 34881 const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{}' in enum '{}'", .{ 34882 field_name.fmt(ip), union_obj.tag_ty.fmt(mod), 34883 }); 34884 errdefer msg.destroy(sema.gpa); 34885 try sema.addDeclaredHereNote(msg, union_obj.tag_ty); 34886 break :msg msg; 34887 }; 34888 return sema.failWithOwnedErrorMsg(msg); 34889 }; 34890 // No check for duplicate because the check already happened in order 34891 // to create the enum type in the first place. 34892 assert(!explicit_tags_seen[enum_index]); 34893 explicit_tags_seen[enum_index] = true; 34894 } 34895 34896 if (field_ty.zigTypeTag(mod) == .Opaque) { 34897 const msg = msg: { 34898 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34899 .index = field_i, 34900 .range = .type, 34901 }).lazy; 34902 const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); 34903 errdefer msg.destroy(sema.gpa); 34904 34905 try sema.addDeclaredHereNote(msg, field_ty); 34906 break :msg msg; 34907 }; 34908 return sema.failWithOwnedErrorMsg(msg); 34909 } 34910 if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { 34911 const msg = msg: { 34912 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34913 .index = field_i, 34914 .range = .type, 34915 }); 34916 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 34917 errdefer msg.destroy(sema.gpa); 34918 34919 try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .union_field); 34920 34921 try sema.addDeclaredHereNote(msg, field_ty); 34922 break :msg msg; 34923 }; 34924 return sema.failWithOwnedErrorMsg(msg); 34925 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { 34926 const msg = msg: { 34927 const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34928 .index = field_i, 34929 .range = .type, 34930 }); 34931 const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); 34932 errdefer msg.destroy(sema.gpa); 34933 34934 try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty); 34935 34936 try sema.addDeclaredHereNote(msg, field_ty); 34937 break :msg msg; 34938 }; 34939 return sema.failWithOwnedErrorMsg(msg); 34940 } 34941 34942 gop.value_ptr.* = .{ 34943 .ty = field_ty, 34944 .abi_align = .none, 34945 }; 34946 34947 if (align_ref != .none) { 34948 gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { 34949 error.NeededSourceLocation => { 34950 const align_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ 34951 .index = field_i, 34952 .range = .alignment, 34953 }).lazy; 34954 _ = try sema.resolveAlign(&block_scope, align_src, align_ref); 34955 unreachable; 34956 }, 34957 else => |e| return e, 34958 }; 34959 } else { 34960 gop.value_ptr.abi_align = .none; 34961 } 34962 } 34963 34964 if (explicit_tags_seen.len > 0) { 34965 const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; 34966 if (tag_info.names.len > fields_len) { 34967 const msg = msg: { 34968 const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); 34969 errdefer msg.destroy(sema.gpa); 34970 34971 const enum_ty = union_obj.tag_ty; 34972 for (tag_info.names, 0..) |field_name, field_index| { 34973 if (explicit_tags_seen[field_index]) continue; 34974 try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{ 34975 field_name.fmt(ip), 34976 }); 34977 } 34978 try sema.addDeclaredHereNote(msg, union_obj.tag_ty); 34979 break :msg msg; 34980 }; 34981 return sema.failWithOwnedErrorMsg(msg); 34982 } 34983 } else if (enum_field_vals.count() > 0) { 34984 union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_obj); 34985 } else { 34986 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_obj); 34987 } 34988 } 34989 34990 fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Type, tag_ref: Air.Inst.Ref) CompileError!Value { 34991 const coerced = try sema.coerce(block, int_tag_ty, tag_ref, src); 34992 return sema.resolveConstValue(block, src, coerced, "enum tag value must be comptime-known"); 34993 } 34994 34995 fn generateUnionTagTypeNumbered( 34996 sema: *Sema, 34997 block: *Block, 34998 enum_field_names: []const InternPool.NullTerminatedString, 34999 enum_field_vals: []const InternPool.Index, 35000 union_obj: *Module.Union, 35001 ) !Type { 35002 const mod = sema.mod; 35003 const gpa = sema.gpa; 35004 35005 const src_decl = mod.declPtr(block.src_decl); 35006 const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); 35007 errdefer mod.destroyDecl(new_decl_index); 35008 const fqn = try union_obj.getFullyQualifiedName(mod); 35009 const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)}); 35010 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ 35011 .ty = Type.noreturn, 35012 .val = Value.@"unreachable", 35013 }, name); 35014 errdefer mod.abortAnonDecl(new_decl_index); 35015 35016 const new_decl = mod.declPtr(new_decl_index); 35017 new_decl.name_fully_qualified = true; 35018 new_decl.owns_tv = true; 35019 new_decl.name_fully_qualified = true; 35020 35021 const enum_ty = try mod.intern(.{ .enum_type = .{ 35022 .decl = new_decl_index, 35023 .namespace = .none, 35024 .tag_ty = if (enum_field_vals.len == 0) 35025 (try mod.intType(.unsigned, 0)).toIntern() 35026 else 35027 mod.intern_pool.typeOf(enum_field_vals[0]), 35028 .names = enum_field_names, 35029 .values = enum_field_vals, 35030 .tag_mode = .explicit, 35031 } }); 35032 35033 new_decl.ty = Type.type; 35034 new_decl.val = enum_ty.toValue(); 35035 35036 try mod.finalizeAnonDecl(new_decl_index); 35037 return enum_ty.toType(); 35038 } 35039 35040 fn generateUnionTagTypeSimple( 35041 sema: *Sema, 35042 block: *Block, 35043 enum_field_names: []const InternPool.NullTerminatedString, 35044 maybe_union_obj: ?*Module.Union, 35045 ) !Type { 35046 const mod = sema.mod; 35047 const gpa = sema.gpa; 35048 35049 const new_decl_index = new_decl_index: { 35050 const union_obj = maybe_union_obj orelse { 35051 break :new_decl_index try mod.createAnonymousDecl(block, .{ 35052 .ty = Type.noreturn, 35053 .val = Value.@"unreachable", 35054 }); 35055 }; 35056 const src_decl = mod.declPtr(block.src_decl); 35057 const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); 35058 errdefer mod.destroyDecl(new_decl_index); 35059 const fqn = try union_obj.getFullyQualifiedName(mod); 35060 const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)}); 35061 try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ 35062 .ty = Type.noreturn, 35063 .val = Value.@"unreachable", 35064 }, name); 35065 mod.declPtr(new_decl_index).name_fully_qualified = true; 35066 break :new_decl_index new_decl_index; 35067 }; 35068 errdefer mod.abortAnonDecl(new_decl_index); 35069 35070 const enum_ty = try mod.intern(.{ .enum_type = .{ 35071 .decl = new_decl_index, 35072 .namespace = .none, 35073 .tag_ty = if (enum_field_names.len == 0) 35074 (try mod.intType(.unsigned, 0)).toIntern() 35075 else 35076 (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(), 35077 .names = enum_field_names, 35078 .values = &.{}, 35079 .tag_mode = .auto, 35080 } }); 35081 35082 const new_decl = mod.declPtr(new_decl_index); 35083 new_decl.owns_tv = true; 35084 new_decl.ty = Type.type; 35085 new_decl.val = enum_ty.toValue(); 35086 35087 try mod.finalizeAnonDecl(new_decl_index); 35088 return enum_ty.toType(); 35089 } 35090 35091 fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { 35092 const gpa = sema.gpa; 35093 const src = LazySrcLoc.nodeOffset(0); 35094 35095 var wip_captures = try WipCaptureScope.init(gpa, sema.owner_decl.src_scope); 35096 defer wip_captures.deinit(); 35097 35098 var block: Block = .{ 35099 .parent = null, 35100 .sema = sema, 35101 .src_decl = sema.owner_decl_index, 35102 .namespace = sema.owner_decl.src_namespace, 35103 .wip_capture_scope = wip_captures.scope, 35104 .instructions = .{}, 35105 .inlining = null, 35106 .is_comptime = true, 35107 }; 35108 defer { 35109 block.instructions.deinit(gpa); 35110 block.params.deinit(gpa); 35111 } 35112 35113 const decl_index = try getBuiltinDecl(sema, &block, name); 35114 return sema.analyzeDeclVal(&block, src, decl_index); 35115 } 35116 35117 fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Module.Decl.Index { 35118 const gpa = sema.gpa; 35119 35120 const src = LazySrcLoc.nodeOffset(0); 35121 35122 const mod = sema.mod; 35123 const ip = &mod.intern_pool; 35124 const std_pkg = mod.main_pkg.table.get("std").?; 35125 const std_file = (mod.importPkg(std_pkg) catch unreachable).file; 35126 const opt_builtin_inst = (try sema.namespaceLookupRef( 35127 block, 35128 src, 35129 mod.declPtr(std_file.root_decl.unwrap().?).src_namespace, 35130 try ip.getOrPutString(gpa, "builtin"), 35131 )) orelse @panic("lib/std.zig is corrupt and missing 'builtin'"); 35132 const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst, src); 35133 const builtin_ty = sema.analyzeAsType(block, src, builtin_inst) catch |err| switch (err) { 35134 error.AnalysisFail => std.debug.panic("std.builtin is corrupt", .{}), 35135 else => |e| return e, 35136 }; 35137 const decl_index = (try sema.namespaceLookup( 35138 block, 35139 src, 35140 builtin_ty.getNamespaceIndex(mod).unwrap().?, 35141 try ip.getOrPutString(gpa, name), 35142 )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name}); 35143 return decl_index; 35144 } 35145 35146 fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type { 35147 const ty_inst = try sema.getBuiltin(name); 35148 35149 var wip_captures = try WipCaptureScope.init(sema.gpa, sema.owner_decl.src_scope); 35150 defer wip_captures.deinit(); 35151 35152 var block: Block = .{ 35153 .parent = null, 35154 .sema = sema, 35155 .src_decl = sema.owner_decl_index, 35156 .namespace = sema.owner_decl.src_namespace, 35157 .wip_capture_scope = wip_captures.scope, 35158 .instructions = .{}, 35159 .inlining = null, 35160 .is_comptime = true, 35161 }; 35162 defer { 35163 block.instructions.deinit(sema.gpa); 35164 block.params.deinit(sema.gpa); 35165 } 35166 const src = LazySrcLoc.nodeOffset(0); 35167 35168 const result_ty = sema.analyzeAsType(&block, src, ty_inst) catch |err| switch (err) { 35169 error.AnalysisFail => std.debug.panic("std.builtin.{s} is corrupt", .{name}), 35170 else => |e| return e, 35171 }; 35172 try sema.resolveTypeFully(result_ty); // Should not fail 35173 return result_ty; 35174 } 35175 35176 /// There is another implementation of this in `Type.onePossibleValue`. This one 35177 /// in `Sema` is for calling during semantic analysis, and performs field resolution 35178 /// to get the answer. The one in `Type` is for calling during codegen and asserts 35179 /// that the types are already resolved. 35180 /// TODO assert the return value matches `ty.onePossibleValue` 35181 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { 35182 const mod = sema.mod; 35183 return switch (ty.toIntern()) { 35184 .u0_type, 35185 .i0_type, 35186 => try mod.intValue(ty, 0), 35187 .u1_type, 35188 .u8_type, 35189 .i8_type, 35190 .u16_type, 35191 .i16_type, 35192 .u29_type, 35193 .u32_type, 35194 .i32_type, 35195 .u64_type, 35196 .i64_type, 35197 .u80_type, 35198 .u128_type, 35199 .i128_type, 35200 .usize_type, 35201 .isize_type, 35202 .c_char_type, 35203 .c_short_type, 35204 .c_ushort_type, 35205 .c_int_type, 35206 .c_uint_type, 35207 .c_long_type, 35208 .c_ulong_type, 35209 .c_longlong_type, 35210 .c_ulonglong_type, 35211 .c_longdouble_type, 35212 .f16_type, 35213 .f32_type, 35214 .f64_type, 35215 .f80_type, 35216 .f128_type, 35217 .anyopaque_type, 35218 .bool_type, 35219 .type_type, 35220 .anyerror_type, 35221 .comptime_int_type, 35222 .comptime_float_type, 35223 .enum_literal_type, 35224 .atomic_order_type, 35225 .atomic_rmw_op_type, 35226 .calling_convention_type, 35227 .address_space_type, 35228 .float_mode_type, 35229 .reduce_op_type, 35230 .call_modifier_type, 35231 .prefetch_options_type, 35232 .export_options_type, 35233 .extern_options_type, 35234 .type_info_type, 35235 .manyptr_u8_type, 35236 .manyptr_const_u8_type, 35237 .manyptr_const_u8_sentinel_0_type, 35238 .single_const_pointer_to_comptime_int_type, 35239 .slice_const_u8_type, 35240 .slice_const_u8_sentinel_0_type, 35241 .anyerror_void_error_union_type, 35242 => null, 35243 .void_type => Value.void, 35244 .noreturn_type => Value.@"unreachable", 35245 .anyframe_type => unreachable, 35246 .null_type => Value.null, 35247 .undefined_type => Value.undef, 35248 .optional_noreturn_type => try mod.nullValue(ty), 35249 .generic_poison_type => error.GenericPoison, 35250 .empty_struct_type => Value.empty_struct, 35251 // values, not types 35252 .undef, 35253 .zero, 35254 .zero_usize, 35255 .zero_u8, 35256 .one, 35257 .one_usize, 35258 .one_u8, 35259 .four_u8, 35260 .negative_one, 35261 .calling_convention_c, 35262 .calling_convention_inline, 35263 .void_value, 35264 .unreachable_value, 35265 .null_value, 35266 .bool_true, 35267 .bool_false, 35268 .empty_struct, 35269 .generic_poison, 35270 // invalid 35271 .var_args_param_type, 35272 .none, 35273 => unreachable, 35274 _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) { 35275 .type_int_signed, // i0 handled above 35276 .type_int_unsigned, // u0 handled above 35277 .type_pointer, 35278 .type_slice, 35279 .type_optional, // ?noreturn handled above 35280 .type_anyframe, 35281 .type_error_union, 35282 .type_error_set, 35283 .type_inferred_error_set, 35284 .type_opaque, 35285 .type_function, 35286 => null, 35287 .simple_type, // handled above 35288 // values, not types 35289 .undef, 35290 .runtime_value, 35291 .simple_value, 35292 .ptr_decl, 35293 .ptr_mut_decl, 35294 .ptr_comptime_field, 35295 .ptr_int, 35296 .ptr_eu_payload, 35297 .ptr_opt_payload, 35298 .ptr_elem, 35299 .ptr_field, 35300 .ptr_slice, 35301 .opt_payload, 35302 .opt_null, 35303 .int_u8, 35304 .int_u16, 35305 .int_u32, 35306 .int_i32, 35307 .int_usize, 35308 .int_comptime_int_u32, 35309 .int_comptime_int_i32, 35310 .int_small, 35311 .int_positive, 35312 .int_negative, 35313 .int_lazy_align, 35314 .int_lazy_size, 35315 .error_set_error, 35316 .error_union_error, 35317 .error_union_payload, 35318 .enum_literal, 35319 .enum_tag, 35320 .float_f16, 35321 .float_f32, 35322 .float_f64, 35323 .float_f80, 35324 .float_f128, 35325 .float_c_longdouble_f80, 35326 .float_c_longdouble_f128, 35327 .float_comptime_float, 35328 .variable, 35329 .extern_func, 35330 .func, 35331 .only_possible_value, 35332 .union_value, 35333 .bytes, 35334 .aggregate, 35335 .repeated, 35336 // memoized value, not types 35337 .memoized_call, 35338 => unreachable, 35339 .type_array_big, 35340 .type_array_small, 35341 .type_vector, 35342 .type_enum_auto, 35343 .type_enum_explicit, 35344 .type_enum_nonexhaustive, 35345 .type_struct, 35346 .type_struct_ns, 35347 .type_struct_anon, 35348 .type_tuple_anon, 35349 .type_union_tagged, 35350 .type_union_untagged, 35351 .type_union_safety, 35352 => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 35353 inline .array_type, .vector_type => |seq_type, seq_tag| { 35354 const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; 35355 if (seq_type.len + @intFromBool(has_sentinel) == 0) return (try mod.intern(.{ .aggregate = .{ 35356 .ty = ty.toIntern(), 35357 .storage = .{ .elems = &.{} }, 35358 } })).toValue(); 35359 35360 if (try sema.typeHasOnePossibleValue(seq_type.child.toType())) |opv| { 35361 return (try mod.intern(.{ .aggregate = .{ 35362 .ty = ty.toIntern(), 35363 .storage = .{ .repeated_elem = opv.toIntern() }, 35364 } })).toValue(); 35365 } 35366 return null; 35367 }, 35368 35369 .struct_type => |struct_type| { 35370 const resolved_ty = try sema.resolveTypeFields(ty); 35371 if (mod.structPtrUnwrap(struct_type.index)) |s| { 35372 const field_vals = try sema.arena.alloc(InternPool.Index, s.fields.count()); 35373 for (field_vals, s.fields.values(), 0..) |*field_val, field, i| { 35374 if (field.is_comptime) { 35375 field_val.* = field.default_val; 35376 continue; 35377 } 35378 if (field.ty.eql(resolved_ty, sema.mod)) { 35379 const msg = try Module.ErrorMsg.create( 35380 sema.gpa, 35381 s.srcLoc(sema.mod), 35382 "struct '{}' depends on itself", 35383 .{ty.fmt(sema.mod)}, 35384 ); 35385 try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{}); 35386 return sema.failWithOwnedErrorMsg(msg); 35387 } 35388 if (try sema.typeHasOnePossibleValue(field.ty)) |field_opv| { 35389 field_val.* = try field_opv.intern(field.ty, mod); 35390 } else return null; 35391 } 35392 35393 // In this case the struct has no runtime-known fields and 35394 // therefore has one possible value. 35395 return (try mod.intern(.{ .aggregate = .{ 35396 .ty = ty.toIntern(), 35397 .storage = .{ .elems = field_vals }, 35398 } })).toValue(); 35399 } 35400 35401 // In this case the struct has no fields at all and 35402 // therefore has one possible value. 35403 return (try mod.intern(.{ .aggregate = .{ 35404 .ty = ty.toIntern(), 35405 .storage = .{ .elems = &.{} }, 35406 } })).toValue(); 35407 }, 35408 35409 .anon_struct_type => |tuple| { 35410 for (tuple.values) |val| { 35411 if (val == .none) return null; 35412 } 35413 // In this case the struct has all comptime-known fields and 35414 // therefore has one possible value. 35415 // TODO: write something like getCoercedInts to avoid needing to dupe 35416 return (try mod.intern(.{ .aggregate = .{ 35417 .ty = ty.toIntern(), 35418 .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values) }, 35419 } })).toValue(); 35420 }, 35421 35422 .union_type => |union_type| { 35423 const resolved_ty = try sema.resolveTypeFields(ty); 35424 const union_obj = mod.unionPtr(union_type.index); 35425 const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse 35426 return null; 35427 const fields = union_obj.fields.values(); 35428 if (fields.len == 0) { 35429 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); 35430 return only.toValue(); 35431 } 35432 const only_field = fields[0]; 35433 if (only_field.ty.eql(resolved_ty, sema.mod)) { 35434 const msg = try Module.ErrorMsg.create( 35435 sema.gpa, 35436 union_obj.srcLoc(sema.mod), 35437 "union '{}' depends on itself", 35438 .{ty.fmt(sema.mod)}, 35439 ); 35440 try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{}); 35441 return sema.failWithOwnedErrorMsg(msg); 35442 } 35443 const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse 35444 return null; 35445 const only = try mod.intern(.{ .un = .{ 35446 .ty = resolved_ty.toIntern(), 35447 .tag = tag_val.toIntern(), 35448 .val = val_val.toIntern(), 35449 } }); 35450 return only.toValue(); 35451 }, 35452 35453 .enum_type => |enum_type| switch (enum_type.tag_mode) { 35454 .nonexhaustive => { 35455 if (enum_type.tag_ty == .comptime_int_type) return null; 35456 35457 if (try sema.typeHasOnePossibleValue(enum_type.tag_ty.toType())) |int_opv| { 35458 const only = try mod.intern(.{ .enum_tag = .{ 35459 .ty = ty.toIntern(), 35460 .int = int_opv.toIntern(), 35461 } }); 35462 return only.toValue(); 35463 } 35464 35465 return null; 35466 }, 35467 .auto, .explicit => { 35468 if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null; 35469 35470 switch (enum_type.names.len) { 35471 0 => { 35472 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); 35473 return only.toValue(); 35474 }, 35475 1 => return try mod.getCoerced((if (enum_type.values.len == 0) 35476 try mod.intern(.{ .int = .{ 35477 .ty = enum_type.tag_ty, 35478 .storage = .{ .u64 = 0 }, 35479 } }) 35480 else 35481 enum_type.values[0]).toValue(), ty), 35482 else => return null, 35483 } 35484 }, 35485 }, 35486 35487 else => unreachable, 35488 }, 35489 }, 35490 }; 35491 } 35492 35493 /// Returns the type of the AIR instruction. 35494 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type { 35495 return sema.getTmpAir().typeOf(inst, &sema.mod.intern_pool); 35496 } 35497 35498 pub fn getTmpAir(sema: Sema) Air { 35499 return .{ 35500 .instructions = sema.air_instructions.slice(), 35501 .extra = sema.air_extra.items, 35502 }; 35503 } 35504 35505 // TODO: make this non-fallible or remove it entirely 35506 pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { 35507 _ = sema; 35508 return Air.internedToRef(ty.toIntern()); 35509 } 35510 35511 fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref { 35512 const mod = sema.mod; 35513 return sema.addConstant(try mod.intValue(ty, int)); 35514 } 35515 35516 fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { 35517 return sema.addConstant((try sema.mod.intern(.{ .undef = ty.toIntern() })).toValue()); 35518 } 35519 35520 // TODO: make this non-fallible or remove it entirely 35521 pub fn addConstant(sema: *Sema, val: Value) !Air.Inst.Ref { 35522 _ = sema; 35523 return Air.internedToRef(val.toIntern()); 35524 } 35525 35526 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 { 35527 const fields = std.meta.fields(@TypeOf(extra)); 35528 try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len); 35529 return sema.addExtraAssumeCapacity(extra); 35530 } 35531 35532 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { 35533 const fields = std.meta.fields(@TypeOf(extra)); 35534 const result = @as(u32, @intCast(sema.air_extra.items.len)); 35535 inline for (fields) |field| { 35536 sema.air_extra.appendAssumeCapacity(switch (field.type) { 35537 u32 => @field(extra, field.name), 35538 Air.Inst.Ref => @intFromEnum(@field(extra, field.name)), 35539 i32 => @as(u32, @bitCast(@field(extra, field.name))), 35540 InternPool.Index => @intFromEnum(@field(extra, field.name)), 35541 else => @compileError("bad field type: " ++ @typeName(field.type)), 35542 }); 35543 } 35544 return result; 35545 } 35546 35547 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void { 35548 const coerced = @as([]const u32, @ptrCast(refs)); 35549 sema.air_extra.appendSliceAssumeCapacity(coerced); 35550 } 35551 35552 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index { 35553 const air_datas = sema.air_instructions.items(.data); 35554 const air_tags = sema.air_instructions.items(.tag); 35555 switch (air_tags[inst_index]) { 35556 .br => return air_datas[inst_index].br.block_inst, 35557 else => return null, 35558 } 35559 } 35560 35561 fn isComptimeKnown( 35562 sema: *Sema, 35563 inst: Air.Inst.Ref, 35564 ) !bool { 35565 return (try sema.resolveMaybeUndefVal(inst)) != null; 35566 } 35567 35568 fn analyzeComptimeAlloc( 35569 sema: *Sema, 35570 block: *Block, 35571 var_type: Type, 35572 alignment: Alignment, 35573 ) CompileError!Air.Inst.Ref { 35574 const mod = sema.mod; 35575 35576 // Needed to make an anon decl with type `var_type` (the `finish()` call below). 35577 _ = try sema.typeHasOnePossibleValue(var_type); 35578 35579 const ptr_type = try mod.ptrType(.{ 35580 .child = var_type.toIntern(), 35581 .flags = .{ 35582 .alignment = alignment, 35583 .address_space = target_util.defaultAddressSpace(mod.getTarget(), .global_constant), 35584 }, 35585 }); 35586 35587 var anon_decl = try block.startAnonDecl(); 35588 defer anon_decl.deinit(); 35589 35590 const decl_index = try anon_decl.finish( 35591 var_type, 35592 // There will be stores before the first load, but they may be to sub-elements or 35593 // sub-fields. So we need to initialize with undef to allow the mechanism to expand 35594 // into fields/elements and have those overridden with stored values. 35595 (try mod.intern(.{ .undef = var_type.toIntern() })).toValue(), 35596 alignment, 35597 ); 35598 const decl = mod.declPtr(decl_index); 35599 decl.alignment = alignment; 35600 35601 try sema.comptime_mutable_decls.append(decl_index); 35602 try mod.declareDeclDependency(sema.owner_decl_index, decl_index); 35603 return sema.addConstant((try mod.intern(.{ .ptr = .{ 35604 .ty = ptr_type.toIntern(), 35605 .addr = .{ .mut_decl = .{ 35606 .decl = decl_index, 35607 .runtime_index = block.runtime_index, 35608 } }, 35609 } })).toValue()); 35610 } 35611 35612 /// The places where a user can specify an address space attribute 35613 pub const AddressSpaceContext = enum { 35614 /// A function is specified to be placed in a certain address space. 35615 function, 35616 35617 /// A (global) variable is specified to be placed in a certain address space. 35618 /// In contrast to .constant, these values (and thus the address space they will be 35619 /// placed in) are required to be mutable. 35620 variable, 35621 35622 /// A (global) constant value is specified to be placed in a certain address space. 35623 /// In contrast to .variable, values placed in this address space are not required to be mutable. 35624 constant, 35625 35626 /// A pointer is ascripted to point into a certain address space. 35627 pointer, 35628 }; 35629 35630 pub fn analyzeAddressSpace( 35631 sema: *Sema, 35632 block: *Block, 35633 src: LazySrcLoc, 35634 zir_ref: Zir.Inst.Ref, 35635 ctx: AddressSpaceContext, 35636 ) !std.builtin.AddressSpace { 35637 const mod = sema.mod; 35638 const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, "addresspace must be comptime-known"); 35639 const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val); 35640 const target = sema.mod.getTarget(); 35641 const arch = target.cpu.arch; 35642 35643 const is_nv = arch == .nvptx or arch == .nvptx64; 35644 const is_amd = arch == .amdgcn; 35645 const is_spirv = arch == .spirv32 or arch == .spirv64; 35646 const is_gpu = is_nv or is_amd or is_spirv; 35647 35648 const supported = switch (address_space) { 35649 // TODO: on spir-v only when os is opencl. 35650 .generic => true, 35651 .gs, .fs, .ss => (arch == .x86 or arch == .x86_64) and ctx == .pointer, 35652 // TODO: check that .shared and .local are left uninitialized 35653 .param => is_nv, 35654 .global, .shared, .local => is_gpu, 35655 .constant => is_gpu and (ctx == .constant), 35656 // TODO this should also check how many flash banks the cpu has 35657 .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr, 35658 }; 35659 35660 if (!supported) { 35661 // TODO error messages could be made more elaborate here 35662 const entity = switch (ctx) { 35663 .function => "functions", 35664 .variable => "mutable values", 35665 .constant => "constant values", 35666 .pointer => "pointers", 35667 }; 35668 return sema.fail( 35669 block, 35670 src, 35671 "{s} with address space '{s}' are not supported on {s}", 35672 .{ entity, @tagName(address_space), arch.genericName() }, 35673 ); 35674 } 35675 35676 return address_space; 35677 } 35678 35679 /// Asserts the value is a pointer and dereferences it. 35680 /// Returns `null` if the pointer contents cannot be loaded at comptime. 35681 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { 35682 const mod = sema.mod; 35683 const load_ty = ptr_ty.childType(mod); 35684 const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty); 35685 switch (res) { 35686 .runtime_load => return null, 35687 .val => |v| return v, 35688 .needed_well_defined => |ty| return sema.fail( 35689 block, 35690 src, 35691 "comptime dereference requires '{}' to have a well-defined layout, but it does not.", 35692 .{ty.fmt(sema.mod)}, 35693 ), 35694 .out_of_bounds => |ty| return sema.fail( 35695 block, 35696 src, 35697 "dereference of '{}' exceeds bounds of containing decl of type '{}'", 35698 .{ ptr_ty.fmt(sema.mod), ty.fmt(sema.mod) }, 35699 ), 35700 } 35701 } 35702 35703 const DerefResult = union(enum) { 35704 runtime_load, 35705 val: Value, 35706 needed_well_defined: Type, 35707 out_of_bounds: Type, 35708 }; 35709 35710 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type) CompileError!DerefResult { 35711 const mod = sema.mod; 35712 const target = mod.getTarget(); 35713 const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) { 35714 error.RuntimeLoad => return DerefResult{ .runtime_load = {} }, 35715 else => |e| return e, 35716 }; 35717 35718 if (deref.pointee) |tv| { 35719 const coerce_in_mem_ok = 35720 (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or 35721 (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok; 35722 if (coerce_in_mem_ok) { 35723 // We have a Value that lines up in virtual memory exactly with what we want to load, 35724 // and it is in-memory coercible to load_ty. It may be returned without modifications. 35725 // Move mutable decl values to the InternPool and assert other decls are already in 35726 // the InternPool. 35727 const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern(); 35728 const coerced_val = try mod.getCoerced(uncoerced_val.toValue(), load_ty); 35729 return .{ .val = coerced_val }; 35730 } 35731 } 35732 35733 // The type is not in-memory coercible or the direct dereference failed, so it must 35734 // be bitcast according to the pointer type we are performing the load through. 35735 if (!load_ty.hasWellDefinedLayout(mod)) { 35736 return DerefResult{ .needed_well_defined = load_ty }; 35737 } 35738 35739 const load_sz = try sema.typeAbiSize(load_ty); 35740 35741 // Try the smaller bit-cast first, since that's more efficient than using the larger `parent` 35742 if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty)) 35743 return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load }; 35744 35745 // If that fails, try to bit-cast from the largest parent value with a well-defined layout 35746 if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty)) 35747 return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load }; 35748 35749 if (deref.ty_without_well_defined_layout) |bad_ty| { 35750 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem 35751 // is that some type we encountered when de-referencing does not have a well-defined layout. 35752 return DerefResult{ .needed_well_defined = bad_ty }; 35753 } else { 35754 // If all encountered types had well-defined layouts, the parent is the root decl and it just 35755 // wasn't big enough for the load. 35756 return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty }; 35757 } 35758 } 35759 35760 /// Used to convert a u64 value to a usize value, emitting a compile error if the number 35761 /// is too big to fit. 35762 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { 35763 if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; 35764 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}); 35765 } 35766 35767 /// For pointer-like optionals, it returns the pointer type. For pointers, 35768 /// the type is returned unmodified. 35769 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether 35770 /// a type has zero bits, which can cause a "foo depends on itself" compile error. 35771 /// This logic must be kept in sync with `Type.isPtrLikeOptional`. 35772 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { 35773 const mod = sema.mod; 35774 return switch (mod.intern_pool.indexToKey(ty.toIntern())) { 35775 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 35776 .One, .Many, .C => ty, 35777 .Slice => null, 35778 }, 35779 .opt_type => |opt_child| switch (mod.intern_pool.indexToKey(opt_child)) { 35780 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 35781 .Slice, .C => null, 35782 .Many, .One => { 35783 if (ptr_type.flags.is_allowzero) return null; 35784 35785 // optionals of zero sized types behave like bools, not pointers 35786 const payload_ty = opt_child.toType(); 35787 if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) { 35788 return null; 35789 } 35790 35791 return payload_ty; 35792 }, 35793 }, 35794 else => null, 35795 }, 35796 else => null, 35797 }; 35798 } 35799 35800 /// `generic_poison` will return false. 35801 /// This function returns false negatives when structs and unions are having their 35802 /// field types resolved. 35803 /// TODO assert the return value matches `ty.comptimeOnly` 35804 /// TODO merge these implementations together with the "advanced"/opt_sema pattern seen 35805 /// elsewhere in value.zig 35806 pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { 35807 const mod = sema.mod; 35808 return switch (ty.toIntern()) { 35809 .empty_struct_type => false, 35810 35811 else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { 35812 .int_type => return false, 35813 .ptr_type => |ptr_type| { 35814 const child_ty = ptr_type.child.toType(); 35815 if (child_ty.zigTypeTag(mod) == .Fn) { 35816 return mod.typeToFunc(child_ty).?.is_generic; 35817 } else { 35818 return sema.typeRequiresComptime(child_ty); 35819 } 35820 }, 35821 .anyframe_type => |child| { 35822 if (child == .none) return false; 35823 return sema.typeRequiresComptime(child.toType()); 35824 }, 35825 .array_type => |array_type| return sema.typeRequiresComptime(array_type.child.toType()), 35826 .vector_type => |vector_type| return sema.typeRequiresComptime(vector_type.child.toType()), 35827 .opt_type => |child| return sema.typeRequiresComptime(child.toType()), 35828 35829 .error_union_type => |error_union_type| { 35830 return sema.typeRequiresComptime(error_union_type.payload_type.toType()); 35831 }, 35832 35833 .error_set_type, .inferred_error_set_type => false, 35834 35835 .func_type => true, 35836 35837 .simple_type => |t| return switch (t) { 35838 .f16, 35839 .f32, 35840 .f64, 35841 .f80, 35842 .f128, 35843 .usize, 35844 .isize, 35845 .c_char, 35846 .c_short, 35847 .c_ushort, 35848 .c_int, 35849 .c_uint, 35850 .c_long, 35851 .c_ulong, 35852 .c_longlong, 35853 .c_ulonglong, 35854 .c_longdouble, 35855 .anyopaque, 35856 .bool, 35857 .void, 35858 .anyerror, 35859 .noreturn, 35860 .generic_poison, 35861 .atomic_order, 35862 .atomic_rmw_op, 35863 .calling_convention, 35864 .address_space, 35865 .float_mode, 35866 .reduce_op, 35867 .call_modifier, 35868 .prefetch_options, 35869 .export_options, 35870 .extern_options, 35871 => false, 35872 35873 .type, 35874 .comptime_int, 35875 .comptime_float, 35876 .null, 35877 .undefined, 35878 .enum_literal, 35879 .type_info, 35880 => true, 35881 }, 35882 .struct_type => |struct_type| { 35883 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; 35884 switch (struct_obj.requires_comptime) { 35885 .no, .wip => return false, 35886 .yes => return true, 35887 .unknown => { 35888 if (struct_obj.status == .field_types_wip) 35889 return false; 35890 35891 try sema.resolveTypeFieldsStruct(ty, struct_obj); 35892 35893 struct_obj.requires_comptime = .wip; 35894 for (struct_obj.fields.values()) |field| { 35895 if (field.is_comptime) continue; 35896 if (try sema.typeRequiresComptime(field.ty)) { 35897 struct_obj.requires_comptime = .yes; 35898 return true; 35899 } 35900 } 35901 struct_obj.requires_comptime = .no; 35902 return false; 35903 }, 35904 } 35905 }, 35906 .anon_struct_type => |tuple| { 35907 for (tuple.types, tuple.values) |field_ty, val| { 35908 const have_comptime_val = val != .none; 35909 if (!have_comptime_val and try sema.typeRequiresComptime(field_ty.toType())) { 35910 return true; 35911 } 35912 } 35913 return false; 35914 }, 35915 35916 .union_type => |union_type| { 35917 const union_obj = mod.unionPtr(union_type.index); 35918 switch (union_obj.requires_comptime) { 35919 .no, .wip => return false, 35920 .yes => return true, 35921 .unknown => { 35922 if (union_obj.status == .field_types_wip) 35923 return false; 35924 35925 try sema.resolveTypeFieldsUnion(ty, union_obj); 35926 35927 union_obj.requires_comptime = .wip; 35928 for (union_obj.fields.values()) |field| { 35929 if (try sema.typeRequiresComptime(field.ty)) { 35930 union_obj.requires_comptime = .yes; 35931 return true; 35932 } 35933 } 35934 union_obj.requires_comptime = .no; 35935 return false; 35936 }, 35937 } 35938 }, 35939 35940 .opaque_type => false, 35941 .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()), 35942 35943 // values, not types 35944 .undef, 35945 .runtime_value, 35946 .simple_value, 35947 .variable, 35948 .extern_func, 35949 .func, 35950 .int, 35951 .err, 35952 .error_union, 35953 .enum_literal, 35954 .enum_tag, 35955 .empty_enum_value, 35956 .float, 35957 .ptr, 35958 .opt, 35959 .aggregate, 35960 .un, 35961 // memoization, not types 35962 .memoized_call, 35963 => unreachable, 35964 }, 35965 }; 35966 } 35967 35968 pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { 35969 const mod = sema.mod; 35970 return ty.hasRuntimeBitsAdvanced(mod, false, .{ .sema = sema }) catch |err| switch (err) { 35971 error.NeedLazy => unreachable, 35972 else => |e| return e, 35973 }; 35974 } 35975 35976 fn typeAbiSize(sema: *Sema, ty: Type) !u64 { 35977 try sema.resolveTypeLayout(ty); 35978 return ty.abiSize(sema.mod); 35979 } 35980 35981 fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!u32 { 35982 return (try ty.abiAlignmentAdvanced(sema.mod, .{ .sema = sema })).scalar; 35983 } 35984 35985 /// Not valid to call for packed unions. 35986 /// Keep implementation in sync with `Module.Union.Field.normalAlignment`. 35987 fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 { 35988 return @as(u32, @intCast(if (field.ty.isNoReturn(sema.mod)) 35989 0 35990 else 35991 field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty))); 35992 } 35993 35994 /// Keep implementation in sync with `Module.Struct.Field.alignment`. 35995 fn structFieldAlignment(sema: *Sema, field: Module.Struct.Field, layout: std.builtin.Type.ContainerLayout) !u32 { 35996 const mod = sema.mod; 35997 if (field.abi_align.toByteUnitsOptional()) |a| { 35998 assert(layout != .Packed); 35999 return @as(u32, @intCast(a)); 36000 } 36001 switch (layout) { 36002 .Packed => return 0, 36003 .Auto => if (mod.getTarget().ofmt != .c) { 36004 return sema.typeAbiAlignment(field.ty); 36005 }, 36006 .Extern => {}, 36007 } 36008 // extern 36009 const ty_abi_align = try sema.typeAbiAlignment(field.ty); 36010 if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) { 36011 return @max(ty_abi_align, 16); 36012 } 36013 return ty_abi_align; 36014 } 36015 36016 /// Synchronize logic with `Type.isFnOrHasRuntimeBits`. 36017 pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { 36018 const mod = sema.mod; 36019 const fn_info = mod.typeToFunc(ty).?; 36020 if (fn_info.is_generic) return false; 36021 if (fn_info.is_var_args) return true; 36022 switch (fn_info.cc) { 36023 // If there was a comptime calling convention, it should also return false here. 36024 .Inline => return false, 36025 else => {}, 36026 } 36027 if (try sema.typeRequiresComptime(fn_info.return_type.toType())) { 36028 return false; 36029 } 36030 return true; 36031 } 36032 36033 fn unionFieldIndex( 36034 sema: *Sema, 36035 block: *Block, 36036 unresolved_union_ty: Type, 36037 field_name: InternPool.NullTerminatedString, 36038 field_src: LazySrcLoc, 36039 ) !u32 { 36040 const mod = sema.mod; 36041 const union_ty = try sema.resolveTypeFields(unresolved_union_ty); 36042 const union_obj = mod.typeToUnion(union_ty).?; 36043 const field_index_usize = union_obj.fields.getIndex(field_name) orelse 36044 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); 36045 return @as(u32, @intCast(field_index_usize)); 36046 } 36047 36048 fn structFieldIndex( 36049 sema: *Sema, 36050 block: *Block, 36051 unresolved_struct_ty: Type, 36052 field_name: InternPool.NullTerminatedString, 36053 field_src: LazySrcLoc, 36054 ) !u32 { 36055 const mod = sema.mod; 36056 const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); 36057 if (struct_ty.isAnonStruct(mod)) { 36058 return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src); 36059 } else { 36060 const struct_obj = mod.typeToStruct(struct_ty).?; 36061 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse 36062 return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); 36063 return @as(u32, @intCast(field_index_usize)); 36064 } 36065 } 36066 36067 fn anonStructFieldIndex( 36068 sema: *Sema, 36069 block: *Block, 36070 struct_ty: Type, 36071 field_name: InternPool.NullTerminatedString, 36072 field_src: LazySrcLoc, 36073 ) !u32 { 36074 const mod = sema.mod; 36075 switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) { 36076 .anon_struct_type => |anon_struct_type| for (anon_struct_type.names, 0..) |name, i| { 36077 if (name == field_name) return @as(u32, @intCast(i)); 36078 }, 36079 .struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| { 36080 for (struct_obj.fields.keys(), 0..) |name, i| { 36081 if (name == field_name) { 36082 return @as(u32, @intCast(i)); 36083 } 36084 } 36085 }, 36086 else => unreachable, 36087 } 36088 return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{ 36089 field_name.fmt(&mod.intern_pool), struct_ty.fmt(sema.mod), 36090 }); 36091 } 36092 36093 fn queueFullTypeResolution(sema: *Sema, ty: Type) !void { 36094 try sema.types_to_resolve.put(sema.gpa, ty.toIntern(), {}); 36095 } 36096 36097 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting 36098 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar). 36099 fn intAdd(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value { 36100 var overflow: usize = undefined; 36101 return sema.intAddInner(lhs, rhs, ty, &overflow) catch |err| switch (err) { 36102 error.Overflow => { 36103 const is_vec = ty.isVector(sema.mod); 36104 overflow_idx.* = if (is_vec) overflow else 0; 36105 const safe_ty = if (is_vec) try sema.mod.vectorType(.{ 36106 .len = ty.vectorLen(sema.mod), 36107 .child = .comptime_int_type, 36108 }) else Type.comptime_int; 36109 return sema.intAddInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) { 36110 error.Overflow => unreachable, 36111 else => |e| return e, 36112 }; 36113 }, 36114 else => |e| return e, 36115 }; 36116 } 36117 36118 fn intAddInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value { 36119 const mod = sema.mod; 36120 if (ty.zigTypeTag(mod) == .Vector) { 36121 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 36122 const scalar_ty = ty.scalarType(mod); 36123 for (result_data, 0..) |*scalar, i| { 36124 const lhs_elem = try lhs.elemValue(mod, i); 36125 const rhs_elem = try rhs.elemValue(mod, i); 36126 const val = sema.intAddScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) { 36127 error.Overflow => { 36128 overflow_idx.* = i; 36129 return error.Overflow; 36130 }, 36131 else => |e| return e, 36132 }; 36133 scalar.* = try val.intern(scalar_ty, mod); 36134 } 36135 return (try mod.intern(.{ .aggregate = .{ 36136 .ty = ty.toIntern(), 36137 .storage = .{ .elems = result_data }, 36138 } })).toValue(); 36139 } 36140 return sema.intAddScalar(lhs, rhs, ty); 36141 } 36142 36143 fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value { 36144 const mod = sema.mod; 36145 if (scalar_ty.toIntern() != .comptime_int_type) { 36146 const res = try sema.intAddWithOverflowScalar(lhs, rhs, scalar_ty); 36147 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow; 36148 return res.wrapped_result; 36149 } 36150 // TODO is this a performance issue? maybe we should try the operation without 36151 // resorting to BigInt first. 36152 var lhs_space: Value.BigIntSpace = undefined; 36153 var rhs_space: Value.BigIntSpace = undefined; 36154 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 36155 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 36156 const limbs = try sema.arena.alloc( 36157 std.math.big.Limb, 36158 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, 36159 ); 36160 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 36161 result_bigint.add(lhs_bigint, rhs_bigint); 36162 return mod.intValue_big(scalar_ty, result_bigint.toConst()); 36163 } 36164 36165 /// Supports both floats and ints; handles undefined. 36166 fn numberAddWrapScalar( 36167 sema: *Sema, 36168 lhs: Value, 36169 rhs: Value, 36170 ty: Type, 36171 ) !Value { 36172 const mod = sema.mod; 36173 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; 36174 36175 if (ty.zigTypeTag(mod) == .ComptimeInt) { 36176 return sema.intAdd(lhs, rhs, ty, undefined); 36177 } 36178 36179 if (ty.isAnyFloat()) { 36180 return Value.floatAdd(lhs, rhs, ty, sema.arena, mod); 36181 } 36182 36183 const overflow_result = try sema.intAddWithOverflow(lhs, rhs, ty); 36184 return overflow_result.wrapped_result; 36185 } 36186 36187 /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting 36188 /// overflow_idx to the vector index the overflow was at (or 0 for a scalar). 36189 fn intSub(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *?usize) !Value { 36190 var overflow: usize = undefined; 36191 return sema.intSubInner(lhs, rhs, ty, &overflow) catch |err| switch (err) { 36192 error.Overflow => { 36193 const is_vec = ty.isVector(sema.mod); 36194 overflow_idx.* = if (is_vec) overflow else 0; 36195 const safe_ty = if (is_vec) try sema.mod.vectorType(.{ 36196 .len = ty.vectorLen(sema.mod), 36197 .child = .comptime_int_type, 36198 }) else Type.comptime_int; 36199 return sema.intSubInner(lhs, rhs, safe_ty, undefined) catch |err1| switch (err1) { 36200 error.Overflow => unreachable, 36201 else => |e| return e, 36202 }; 36203 }, 36204 else => |e| return e, 36205 }; 36206 } 36207 36208 fn intSubInner(sema: *Sema, lhs: Value, rhs: Value, ty: Type, overflow_idx: *usize) !Value { 36209 const mod = sema.mod; 36210 if (ty.zigTypeTag(mod) == .Vector) { 36211 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 36212 const scalar_ty = ty.scalarType(mod); 36213 for (result_data, 0..) |*scalar, i| { 36214 const lhs_elem = try lhs.elemValue(sema.mod, i); 36215 const rhs_elem = try rhs.elemValue(sema.mod, i); 36216 const val = sema.intSubScalar(lhs_elem, rhs_elem, scalar_ty) catch |err| switch (err) { 36217 error.Overflow => { 36218 overflow_idx.* = i; 36219 return error.Overflow; 36220 }, 36221 else => |e| return e, 36222 }; 36223 scalar.* = try val.intern(scalar_ty, mod); 36224 } 36225 return (try mod.intern(.{ .aggregate = .{ 36226 .ty = ty.toIntern(), 36227 .storage = .{ .elems = result_data }, 36228 } })).toValue(); 36229 } 36230 return sema.intSubScalar(lhs, rhs, ty); 36231 } 36232 36233 fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) !Value { 36234 const mod = sema.mod; 36235 if (scalar_ty.toIntern() != .comptime_int_type) { 36236 const res = try sema.intSubWithOverflowScalar(lhs, rhs, scalar_ty); 36237 if (res.overflow_bit.compareAllWithZero(.neq, mod)) return error.Overflow; 36238 return res.wrapped_result; 36239 } 36240 // TODO is this a performance issue? maybe we should try the operation without 36241 // resorting to BigInt first. 36242 var lhs_space: Value.BigIntSpace = undefined; 36243 var rhs_space: Value.BigIntSpace = undefined; 36244 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 36245 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 36246 const limbs = try sema.arena.alloc( 36247 std.math.big.Limb, 36248 @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, 36249 ); 36250 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 36251 result_bigint.sub(lhs_bigint, rhs_bigint); 36252 return mod.intValue_big(scalar_ty, result_bigint.toConst()); 36253 } 36254 36255 /// Supports both floats and ints; handles undefined. 36256 fn numberSubWrapScalar( 36257 sema: *Sema, 36258 lhs: Value, 36259 rhs: Value, 36260 ty: Type, 36261 ) !Value { 36262 const mod = sema.mod; 36263 if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef; 36264 36265 if (ty.zigTypeTag(mod) == .ComptimeInt) { 36266 return sema.intSub(lhs, rhs, ty, undefined); 36267 } 36268 36269 if (ty.isAnyFloat()) { 36270 return Value.floatSub(lhs, rhs, ty, sema.arena, mod); 36271 } 36272 36273 const overflow_result = try sema.intSubWithOverflow(lhs, rhs, ty); 36274 return overflow_result.wrapped_result; 36275 } 36276 36277 fn intSubWithOverflow( 36278 sema: *Sema, 36279 lhs: Value, 36280 rhs: Value, 36281 ty: Type, 36282 ) !Value.OverflowArithmeticResult { 36283 const mod = sema.mod; 36284 if (ty.zigTypeTag(mod) == .Vector) { 36285 const vec_len = ty.vectorLen(mod); 36286 const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len); 36287 const result_data = try sema.arena.alloc(InternPool.Index, vec_len); 36288 const scalar_ty = ty.scalarType(mod); 36289 for (overflowed_data, result_data, 0..) |*of, *scalar, i| { 36290 const lhs_elem = try lhs.elemValue(sema.mod, i); 36291 const rhs_elem = try rhs.elemValue(sema.mod, i); 36292 const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty); 36293 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod); 36294 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod); 36295 } 36296 return Value.OverflowArithmeticResult{ 36297 .overflow_bit = (try mod.intern(.{ .aggregate = .{ 36298 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(), 36299 .storage = .{ .elems = overflowed_data }, 36300 } })).toValue(), 36301 .wrapped_result = (try mod.intern(.{ .aggregate = .{ 36302 .ty = ty.toIntern(), 36303 .storage = .{ .elems = result_data }, 36304 } })).toValue(), 36305 }; 36306 } 36307 return sema.intSubWithOverflowScalar(lhs, rhs, ty); 36308 } 36309 36310 fn intSubWithOverflowScalar( 36311 sema: *Sema, 36312 lhs: Value, 36313 rhs: Value, 36314 ty: Type, 36315 ) !Value.OverflowArithmeticResult { 36316 const mod = sema.mod; 36317 const info = ty.intInfo(mod); 36318 36319 var lhs_space: Value.BigIntSpace = undefined; 36320 var rhs_space: Value.BigIntSpace = undefined; 36321 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 36322 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 36323 const limbs = try sema.arena.alloc( 36324 std.math.big.Limb, 36325 std.math.big.int.calcTwosCompLimbCount(info.bits), 36326 ); 36327 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 36328 const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); 36329 const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()); 36330 return Value.OverflowArithmeticResult{ 36331 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)), 36332 .wrapped_result = wrapped_result, 36333 }; 36334 } 36335 36336 fn intFromFloat( 36337 sema: *Sema, 36338 block: *Block, 36339 src: LazySrcLoc, 36340 val: Value, 36341 float_ty: Type, 36342 int_ty: Type, 36343 ) CompileError!Value { 36344 const mod = sema.mod; 36345 if (float_ty.zigTypeTag(mod) == .Vector) { 36346 const elem_ty = float_ty.scalarType(mod); 36347 const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(mod)); 36348 const scalar_ty = int_ty.scalarType(mod); 36349 for (result_data, 0..) |*scalar, i| { 36350 const elem_val = try val.elemValue(sema.mod, i); 36351 scalar.* = try (try sema.intFromFloatScalar(block, src, elem_val, elem_ty, int_ty.scalarType(mod))).intern(scalar_ty, mod); 36352 } 36353 return (try mod.intern(.{ .aggregate = .{ 36354 .ty = int_ty.toIntern(), 36355 .storage = .{ .elems = result_data }, 36356 } })).toValue(); 36357 } 36358 return sema.intFromFloatScalar(block, src, val, float_ty, int_ty); 36359 } 36360 36361 // float is expected to be finite and non-NaN 36362 fn float128IntPartToBigInt( 36363 arena: Allocator, 36364 float: f128, 36365 ) !std.math.big.int.Managed { 36366 const is_negative = std.math.signbit(float); 36367 const floored = @floor(@fabs(float)); 36368 36369 var rational = try std.math.big.Rational.init(arena); 36370 defer rational.q.deinit(); 36371 rational.setFloat(f128, floored) catch |err| switch (err) { 36372 error.NonFiniteFloat => unreachable, 36373 error.OutOfMemory => return error.OutOfMemory, 36374 }; 36375 36376 // The float is reduced in rational.setFloat, so we assert that denominator is equal to one 36377 const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; 36378 assert(rational.q.toConst().eqAbs(big_one)); 36379 36380 if (is_negative) { 36381 rational.negate(); 36382 } 36383 return rational.p; 36384 } 36385 36386 fn intFromFloatScalar( 36387 sema: *Sema, 36388 block: *Block, 36389 src: LazySrcLoc, 36390 val: Value, 36391 float_ty: Type, 36392 int_ty: Type, 36393 ) CompileError!Value { 36394 const mod = sema.mod; 36395 36396 const float = val.toFloat(f128, mod); 36397 if (std.math.isNan(float)) { 36398 return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ 36399 int_ty.fmt(sema.mod), 36400 }); 36401 } 36402 if (std.math.isInf(float)) { 36403 return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{ 36404 int_ty.fmt(sema.mod), 36405 }); 36406 } 36407 36408 var big_int = try float128IntPartToBigInt(sema.arena, float); 36409 defer big_int.deinit(); 36410 36411 const cti_result = try mod.intValue_big(Type.comptime_int, big_int.toConst()); 36412 36413 if (!(try sema.intFitsInType(cti_result, int_ty, null))) { 36414 return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{ 36415 val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod), 36416 }); 36417 } 36418 return mod.getCoerced(cti_result, int_ty); 36419 } 36420 36421 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. 36422 /// Vectors are also accepted. Vector results are reduced with AND. 36423 /// 36424 /// If provided, `vector_index` reports the first element that failed the range check. 36425 fn intFitsInType( 36426 sema: *Sema, 36427 val: Value, 36428 ty: Type, 36429 vector_index: ?*usize, 36430 ) CompileError!bool { 36431 const mod = sema.mod; 36432 if (ty.toIntern() == .comptime_int_type) return true; 36433 const info = ty.intInfo(mod); 36434 switch (val.toIntern()) { 36435 .zero_usize, .zero_u8 => return true, 36436 else => switch (mod.intern_pool.indexToKey(val.toIntern())) { 36437 .undef => return true, 36438 .variable, .extern_func, .func, .ptr => { 36439 const target = mod.getTarget(); 36440 const ptr_bits = target.ptrBitWidth(); 36441 return switch (info.signedness) { 36442 .signed => info.bits > ptr_bits, 36443 .unsigned => info.bits >= ptr_bits, 36444 }; 36445 }, 36446 .int => |int| switch (int.storage) { 36447 .u64, .i64, .big_int => { 36448 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; 36449 const big_int = int.storage.toBigInt(&buffer); 36450 return big_int.fitsInTwosComp(info.signedness, info.bits); 36451 }, 36452 .lazy_align => |lazy_ty| { 36453 const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed); 36454 // If it is u16 or bigger we know the alignment fits without resolving it. 36455 if (info.bits >= max_needed_bits) return true; 36456 const x = try sema.typeAbiAlignment(lazy_ty.toType()); 36457 if (x == 0) return true; 36458 const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed); 36459 return info.bits >= actual_needed_bits; 36460 }, 36461 .lazy_size => |lazy_ty| { 36462 const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed); 36463 // If it is u64 or bigger we know the size fits without resolving it. 36464 if (info.bits >= max_needed_bits) return true; 36465 const x = try sema.typeAbiSize(lazy_ty.toType()); 36466 if (x == 0) return true; 36467 const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed); 36468 return info.bits >= actual_needed_bits; 36469 }, 36470 }, 36471 .aggregate => |aggregate| { 36472 assert(ty.zigTypeTag(mod) == .Vector); 36473 return switch (aggregate.storage) { 36474 .bytes => |bytes| for (bytes, 0..) |byte, i| { 36475 if (byte == 0) continue; 36476 const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); 36477 if (info.bits >= actual_needed_bits) continue; 36478 if (vector_index) |vi| vi.* = i; 36479 break false; 36480 } else true, 36481 .elems, .repeated_elem => for (switch (aggregate.storage) { 36482 .bytes => unreachable, 36483 .elems => |elems| elems, 36484 .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem), 36485 }, 0..) |elem, i| { 36486 if (try sema.intFitsInType(elem.toValue(), ty.scalarType(mod), null)) continue; 36487 if (vector_index) |vi| vi.* = i; 36488 break false; 36489 } else true, 36490 }; 36491 }, 36492 else => unreachable, 36493 }, 36494 } 36495 } 36496 36497 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { 36498 const mod = sema.mod; 36499 if (!(try int_val.compareAllWithZeroAdvanced(.gte, sema))) return false; 36500 const end_val = try mod.intValue(tag_ty, end); 36501 if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false; 36502 return true; 36503 } 36504 36505 /// Asserts the type is an enum. 36506 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { 36507 const mod = sema.mod; 36508 const enum_type = mod.intern_pool.indexToKey(ty.toIntern()).enum_type; 36509 assert(enum_type.tag_mode != .nonexhaustive); 36510 // The `tagValueIndex` function call below relies on the type being the integer tag type. 36511 // `getCoerced` assumes the value will fit the new type. 36512 if (!(try sema.intFitsInType(int, enum_type.tag_ty.toType(), null))) return false; 36513 const int_coerced = try mod.getCoerced(int, enum_type.tag_ty.toType()); 36514 36515 return enum_type.tagValueIndex(&mod.intern_pool, int_coerced.toIntern()) != null; 36516 } 36517 36518 fn intAddWithOverflow( 36519 sema: *Sema, 36520 lhs: Value, 36521 rhs: Value, 36522 ty: Type, 36523 ) !Value.OverflowArithmeticResult { 36524 const mod = sema.mod; 36525 if (ty.zigTypeTag(mod) == .Vector) { 36526 const vec_len = ty.vectorLen(mod); 36527 const overflowed_data = try sema.arena.alloc(InternPool.Index, vec_len); 36528 const result_data = try sema.arena.alloc(InternPool.Index, vec_len); 36529 const scalar_ty = ty.scalarType(mod); 36530 for (overflowed_data, result_data, 0..) |*of, *scalar, i| { 36531 const lhs_elem = try lhs.elemValue(sema.mod, i); 36532 const rhs_elem = try rhs.elemValue(sema.mod, i); 36533 const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty); 36534 of.* = try of_math_result.overflow_bit.intern(Type.u1, mod); 36535 scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod); 36536 } 36537 return Value.OverflowArithmeticResult{ 36538 .overflow_bit = (try mod.intern(.{ .aggregate = .{ 36539 .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(), 36540 .storage = .{ .elems = overflowed_data }, 36541 } })).toValue(), 36542 .wrapped_result = (try mod.intern(.{ .aggregate = .{ 36543 .ty = ty.toIntern(), 36544 .storage = .{ .elems = result_data }, 36545 } })).toValue(), 36546 }; 36547 } 36548 return sema.intAddWithOverflowScalar(lhs, rhs, ty); 36549 } 36550 36551 fn intAddWithOverflowScalar( 36552 sema: *Sema, 36553 lhs: Value, 36554 rhs: Value, 36555 ty: Type, 36556 ) !Value.OverflowArithmeticResult { 36557 const mod = sema.mod; 36558 const info = ty.intInfo(mod); 36559 36560 var lhs_space: Value.BigIntSpace = undefined; 36561 var rhs_space: Value.BigIntSpace = undefined; 36562 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, mod, sema); 36563 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, mod, sema); 36564 const limbs = try sema.arena.alloc( 36565 std.math.big.Limb, 36566 std.math.big.int.calcTwosCompLimbCount(info.bits), 36567 ); 36568 var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; 36569 const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); 36570 const result = try mod.intValue_big(ty, result_bigint.toConst()); 36571 return Value.OverflowArithmeticResult{ 36572 .overflow_bit = try mod.intValue(Type.u1, @intFromBool(overflowed)), 36573 .wrapped_result = result, 36574 }; 36575 } 36576 36577 /// Asserts the values are comparable. Both operands have type `ty`. 36578 /// For vectors, returns true if the comparison is true for ALL elements. 36579 /// 36580 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)` 36581 fn compareAll( 36582 sema: *Sema, 36583 lhs: Value, 36584 op: std.math.CompareOperator, 36585 rhs: Value, 36586 ty: Type, 36587 ) CompileError!bool { 36588 const mod = sema.mod; 36589 if (ty.zigTypeTag(mod) == .Vector) { 36590 var i: usize = 0; 36591 while (i < ty.vectorLen(mod)) : (i += 1) { 36592 const lhs_elem = try lhs.elemValue(sema.mod, i); 36593 const rhs_elem = try rhs.elemValue(sema.mod, i); 36594 if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)))) { 36595 return false; 36596 } 36597 } 36598 return true; 36599 } 36600 return sema.compareScalar(lhs, op, rhs, ty); 36601 } 36602 36603 /// Asserts the values are comparable. Both operands have type `ty`. 36604 fn compareScalar( 36605 sema: *Sema, 36606 lhs: Value, 36607 op: std.math.CompareOperator, 36608 rhs: Value, 36609 ty: Type, 36610 ) CompileError!bool { 36611 const mod = sema.mod; 36612 const coerced_lhs = try mod.getCoerced(lhs, ty); 36613 const coerced_rhs = try mod.getCoerced(rhs, ty); 36614 switch (op) { 36615 .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty), 36616 .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)), 36617 else => return Value.compareHeteroAdvanced(coerced_lhs, op, coerced_rhs, mod, sema), 36618 } 36619 } 36620 36621 fn valuesEqual( 36622 sema: *Sema, 36623 lhs: Value, 36624 rhs: Value, 36625 ty: Type, 36626 ) CompileError!bool { 36627 return lhs.eql(rhs, ty, sema.mod); 36628 } 36629 36630 /// Asserts the values are comparable vectors of type `ty`. 36631 fn compareVector( 36632 sema: *Sema, 36633 lhs: Value, 36634 op: std.math.CompareOperator, 36635 rhs: Value, 36636 ty: Type, 36637 ) !Value { 36638 const mod = sema.mod; 36639 assert(ty.zigTypeTag(mod) == .Vector); 36640 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(mod)); 36641 for (result_data, 0..) |*scalar, i| { 36642 const lhs_elem = try lhs.elemValue(sema.mod, i); 36643 const rhs_elem = try rhs.elemValue(sema.mod, i); 36644 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(mod)); 36645 scalar.* = try Value.makeBool(res_bool).intern(Type.bool, mod); 36646 } 36647 return (try mod.intern(.{ .aggregate = .{ 36648 .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .bool_type })).toIntern(), 36649 .storage = .{ .elems = result_data }, 36650 } })).toValue(); 36651 } 36652 36653 /// Returns the type of a pointer to an element. 36654 /// Asserts that the type is a pointer, and that the element type is indexable. 36655 /// For *[N]T, return *T 36656 /// For [*]T, returns *T 36657 /// For []T, returns *T 36658 /// Handles const-ness and address spaces in particular. 36659 /// This code is duplicated in `analyzePtrArithmetic`. 36660 fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { 36661 const mod = sema.mod; 36662 const ptr_info = ptr_ty.ptrInfo(mod); 36663 const elem_ty = ptr_ty.elemType2(mod); 36664 const is_allowzero = ptr_info.flags.is_allowzero and (offset orelse 0) == 0; 36665 const parent_ty = ptr_ty.childType(mod); 36666 36667 const VI = InternPool.Key.PtrType.VectorIndex; 36668 36669 const vector_info: struct { 36670 host_size: u16 = 0, 36671 alignment: u32 = 0, 36672 vector_index: VI = .none, 36673 } = if (parent_ty.isVector(mod) and ptr_info.flags.size == .One) blk: { 36674 const elem_bits = elem_ty.bitSize(mod); 36675 if (elem_bits == 0) break :blk .{}; 36676 const is_packed = elem_bits < 8 or !std.math.isPowerOfTwo(elem_bits); 36677 if (!is_packed) break :blk .{}; 36678 36679 break :blk .{ 36680 .host_size = @as(u16, @intCast(parent_ty.arrayLen(mod))), 36681 .alignment = @as(u32, @intCast(parent_ty.abiAlignment(mod))), 36682 .vector_index = if (offset) |some| @as(VI, @enumFromInt(some)) else .runtime, 36683 }; 36684 } else .{}; 36685 36686 const alignment: Alignment = a: { 36687 // Calculate the new pointer alignment. 36688 if (ptr_info.flags.alignment == .none) { 36689 if (vector_info.alignment != 0) break :a Alignment.fromNonzeroByteUnits(vector_info.alignment); 36690 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. 36691 break :a .none; 36692 } 36693 // If the addend is not a comptime-known value we can still count on 36694 // it being a multiple of the type size. 36695 const elem_size = try sema.typeAbiSize(elem_ty); 36696 const addend = if (offset) |off| elem_size * off else elem_size; 36697 36698 // The resulting pointer is aligned to the lcd between the offset (an 36699 // arbitrary number) and the alignment factor (always a power of two, 36700 // non zero). 36701 const new_align = @as(Alignment, @enumFromInt(@min( 36702 @ctz(addend), 36703 @intFromEnum(ptr_info.flags.alignment), 36704 ))); 36705 assert(new_align != .none); 36706 break :a new_align; 36707 }; 36708 return mod.ptrType(.{ 36709 .child = elem_ty.toIntern(), 36710 .flags = .{ 36711 .alignment = alignment, 36712 .is_const = ptr_info.flags.is_const, 36713 .is_volatile = ptr_info.flags.is_volatile, 36714 .is_allowzero = is_allowzero, 36715 .address_space = ptr_info.flags.address_space, 36716 .vector_index = vector_info.vector_index, 36717 }, 36718 .packed_offset = .{ 36719 .host_size = vector_info.host_size, 36720 .bit_offset = 0, 36721 }, 36722 }); 36723 } 36724 36725 /// Merge lhs with rhs. 36726 /// Asserts that lhs and rhs are both error sets and are resolved. 36727 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { 36728 const mod = sema.mod; 36729 const arena = sema.arena; 36730 const lhs_names = lhs.errorSetNames(mod); 36731 const rhs_names = rhs.errorSetNames(mod); 36732 var names: Module.Fn.InferredErrorSet.NameMap = .{}; 36733 try names.ensureUnusedCapacity(arena, lhs_names.len); 36734 36735 for (lhs_names) |name| { 36736 names.putAssumeCapacityNoClobber(name, {}); 36737 } 36738 for (rhs_names) |name| { 36739 try names.put(arena, name, {}); 36740 } 36741 36742 return mod.errorSetFromUnsortedNames(names.keys()); 36743 } 36744 36745 /// Avoids crashing the compiler when asking if inferred allocations are noreturn. 36746 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool { 36747 if (ref == .unreachable_value) return true; 36748 if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) { 36749 .inferred_alloc, .inferred_alloc_comptime => return false, 36750 else => {}, 36751 }; 36752 return sema.typeOf(ref).isNoReturn(sema.mod); 36753 } 36754 36755 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type. 36756 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool { 36757 if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) { 36758 .inferred_alloc, .inferred_alloc_comptime => return false, 36759 else => {}, 36760 }; 36761 return sema.typeOf(ref).zigTypeTag(sema.mod) == tag; 36762 }